What is Janux?#

Janux is a fullstack UI framework with two first-class audiences: the humans who use your app and the AI agents that operate it. You write one component definition; Janux projects it three ways — a view for people, a typed resource agents can read, and typed tools both can invoke. They can't drift, because they're generated from the same source.

TSXsrc/components/Counter.tsx
export const Counter = component({
  name: 'counter',
  description: 'A counter agents can read and change',
  state: schema({ count: int() }),
  intents: {
    inc: intent({ description: 'Increment', input: schema({ by: int().default(1) }), run: ({ state, input }) => (state.count += input.by) }),
  },
  view: ({ state, intents }) => (
    <button onClick={intents.inc}>{state.count}</button>
  ),
});

That definition is simultaneously:

  • A view — server-rendered HTML that resumes on first interaction.
  • A resourceui://counter, plain JSON state agents can read.
  • A toolcounter.inc with a JSON Schema input and a guard, callable by a copilot, by a remote MCP client, or by a click.

Why it exists#

Two shifts happened at once. Models now write most UI code, and they need a target with fewer traps than hooks-and-effects. And apps are increasingly expected to be operable by agents, which today means bolting a second, hand-written integration onto an app that already has all the logic. Janux collapses both problems into one definition:

Problem How Janux answers it
Agents generate plausible-but-broken UI code One declarative shape per component. No hooks, no dependency arrays, no lexical capture in mutations.
Humans can't review AI-written diffs fast State, actions, guards and view sit in a single definition — the PR is the whole truth of the component.
Wiring an app to an agent is a project The manifest, tools and MCP endpoint are generated. Connecting a copilot is a URL.
Agent contracts rot They're derived from the code that renders. Drift is structurally impossible.
Agents doing dangerous things Guards (auto / confirm / forbidden) are declared per intent and enforced per request origin.

What it is not#

  • Not a chatbot bolted onto a UI. The agent surface is the app's own contract; the copilot is optional.
  • Not React with extra steps. The component model is different on purpose — see Mental model. You can mount real React components unchanged where you need the ecosystem (interop).
  • Not a hydration framework. Pages resume from JSON snapshots; static pages ship 0 KB of JS.

What's in the box#

Server rendering with islands and resumability, file-system routing with layouts and typed matchers, api() server functions that double as agent tools, a client data cache, i18n, a built-in copilot with an embedded agent harness (memory, durable workflows, guardrails, rate limiting), a hosted MCP endpoint, llms.txt and Markdown projections of every page, plus a CLI that builds to a Bun server or a fully static site.

Next: Quick start — an app running in about a minute.