Examples#
Every example in examples/ is a complete, runnable app. Scaffold any of them as your own project straight from the CLI:
bun create janux my-shop --example shop
cd my-shop && bun install && bun run dev # http://localhost:4321Or run them in place inside the monorepo: git clone https://github.com/aralroca/Janux.git && cd Janux && bun install && bun run --cwd examples/shop dev.
| Example | What it demonstrates |
|---|---|
shop |
The flagship app: catalog source, debounced persist effect, confirm-guarded checkout with human approval, copilot island, orders/[id] route and a headless agent eval (bunx janux eval). |
i18n |
Locale-prefixed routing (/en, /es, /fr), type-safe t() with plurals (label_one / label_other), language switcher, and page-scoped client translations — only the counter's keys ship to the browser. |
interop-react |
A React mixer mounted unchanged with foreign(): tracked props from island state and its onBand callback bridged to a setBand intent — the wrap-once pattern that gives a foreign component an agent surface. |
interop-data-grid |
The data-grid category of the interop matrix: @tanstack/react-table mounted unchanged and fully controlled from island state, with its updater-function callbacks mapped onto typed intents — the case that on: { prop: 'intent' } cannot express. |
interop-charts |
The charts category of the interop matrix: recharts mounted unchanged, with its onClick(data, **index**, event) payload mapped onto an intent — and an e2e test that asserts what Recharts does not server-render, so the caveat can't rot. |
interop-virtual-list |
The virtualization category of the interop matrix: @tanstack/react-virtual over 10,000 rows, server-rendered as a real first window via initialRect — and list.scrollToRow, which reaches a row no DOM-scraping agent could click because it does not exist yet. |
interop-drag-drop |
The drag-and-drop category of the interop matrix: @dnd-kit mounted unchanged with its a11y wiring server-rendered, and its unserializable drag event mapped onto board.move { id, toIndex } — the same tool an agent calls to reorder without dragging. |
interop-command-palette |
The command-palette category of the interop matrix: cmdk mounted unchanged, with an e2e assertion that the rendered command ids and palette.run's enum are the same list — so what a human can pick and what an agent can call cannot drift. |
interop-forms |
The forms category of the interop matrix, and its honest ⚠️: react-hook-form + zod keep their own copy of the form state in uncontrolled inputs, so an agent's fill has to be reconciled into them explicitly. zod validates the human path inside React, and because an agent calling the intent never touches zod, submit checks the email itself — two doors, both shut. |
interop-graph-editor |
The graph-editor category of the interop matrix: @xyflow/react driven in both directions — a node drag and a drawn edge land as moveNode / connect, the same tools the agent calls. hydrate: 'only', because React Flow measures its viewport on mount. |
interop-a11y-primitives |
The a11y-primitives category of the interop matrix: @radix-ui/react-dialog with its focus trap and scroll lock intact, portalling out of the foreign host — and a client-side navigation with the dialog open that neither throws nor leaves <body> unscrollable. |
nested-islands |
Stateful islands three levels deep with per-island render loops, conditional mount/dispose, each level agent-visible as a ui:// resource. |
with-web-agent |
The console from the home page video, operated in natural language: createCopilot({ visualize }) for the status chips, the animated ring and the backdrop veil; glowTarget so the ring waits for React Flow nodes that mount a tick later; a forbidden intent that leaves the display name reachable only through the DOM fallback; and @xyflow/react mounted unchanged with foreign(). Runs offline — the planner is scripted, no API key. |
data-cache |
useQuery with a reactive key (the filter is part of queryKey) and typed urlState: ?tag=… is deep-linkable, the Back button undoes a filter, and the agent's catalog.filter drives the same intent as a click. Also the HTTP cache: /catalog declares a public cachePolicy with a tag, /account declares nothing and stays private, no-store, and a panel reads the live cache-control / x-janux-cache headers while revalidating by tag. |
with-suspense |
Streaming SSR: two slow sources behind independent suspense views that reveal mid-stream, and a /broken route where error views catch and bubble. |
with-tailwind |
@janux/tailwind zero-config: a pricing page with dark mode and a billing toggle island, styled only with Tailwind v4 utilities — the whole setup is one dependency and a one-line CSS import. |
with-sass |
Sass with no configuration beyond the file extension: src/styles.scss with a tokens partial, nesting, a mixin and four accent classes generated by a single @each loop — compiled to the one /styles.css the shell links. |
with-css-variables |
Runtime theming: island state writes --brand, --pad and --radius onto one wrapper and the cascade rethemes everything below — no rebuild, no extra rules, and the style object is typed. |
with-offline |
Service worker, offline and PWA: a prerendered guide that opens with the network switched off, ticks its checklist from the cached island bundle, answers a page it never saw with an offline notice, and moves to the next deploy without a tab being closed. |
with-worker |
worker(): one prime-counting function run on a Web Worker and on the main thread, with a ticker outside the island that makes the frozen page impossible to miss. |
with-node-adapter |
Deploying to Node with @janux/node: bunx janux-node writes a self-contained build/ that node build/index.js serves — SSR, hydration, api() RPC and the agent manifest all intact, with no Bun on the box. |
with-forms |
One schema() as the contract for three surfaces: the form UI with per-field errors and no reload, the persisting api() endpoint, and the typed tool an agent calls with the same data a human submits. |
with-optimistic-ui |
mutation() with optimistic writes and real rollback: onMutate shows the favorite instantly, the server rejects every third save, and onError restores the snapshot with a visible notice. |
cross-island-state |
A store() cart shared by five islands with no prop drilling: the grid writes, badge and panel read, a toast reacts to the bus event, persist: 'local' survives reloads, and a bundle enters in one batch(). |
with-advanced-routing |
The full router grammar: [slug], [...path], [[...rest]], [id=integer]/[uid=uuid] matchers, nested _layout.tsx chains, (marketing) groups, the _404.tsx/_500.tsx pages — plus SPA navigation with a persist island in the shell. |
with-sqlite |
Real persistence with bun:sqlite and both server surfaces over one database: api() RPC whose delete is confirm-guarded (agents propose, a human approves) alongside classic REST handlers. |
with-uploads |
End-to-end uploads: dropzone() feeding a validating multipart handler (type + size limits), a server-rendered gallery refreshed by events, and previews with no reload. |
realtime-chat |
A custom server composing createJanuxServer with Bun's native WebSockets: optimistic message delivery, cursor-based replay after a reconnect, and live presence. |
with-images |
Both halves of Cumulative Layout Shift. The image primitive: one <Image> renders AVIF and WebP candidates that janux build wrote into dist/client/_janux/image/, priority on the LCP hero, width/height on every tag. And the font primitive: the declared family is self-hosted, subset, preloaded and given a fallback measured from the real file. CLS 0 in Chrome, output: 'static', no client runtime at all. |
blog-static |
A markdown blog exported with output: 'static' + staticParams: pages that ship zero JS, configured speculation rules, and the agent face — llms.txt, sitemap, .md projections — from the same build. |
with-content |
Typed content collections: a note's frontmatter is validated by the same schema() that types an island's state, and .mdx notes embed a real Janux island — vote is on the manifest, so an agent votes through the tool a reader clicks — plus a React component mounted unchanged with foreign(). MDX is compiled on the server, so a note of prose still prerenders to a file with no scripts in it. |
hacker-news |
The canonical clone: a streaming-suspense front page over a deterministic local fixture, [page=integer] pagination, a fully server-rendered nested comment tree, useQuery client refresh and hover prefetch. |
with-mcp-url |
The app as a bearer-protected MCP server by URL: a public landing with connection instructions, and a committed tool contract that turns CI red if the agent surface drifts. |
human-in-the-loop |
Who invokes changes what happens: the same confirm intent executes on a human click but parks as a Proposal for an agent — approvals inbox, reject path, and an audit trail labeled human / agent. |
durable-agent |
The agent harness in production shape: Postgres memory that survives restarts, Redis rate limiting shared across instances, injection guardrails with a safe refusal, a durable two-step workflow, and a schedule that triggers it — resuming the same run after the process is killed mid-flight. |
with-mcp-client |
The outbound direction: the agent connects to an external MCP server by URL, filters the remote tools (allowlist/prefix) and re-exposes them on the app's own surface — with clean degradation when the server is down. |
with-subagents |
Agent composition: a front desk delegates lookups to a budgeted research subagent (own prompt, tools intersected with the parent's — a delegate never widens the surface) and hands money conversations off to a billing agent that answers the user from then on. |
a2a-supplier |
The app as an agent other agents can hire: a /.well-known/agent-card.json derived from its api() tools, an A2A endpoint beside the MCP one, and supplier.ship under guard: 'confirm' — a remote agent's call parks for a human on this site and ships nothing until one approves. |
a2a-buyer |
The other side of the same demo: it knows only the supplier's origin, reads the endpoint and the input schemas off the card, hires it over A2A, and follows GetTask while a human at the supplier decides. |
with-local-llm |
The copilot's model runs in the browser over WebGPU (localLlm()): consent-gated download with live progress, serverLlm() fallback, and a runtime local↔cloud swap. |
agent-evals |
janux eval as a CI gate: scripted, model-free agent tasks replayed over the real webMCP surface — including a human approval step and a deliberately broken eval that proves the gate can fail. |
with-skills |
Skills: a returns desk whose multi-step procedure ships as markdown the model loads on demand — index always in context, body only when asked for — projected to MCP, with janux verify refusing a skill that names a tool the app does not have. |
with-channels |
Channels: an on-call desk answering both in the browser and over an HTTP webhook — one agent, one pipeline, the same confirm guard on both doors, and a browser-only intent the model is told by name that it does not have here. |
Each folder has a README with the details. The Playground runs smaller, self-contained snippets directly in the browser.