The Console is the reference application for ObjectUI. It renders a full-featured admin interface from JSON metadata — objects, views, dashboards, and actions — with zero custom pages required.
# From the repository rootpnpm installpnpm dev # starts the console dev server (Vite)
The console opens at http://localhost:5180 (the port is fixed in apps/console/vite.config.ts). There is no bundled mock backend — apps/console/.env.development ships VITE_SERVER_URLempty (same origin), and the Vite dev server proxies /api/* to http://localhost:3000 by default, so an ObjectStack server has to be listening there. See Running with a Real Backend to point dev at a different one.
Switch between the apps discovered from the connected server.
Dynamic Navigation
Sidebar renders from the app's navigation tree (objects, groups, URLs, pages).
Object Views
List / Grid / Kanban / Calendar — backed by @object-ui/plugin-view.
CRUD Dialogs
Create & edit records via schema-driven forms.
Expression Visibility
Show/hide navigation items using visible: "${data.role === 'admin'}".
Branding
Per-app colors, favicons, and logos via AppShell branding.
Command Palette
⌘+K opens a searchable command bar for quick navigation.
Studio Package Scope
Studio home, metadata counts, quick-create links, and diagnostics follow the selected package.
Design in Studio
Workspace admins get a top-bar entry inside a running app that opens its owning package on the Studio design surface. On an interface route — a dashboard, page, or report — it deep-links straight to that surface's design page in the Interfaces pillar (/studio/:packageId/interfaces?surface=<type>:<name>, e.g. surface=page:showcase_crm_workbench); elsewhere (objects, the app root) it opens the package's Data tab (/studio/:packageId/data). These interfaces are authored in Studio — there is no in-page edit panel.
App Creation Wizard
4-step wizard (Basic Info → Objects → Navigation → Branding) to create or edit apps.
Record Approvals Tab
A record with approval requests grows an Approvals tab on its detail page (peer of Details/Related, with a request-count badge) — current step, decision progress, resolved "waiting on" approvers, the merged decision timeline, and a submitter remind button — visible to every viewer who can read the record, not just approvers.
Selecting an object in Studio's Data pillar (/studio/:packageId/data) opens a
tab strip over that object — Records · Form · Validations · Hooks · Actions ·
API · Settings. Each of Validations, Hooks and Actions is a no-code config
panel driven by the corresponding metadata, and each supports adding new
entries — no code round-trip required:
Tab
Edits
Panel
Validations
the object's inline validations[] (spec ValidationRuleSchema)
Master-detail covering every rule type — script, cross_field, state_machine, format, json_schema, conditional. The New menu adds any type (seeded with a valid, never-firing skeleton); a rule's type can be switched in place. CEL predicates reuse the shared ConditionBuilder, fed the object's draft fields.
Hooks
the separate hook metadata type targeting this object
Master-detail whose editor is the platform SchemaFormdriven by the live hook JSONSchema from /meta/types, so its fields and enums always match the running server's contract.
Actions
the object's inline actions[] (spec ActionSchema)
Master-detail using the type-aware ActionDefaultInspector; anything not curated falls through to a "More fields" form fed the live action JSONSchema, so no spec property is un-editable.
Validations and Actions persist with the object's own Save draft; Hooks (a
distinct metadata type) save per-hook. Nothing goes live until the package is
published from the top-bar Publish flow.
The console has no configuration file. It declares no apps, objects or views of its own —
it renders whatever the server it is pointed at publishes. There are only two inputs:
1. VITE_SERVER_URL — which backend to talk to. A build-time Vite variable, and the only
setting the console itself owns. It seeds the data adapter's base URL and the runtime-config
fetch; apps/console/.env.development ships it empty, which means same origin — the
Vite dev server proxies /api/* to the backend. See Running with a Real Backend.
2. Server-pushed runtime config — everything else. Before React mounts, the console
resolves /api/v1/runtime/config from that server and applies it: product branding, feature
flags (marketplace, AI Studio, SSO, custom domain), and the cloud URL. Operators configure
these on the server, not in the SPA, which is why changing them needs no console rebuild.
Apps, objects and views themselves are metadata fetched over HTTP — discovered at connect
time and loaded on demand. To change what the console shows, change the metadata on the
server: author it in the ObjectStack server project (objectstack.config.ts lives there,
not here) or edit and publish it from Studio. See
ObjectOS Integration for the server-side configuration
shape.
VITE_SERVER_URL is the setting that decides which backend the console talks to — the data adapter, auth, i18n and action endpoints all hang off it.
In dev, leave VITE_SERVER_URL empty and point the dev proxy at your server instead.
Only /api/* is proxied, to DEV_PROXY_TARGET when set and http://localhost:3000
otherwise:
DEV_PROXY_TARGET=https://demo.objectstack.ai pnpm dev
This keeps the page and the API on one origin. Setting VITE_SERVER_URL to an absolute origin still works, but it opts dev out of same-origin and into CORS — an absolute VITE_SERVER_URL is the right setting for a built console deployed apart from its backend.
The console will use the ObjectStack client to discover metadata and perform CRUD operations against the server.
Most of what you see in the console does not live in apps/console. The shell and layout, sidebar, header, command palette, object list and record detail views all ship from packages/app-shell (@object-ui/app-shell), so any host application can mount the same experience; the heavier view surfaces (grid, kanban, calendar, charts, designer) come from the @object-ui/plugin-* packages.
apps/console is the assembly layer on top: it owns the route tree, registers the plugin set, wires the backend connection, and adds the surfaces specific to this app (auth pages, the docs portal, system and settings pages). So when you want to change something you see in the console, look in packages/app-shell first.