🚧 Glyx is pre-release software. APIs may change before v1.0. Get started →
Documentation
Guides
JS Plugins

JS Plugins

JS plugins let you move backend-style logic out of React components and into dedicated JavaScript modules. Each exported async function becomes callable from app code as backend.<name>.<fn>().

Use JS plugins when you want a clean backend boundary without introducing Rust code.

JS plugins run inside the same Glyx JavaScript runtime as your app. They are great for orchestration and business logic, but they are not a security boundary.

JS plugins work on both engines — V8 (default) and QuickJS (engine: "quickjs" in glyx.config) — including hot-reload during glyx dev. Editing a plugin file re-registers its exports live on either engine; no window restart needed.


When to use JS plugins

Use a JS plugin when you want to:

  • Keep SQL, file workflows, or sync logic out of UI components
  • Expose app-specific service functions through backend.*
  • Reuse existing Glyx JS APIs like db, fs, dialog, or storage
  • Iterate quickly without adding a Rust build step

If you need native OS APIs, C/C++ bindings, or performance-critical code, see Native Extensions.


Scaffold a plugin

glyx generate plugin notes

This creates a plugin file such as:

src/plugins/notes.plugin.js

The CLI also prints the plugins config snippet you need to add to glyx.config.ts or glyx.config.json.


Register the plugin

glyx.config.ts
import { defineConfig } from '@glyx-dev/config'
 
export default defineConfig({
  capabilities: { db: true },
  plugins: [
    { entry: 'src/plugins/notes.plugin.js', name: 'notes' }
  ]
})
  • entry: path to the plugin entry file
  • name: namespace used from the app, such as backend.notes.getAll()

See the full config reference at glyx.config.ts.


Write the plugin

src/plugins/notes.plugin.js
import { db } from '@glyx-dev/react'
 
await db.open('app.db')
 
export async function getAll() {
  return db.query('SELECT * FROM notes ORDER BY updated_at DESC')
}
 
export async function create(args) {
  const { lastInsertId } = await db.run(
    'INSERT INTO notes (title) VALUES (?)',
    [args.title]
  )
 
  return { id: lastInsertId }
}

Plugins usually:

  • import built-in framework APIs from @glyx-dev/react
  • export async functions
  • return plain JSON-serializable values

Call from app code

src/NotesScreen.jsx
import { backend } from '@glyx-dev/react'
 
const notes = await backend.notes.getAll()
await backend.notes.create({ title: 'New note' })

At runtime:

  • the plugin entry is bundled at startup
  • each exported function is registered under the configured namespace
  • backend.notes.getAll() dispatches to the plugin's getAll() export

What plugins can access

JS plugins can call the same JavaScript-side framework APIs your app uses, including things like:

  • db
  • fs
  • dialog
  • storage
  • clipboard

Make sure the required capabilities are enabled in glyx.config.ts.


Plugin capabilities

Each plugin can declare the capabilities it requires. Glyx validates these at load time — the plugin is rejected if the app has not declared the same capability:

glyx.config.ts
plugins: [
  {
    entry:        'src/plugins/notes.plugin.js',
    name:         'notes',
    capabilities: ['db', 'fs'],   // must also appear in top-level capabilities
  }
]
⚠️

This isn't a hard startup failure — a misconfigured plugin is silently excluded from registration, with an error logged (not printed to the console the app user sees). The app keeps running; backend.notes.getAll simply won't exist, and calling it throws a plain "not a function" error at the call site rather than a clear message pointing at the missing capability. Watch the log output after adding a plugin capability to confirm it actually registered.

An unknown capability name, or one the plugin declares but the app hasn't enabled in capabilities, is never silently granted — the plugin is dropped instead, so this is a fail-closed check, just not a loud one. This still lets you audit which plugins require which permissions at a glance.


Hot reload in dev mode

During glyx dev, the CLI watches each plugin's entry file. When you save the file, the CLI:

  1. Re-bundles the plugin with Bun
  2. Re-evaluates the plugin IIFE in the running JS runtime (V8 or QuickJS)
  3. Replaces the registered backend.<name>.* commands in place

No restart needed — changes to a plugin are live in under a second.


JS Plugins vs Native Extensions

Choose JS plugins for:

  • app-specific workflows
  • SQL helpers
  • file orchestration
  • business logic that benefits from fast iteration

Choose Native Extensions for:

  • native OS integration
  • heavy file processing
  • C/C++ library bindings
  • performance-critical work

If the feature feels like part of your app, start with a JS plugin. If it feels like part of the runtime, move it to a Native Extension.