Skip to content

Vite Plugin

@gjsify/vite-plugin-gjsify brings GJSify’s build transforms to Vite. Spread one preset into vite.config.ts and your dev server resolves modules the same way gjsify build --app browser does, so what you see in dev is what you ship.

You do not need the gjsify CLI for this. The plugin is an ordinary npm package whose only peer dependency is vite. Install it, add it to your config, and run plain vite / vite build.

Install
Terminal
npm install -D @gjsify/vite-plugin-gjsify vite
Terminal
yarn add -D @gjsify/vite-plugin-gjsify vite
Terminal
pnpm add -D @gjsify/vite-plugin-gjsify vite
Terminal
bun add --dev @gjsify/vite-plugin-gjsify vite
Terminal
gjsify install @gjsify/vite-plugin-gjsify vite
vite.config.ts
import { defineConfig } from 'vite';
import { gjsifyBrowser } from '@gjsify/vite-plugin-gjsify';
export default defineConfig({
plugins: [...gjsifyBrowser()],
});

gjsifyBrowser() returns a plugin array, so spread it. Don’t nest it.

The preset is four pieces:

PieceWhat it does
gjsImportsEmptyPlugin()Resolves @girs/* and gi://… to an empty module. They arrive transitively (through @gjsify/unit, for instance) and no browser can resolve them.
blueprintPlugin()Compiles .blp to an XML string, so import T from './window.blp' works in dev too.
deepkitPlugin()Deepkit runtime types. Off unless you set reflection: true.
A Vite config() hookBrowser polyfill aliases (process → @gjsify/empty, assert → @gjsify/assert), resolve.conditions: ['import', 'browser'], resolve.mainFields: ['browser', 'module', 'main'], define for global and window → globalThis, build.target: 'esnext', and optimizeDeps.exclude for GJS-shaped deps that Vite’s esbuild pre-bundler cannot crawl.
OptionDefaultWhat it does
reflectionfalseTurn on @deepkit/type runtime reflection. The type compiler stays uninstalled until you ask for it.
aliases{}Extra resolve.alias entries, merged over the defaults. Yours win.
optimizeDepsExclude[]More packages for optimizeDeps.exclude. @gjsify/unit is always excluded.
cssAsStringfalseSee below.

Share one CSS pattern with the native target

Section titled “Share one CSS pattern with the native target”

By default the preset leaves CSS to Vite: HMR, <link> extraction, PostCSS. That is what a browser-only app wants, so leave it alone unless you also ship a GTK build.

Turn on cssAsString: true when the same app builds for both. A GTK app has no stylesheet link, so gjsify build --app gjs and gjsify build --app browser both hand you the CSS as a string. With the flag on, Vite dev agrees:

TypeScript
import css from './app.css'; // the CSS string, on both targets
// GJS: Gtk.CssProvider.load_from_string(css)
// Browser: document.head.append(Object.assign(document.createElement('style'), { textContent: css }))

You author one real .css file and apply it per target. The trade is Vite’s CSS HMR and <link> extraction.

For a NativeScript app, use the one-file composer @gjsify/nativescript-vite. It adds the GJSify transforms and repairs several @nativescript/vite incompatibilities with Vite 8 and Rolldown:

vite.config.ts
import { defineNativescriptConfig } from '@gjsify/nativescript-vite';
export default defineNativescriptConfig();

If you need to compose the plugins yourself, gjsifyNativescript() is the lower-level form:

TypeScript
import { defineConfig } from 'vite';
import nativescript from '@nativescript/vite';
import { gjsifyNativescript } from '@gjsify/vite-plugin-gjsify';
export default defineConfig({
plugins: [nativescript(), ...gjsifyNativescript()],
});

It takes the same reflection, aliases and optimizeDepsExclude options as gjsifyBrowser(), resolves *.android / *.ios / *.native source variants, and registers the barrel modules your XML references through an xmlns namespace.

The plugin covers development. Reach for gjsify when you need:

  • gjsify build --app gjs, the native GJS/GTK bundle. Vite targets browsers; there is no equivalent.
  • gjsify build --app browser, the production browser bundle this preset mirrors. Plain vite build also works; the preset is what keeps the two equivalent.
  • Everything that is not a build: gjsify install, tsc, lint, format, storybook, publish.

A browser-only app can live entirely on Vite. A dual-target app uses both.