JsonUI
GuidesWeb framework adaptersSince jsonui-cli 1.6.7, every web-framework-specific string rjui emits — the RSC directive, the Link component wiring, the router import, hook and type — is resolved through an adapter selected by `web_framework` in rjui.config.json. The default is the built-in Next.js adapter, whose output is the historical emit byte for byte. Declaring a custom adapter object retargets the same Layout JSON to any React-family framework — Remix, TanStack Start, and friends — without touching the tool.6 min read
1. What the seam coversEmitters are framework-neutral: whenever generated code needs a framework string, it asks the adapter. Five surfaces flow through it — the `"use client"` directive at the top of client components, the Link import line, the attribute name Link takes for its destination (`href` vs `to`), the router hook (import + the statement that reads it), and the router type (import + type name, with a JSDoc spelling for JavaScript projects). Templates copied verbatim into your tree (NetworkImage, useColorMode) pass through the same directive rewrite, so a framework with no RSC directive gets clean files with no leftover blank line.
2. The two forms of web_framework`web_framework` accepts a built-in adapter name as a string, or an inline object declaring a custom adapter. Omitting the key entirely selects `"next"`. An unknown name string fails the build with the list of built-ins — it never falls back silently. Switching adapters is a regenerate away in either direction: retargeting back to `"next"` restores the historical output byte for byte, directive included. Four ways to get it wrong, each with its own message — measured by calling the resolver directly with each shape: an unknown name string (`Unknown web_framework 'vite' in rjui.config.json (built-in: next; or declare a custom adapter object)`), a custom object with a mistyped key (`Unknown web_framework key(s) link_import_lin (allowed: …)`), a custom object whose value is not a string (`web_framework key(s) link_href_attribute must be strings`), and a value that is neither string nor object (`web_framework must be a built-in name string or a custom adapter object (got Array)`). Omitting the key resolves to the built-in Next adapter — the same call returning normally, not a separate path. Since jsonui-cli 1.7.29 the web scaffold reads this declaration too, instead of writing Next's router import directly. One caveat about how much that is worth on any given day: no project in the toolchain's own corpus declares this key, so every real build resolves to Next and the non-default branches are exercised only by planted declarations. A green corpus is not evidence that the adapter path works — it is evidence that nobody took it.
rjui.config.json
// built-in adapter by name (or omit the key entirely — same thing)
"web_framework": "next"
 
// custom adapter, declared inline — no tool changes, no Ruby
"web_framework": {
"name": "remix",
"link_import_line": "import { Link } from '@remix-run/react';",
"link_href_attribute": "to"
}
3. The eight adapter keys and their defaultsEvery key is optional and every value is a string. The defaults are deliberately neutral — no directive, no imports, a plain `<Link href>`, an untyped router — so a minimal adapter declares only what its framework actually needs. `name` may also be declared (informational only; defaults to `custom`).
adapter keys
key | default | controls
-----------------------+------------------+------------------------------------------
use_client_directive | "" (none) | the directive atop client components
link_import_line | "" (no import) | how the Link component is imported
link_href_attribute | "href" | the Link attribute holding the target URL
router_hook_import | "" (no import) | how the router hook is imported
router_hook_statement | "" (no line) | the statement that reads the router
router_type_import | "" (no import) | the import backing the router type
router_type | "any" | the router's TypeScript type
router_type_jsdoc | -> router_type | the JSDoc spelling for JS projects
4. Declaration semantics and errorsThe declaration rules are strict where a typo could silently corrupt output, and lenient where a framework legitimately has nothing to say:• An empty string suppresses the whole line — an adapter with no directive or no router import leaves no blank-line residue in the emit.• `router_type_jsdoc` left undeclared falls back to `router_type` — declare it only when the JSDoc spelling differs from the TypeScript one.• An unknown key fails the build immediately, listing the allowed keys — a misspelled `link_import_line` can never silently become the default.• A non-string value (a boolean, a nested object) also fails the build, naming the offending key.
5. Worked examples: Remix and TanStack StartThe Remix adapter below is the reference example the seam ships with — it exercises every key, including an empty directive and a JSDoc spelling. The TanStack Start one shows the minimal style: declare the Link and router wiring, let everything else fall back (no directive, router typed `any`), and tighten `router_type` / `router_type_import` later once you settle on the exact type.
rjui.config.json — Remix
"web_framework": {
"name": "remix",
"use_client_directive": "",
"link_import_line": "import { Link } from '@remix-run/react';",
"link_href_attribute": "to",
"router_hook_import": "import { useNavigate } from '@remix-run/react';",
"router_hook_statement": "const router = useNavigate();",
"router_type_import": "import { NavigateFunction } from 'react-router-dom';",
"router_type": "NavigateFunction",
"router_type_jsdoc": "import('react-router-dom').NavigateFunction"
}
rjui.config.json — TanStack Start (minimal)
"web_framework": {
"name": "tanstack-start",
"link_import_line": "import { Link } from '@tanstack/react-router';",
"link_href_attribute": "to",
"router_hook_import": "import { useNavigate } from '@tanstack/react-router';",
"router_hook_statement": "const router = useNavigate();"
}
6. Template files follow the directiveNetworkImage and useColorMode are authored in the Next dialect and copied into your tree at build time. The copy passes through the adapter's directive rewrite: with the Next adapter the files are unchanged; with an empty `use_client_directive` the directive line and its trailing blank are dropped; with a different non-empty directive it is replaced in place. You never edit the templates to retarget them.
Keep going
ReactJsonUI (web platform)The web target the adapter retargets: rjui CLI, generated TSX, Tailwind./platforms/react
Navigation between screensThe router contract the adapter's router keys plug into./guides/navigation
jui.config.json referenceWhere web_framework lives among the rest of the project configuration./reference/jui-config