@instio/core
v0.1.2
Published
Core DSL for building declarative UI apps in JavaScript — the instio() API, event model, and component builder.
Downloads
314
Readme
@instio/core
The DSL engine for Instio — define UIs and event logic in pure JavaScript.
@instio/core provides the instio() function, the event model, and a renderer-agnostic component builder. It has no DOM dependency — renderers like @instio/svelte consume the compiled output and handle display.
Installation
npm install @instio/coreIn the Instio monorepo, link via workspaces:
pnpm install # from repo rootQuick Start
import { instio } from "@instio/core";
function add(a, b) {
return Number(a) + Number(b);
}
const app = instio((ui) => {
const a = ui.number({ label: "A", value: 0 });
const b = ui.number({ label: "B", value: 0 });
const out = ui.number({ label: "Result", readonly: true });
ui.button({ label: "Add", variant: "primary" }).click(add, [a, b], [out]);
});
export default app;Pass the default export to a renderer (e.g. @instio/svelte) to mount it in the browser.
Architecture
instio(fn)
│
├── Builder accumulates nodes + events
├── createUI() exposes ui.textbox(), ui.row(), …
├── ComponentHandle .click() / .stream() fluent API
└── State optional shared reactive state
│
▼
{ tree, events, handlers, state }| Field | Description |
|-------|-------------|
| tree | Root node with nested children (layout blocks + components) |
| events | Event descriptors (source id, type, input/output node ids) |
| handlers | Live { [name]: Function } map — bundled into the browser by the renderer |
| state | Root State instance passed to your builder function |
API
instio(builder)
| Parameter | Type | Description |
|-----------|------|-------------|
| builder | (ui, state) => void | Declares the UI tree and event bindings |
Returns: { tree, events, handlers, state }
UI Components
All factory methods live on the ui object. Each leaf component returns a ComponentHandle for event binding. Layout blocks (row, column) take a callback and optional props.
Currently registered components
These are bootstrapped in src/bootstrapComponents.js:
Inputs
ui.textbox({ label, placeholder, value, lines, max_lines, readonly })
ui.number({ label, value, minimum, maximum, step, precision, readonly })
ui.slider({ label, minimum, maximum, step, value })
ui.dropdown({ label, choices, value, multiselect, allow_custom_value })
ui.checkbox({ label, value })
ui.radio({ label, choices, value })
ui.image({ label, type, sources, height, width })
ui.audio({ label, value })Outputs & Interactive
ui.markdown({ value, sanitize_html })
ui.chatbot({ label, value, height, show_copy_button })
ui.button({ label, variant, size, icon }) // variant: "primary" | "secondary" | "stop"
ui.code({ label, value, language })
ui.html({ value })
ui.clear_button({ … }) // alias → button
ui.submit_button({ … }) // alias → buttonLayout & Chrome
ui.row(() => { /* children */ }, { equal_height, variant, gap })
ui.column(() => { /* children */ }, { scale, min_width, variant })
ui.group(() => { /* children */ }, { variant })
ui.accordion(() => { /* children */ }, { label, open })
ui.tabs(() => { /* children */ }, { selected })
ui.tab(() => { /* children */ }, { label, id })Branding configuration (passed to instio options):
import { instio } from "@instio/core";
const app = instio({ branding: { name: "My App", logo: { key: "logo" } } }, (ui) => {
ui.markdown({ value: "Welcome to my app!" });
});Adding a new component
- Create
src/components/my-component.jswith a*Metaexport. - Register it on
COMPONENT_MAPin that file. - Import the file in
src/bootstrapComponents.js. - Implement the matching Svelte (or other) renderer component.
Events
Every ComponentHandle exposes:
handle.click(fn, [inputs], [outputs]);
handle.change(fn, [inputs], [outputs]);
handle.submit(fn, [inputs], [outputs]);
handle.stream(asyncGenerator).from(...inputs).to(...outputs);Handler signatures
// Single output — return value is written to outputs[0]
function add(a, b) {
return Number(a) + Number(b);
}
// Multiple outputs — return an array
function split(text) {
return [text.toUpperCase(), text.length];
}
// No inputs — omit the inputs array
function reset() {
return "0";
}
btn.click(reset, [], [display]);
// Streaming — async generator, each yield updates outputs
async function* generate(prompt) {
for await (const token of streamFromAPI(prompt)) {
yield token;
}
}Input / output wiring
- Inputs — component handles (or arrays of handles) whose current values are passed to the handler.
- Outputs — component handles whose stores receive the return value(s).
const prompt = ui.textbox({ placeholder: "Say hi" });
const output = ui.markdown();
ui.button({ label: "Run" })
.click((text) => `**You typed:** ${text}`, [prompt], [output]);State
The second argument to instio() is a lightweight reactive State container:
const app = instio((ui, state) => {
state.set({ count: 0 });
const counter = ui.number({ label: "Count", value: 0 });
counter.bind(state);
ui.button({ label: "Increment" }).click(() => {
state.set({ count: state.get().count + 1 });
});
});| Method | Description |
|--------|-------------|
| state.get() | Snapshot of current state |
| state.set(partial) | Merge partial update and notify subscribers |
| state.subscribe(fn) | Subscribe to changes; returns unsubscribe |
For complex app logic (calculator memory, multi-step wizards), module-level closures in app.js are also a practical pattern — handlers are real functions, not serialised strings.
Examples
| File | Description |
|------|-------------|
| examples/basic.js | Two number inputs, add button, readonly result |
| examples/chat.js | Streaming chat with async generator |
| ../instio/templates/src/app.js | Calculator with row/column grid layout |
Point @instio/svelte at any of these via INSTIO_APP:
INSTIO_APP=packages/core/examples/chat.js npm run dev # from packages/svelteAdvanced Exports
For renderer authors and tooling:
| Export | Description |
|--------|-------------|
| Builder | Builds and traverses the component node tree |
| ComponentHandle | Fluent event API wrapper |
| Event | Click / change / submit event descriptor |
| StreamEvent | Async-generator streaming event descriptor |
| Node | Base UI node |
| Block | Layout block node |
| Slot | Slot node |
| State | Reactive state container |
| createUI | Low-level DSL factory used by instio() |
| *Meta | Renderer-agnostic prop schemas (ButtonMeta, TextboxMeta, …) |
Development
# From monorepo root
pnpm install
pnpm --filter @instio/core build # ESM + CJS via tsdown
pnpm --filter @instio/core dev # watch modeLicense
MIT © Instio Contributors
