Universal Object UI Application Runner - A standalone development server and runtime for testing Object UI schemas.
- Schema Development - Test and debug Object UI schemas in isolation
- No-Restart Edits - Under
src/app-data/the dev server picks metadata changes up without a restart, because that JSON is part of Vite's module graph; behind?api=it cannot, because Vite never sees your backend (Metadata Loading) - Plugin Support - Pre-configured with the Kanban and Charts plugins
- Development Ready - Built-in Vite development server
- Production Build - Optimized build for deployment
The runner is an application, not a library: package.json declares no main,
module, exports or types, so there is nothing to import from
@object-ui/runner. You run it from a checkout of this repository:
git clone https://github.com/objectstack-ai/objectui.git
cd objectui
pnpm install
# Start the dev server on http://localhost:5173
pnpm --filter @object-ui/runner devInside packages/runner, the same scripts are available directly:
# Start development server
pnpm dev
# Build for production
pnpm build
# Preview production build
pnpm previewThe runner comes with these two plugins pre-configured, registered by the imports in
src/App.tsx:
- @object-ui/plugin-kanban - Kanban board components
- @object-ui/plugin-charts - Chart visualization components
Adding any other plugin means editing the runner's own sources and rebuilding — a
dependency in package.json, a registration import in src/App.tsx, and the
vite.config.ts alias and src/index.css @source entries the two above have. There
is no runtime plugin installation.
The runner picks its metadata loader when it mounts, from the api query parameter of
the page URL (src/App.tsx). This is its only API base URL setting — it reads no
environment variables and no config file:
| URL | Loader | Where metadata comes from |
|---|---|---|
http://localhost:5173/ |
LocalBundleLoader |
JSON bundled from src/app-data/ at build time |
http://localhost:5173/?api=/api |
NetworkLoader |
fetched from the base URL you passed |
With ?api=<base>, the value is used verbatim as a base URL and fixed paths are
appended to it, so a backend only has to serve two kinds of JSON document:
GET <base>/app.json # the app document, loaded once at startup
GET <base>/pages/index.json # route "/"
GET <base>/pages/customers.json # route "/customers"
GET <base>/pages/crm/accounts.json # route "/crm/accounts"
A relative base (?api=/api) keeps the requests same-origin; an absolute one needs
CORS on the backend. fetch is called with no options, so no credentials or custom
headers are sent, and any non-2xx status or network error becomes null — which the
runner renders as a 404 rather than surfacing the status.
Without the parameter, LocalBundleLoader resolves src/app-data/app.json and
src/app-data/pages/**/*.json through Vite's import.meta.glob. That directory is
git-ignored and absent from a fresh checkout, so every load returns null until you
copy or symlink your own metadata directory into it.
In-app navigation carries the query string across, so ?api=… stays in the address
bar and the URL you copy or reload reaches the same backend.
Full details — route resolution order and error handling — are in the Metadata Loading section of the docs.
There is no runner config file and no runner environment variables. The two surfaces that do configure it are:
vite.config.ts— build options and the workspace alias table that lets the runner boot straight from the monorepo sources. The dev server takes Vite's own defaults (port 5173); change them with Vite's flags, e.g.pnpm dev --port 3000.- The
apiquery parameter — the metadata base URL, described under Metadata Loading above.
- Author the metadata as JSON — both loaders resolve fixed
.jsonpaths, and JSON is the only shape either of them can load. Pick one of the two routes described under Metadata Loading:- Bundled — create
packages/runner/src/app-data/and putapp.jsonplus onepages/<route>.jsonper route in it (route/ispages/index.json). That directory is git-ignored, absent from a fresh checkout, and no script in this repo creates it, so making it is a step you do by hand — until it exists, every load returns nothing and the page renders as a 404. - Served — run a backend that answers
<base>/app.jsonand<base>/pages/<route>.json, then open the runner with?api=<base>.
- Bundled — create
- Start the runner with
pnpm dev - Edit the metadata. Under
src/app-data/the dev server picks the change up without a restart, because that JSON is part of Vite's module graph; behind?api=it cannot, because Vite never sees your backend. Reload the page if the view still shows the previous document. - Test your UI in the browser
- Build for production with
pnpm build
{
"type": "page",
"title": "Dashboard",
"body": {
"type": "grid",
"columns": 3,
"gap": 4,
"children": [
{
"type": "card",
"title": "Total Users",
"body": {
"type": "statistic",
"value": 1234,
"trend": "up"
}
},
{
"type": "card",
"title": "Revenue",
"body": {
"type": "statistic",
"value": "$56,789",
"trend": "up"
}
},
{
"type": "card",
"title": "Orders",
"body": {
"type": "statistic",
"value": 432,
"trend": "down"
}
}
]
}
}For detailed documentation, visit the Object UI Documentation.
MIT — see LICENSE.