Skip to main content

React + Vite plugin development tutorial

For real plugins with a UI, use project-based development with React 19 + Vite 7 + TypeScript + Tailwind, together with the launcher’s built-in component library @qomicex/plugin-ui. Compared with hand-written plain HTML, you get component reuse, type checking, HMR, and on-demand packaging. Generate the project with the qomicex create scaffold (Scaffold) (it configures everything for you). This tutorial explains what the scaffold produces and what each config does, so you can adjust as needed.

1. Why Vite

  • Type safety: TS strict mode; tsc --noEmit catches errors before the build
  • Component reuse: Use the same @qomicex/plugin-ui components as the launcher (Card / Button / Dialog / Select, and more); styles follow the theme automatically
  • Clean output: Only the components you use are bundled, and CSS is generated on demand (Tree-shaking + Tailwind JIT)
The generated project structure (Vite + React 19 + TS + Tailwind + @qomicex/plugin-ui, with the id already substituted in manifest.json/package.json):
The scaffold template comes from the built-in template of qomicex create (packages/qomicex-cli/templates/). Add or remove from it when it does not fit your needs.

Manual setup without the CLI (alternative)

@qomicex/plugin-ui is a workspace package inside the launcher repository, so referencing it with file:../../packages/plugin-ui is enough. You do not need to publish it to npm.

package.json

vite.config.ts: the key is a relative base

tailwind.config.js: reference the preset

Tailwind’s content only scans ./src by default. The style classes of @qomicex/plugin-ui components (bg-card, bg-muted, text-foreground) live in the component source, not under your src. If you do not add ../../packages/plugin-ui/src/**/*.{ts,tsx} to content, the built CSS is missing those classes and the plugin page has no styling (a built CSS file of only a few KB is the typical symptom).

postcss.config.js

tsconfig.json

src/index.css

3. Typed wrapper for the plugin API

Plugin scripts talk to the launcher through window.__PLUGIN_API__. Wrap it in a typed module, and handle the case where there is no API bridge when you run npm run dev standalone (opening the page directly in a browser): src/api.ts:

4. Write the plugin page

src/main.tsx:
The repository TS conventionrequires file extensions (./App.tsx, ./api.ts). Vite’s path resolution has a bug and fails when the extension is omitted. allowImportingTsExtensions is already enabled in tsconfig. Using the component library in src/App.tsx:
@qomicex/plugin-ui exports: Button / Badge / Card(Header/Title/Description/Content/Footer) / Checkbox / Combobox / Dialog / Input / Label / Select(+SelectOption/SelectDivider) / Separator / Table / Tabs / Textarea / Tooltip / useMessageBox(+MessageBoxProvider) / Icon / Glyph / ICON_NAMES / hasIcon / cn. Icons also have per-icon subpaths at @qomicex/plugin-ui/icons/<name> (see Icons). See plugin-ui component library for full usage.

5. Overlay window and theme files

overlay.html and theme.css are not part of the Vite build output, so you must copy them into dist/ manually when packaging:
  • overlay.html → dist/overlay.html (where manifest contributes.overlay.file points)
  • theme.css → dist/theme.css (where manifest entry.theme points)
Overlay window bridge timing (important): the launcher appends the overlay window’s __PLUGIN_API__ bridge script at the end of the page <body>. So do not capture window.__PLUGIN_API__ synchronously while the page loads (the bridge is not injected yet). Read it lazily inside event callbacks:

6. Package the .qplugin

qomicex pack handles everything automatically: it builds the frontend, copies the root overlay.html / theme.css into dist/, and assembles the zip (manifest.json at the root plus dist/**). If entry.theme or contributes.overlay.file reference files under dist/ while the source sits in the root directory, they are copied in automatically.

Manual packaging without the CLI (alternative)

Packaging script (scripts/build.sh)

Package structure after packaging

The Backend (Backend)resolves files as plugins/{id}/{path}, so entry.frontend: "dist/index.html" fetches plugins/{id}/dist/index.html. The dist/ subdirectory must stay inside the zip: if you hoist the files from dist to the zip root, then after installation dist/index.html, dist/theme.css, and dist/overlay.html all return 404 (blank page, no styles, overlay window will not open).

7. Install and test

  • Run qomicex pack (or manually bash scripts/build.sh 0.1.0; on Windows: pwsh ./scripts/build.ps1 0.1.0)
  • Launcher → Plugins → upload release/my-plugin-0.1.0.qplugin
  • Enable the plugin → restart the launcher → open the plugin page from the sidebar
After uploading and installing, changes to the code require you to repackage and upload again (the backend reads files from disk in real time, with no cache). During development, using qomicex dev with the debug harness is preferable: hot reload in the browser, no Tauri or backend needed (see Debugging and hot reload).

FAQ

Next steps