Plain HTML and Express
Brio can enhance a static page or any server-rendered response. Start with the working example and the shared lifecycle contract.
Files, not a framework plugin
Section titled “Files, not a framework plugin”Create a public folder containing:
public/ index.html counter.js widgets.js vendor/ brio.min.jsCopy widgets.js into that folder. Copy
dist/brio.min.js from your Brio installation into public/vendor/.
Save this as index.html:
<!doctype html><html lang="en"><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>A little Brio</title> <script defer src="/vendor/brio.min.js"></script> <script type="module" src="/counter.js"></script></head><body> <h1>A server page with a local interaction</h1> <section data-brio-counter data-start="3" aria-label="Counter"> <p>Count: <output b-text="$data.count" aria-live="polite">3</output></p> <button type="button" b-on-click="$data.count++">Add one</button> </section></body></html>Save this as counter.js:
import { createWidgetManager } from './widgets.js';
const widgets = createWidgetManager(brio.createApp);widgets.mountWithin();Serve public over HTTP using your existing server, not file://.
Keep the core script before the module and do not add async. The module
runs after parsing; the target HTML exists before it mounts.
Express
Section titled “Express”In a new example folder, initialize an npm project and install Express:
npm init -ynpm install expressPlace the files above in public. Save this as server.mjs, alongside it:
import express from 'express';import { fileURLToPath } from 'node:url';
const app = express();app.use(express.static(fileURLToPath(new URL('./public/', import.meta.url))));app.listen(3000, () => console.log('Open http://localhost:3000'));Run node server.mjs. Express serves the page and assets; the browser owns
only the local counter. No server mutation endpoint is being demonstrated.
For EJS, Pug, or another view engine, use its escaped output syntax for server
values. Validate the counter’s starting value as a safe integer, and render it
both in data-start and as the output’s fallback text.
Existing npm entrypoint
Section titled “Existing npm entrypoint”If your application already has a bundler, replace the browser-global setup with:
import { createApp } from '@jnreis/briojs';import { createWidgetManager } from './widgets.js';
const widgets = createWidgetManager(createApp);widgets.mountWithin();Normal full-page navigation needs no custom routing. If your own code replaces
HTML, call disposeWithin(oldRegion) before replacement and
mountWithin(newRegion) afterward. Keep server forms, validation, and
authorization on the server.