Solid

by vercel-labsfc2a696a50a3No license18K starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated yesterday

SolidJS renderer for json-render. Use when building @json-render/solid catalogs/registries, wiring Renderer providers, implementing bindings/actions, or troubleshooting Solid-specific reactivity patterns.

FeaturedInstructions onlySoftware Development
AI-generated overview

Guides building SolidJS renderers for json-render: catalogs, registries, providers, bindings, actions and reactivity.

What it does
This skill documents how to use the @json-render/solid package to render json-render specs into Solid component trees with fine-grained reactivity. It covers creating catalogs with defineCatalog and Zod schemas, defining components and registries with defineRegistry, wiring providers such as JSONUIProvider, and using hooks for state, visibility, validation and actions. It also explains spec structure, dynamic props and bindings, built-in actions, streaming UI, and Solid-specific reactivity rules.
When to use it
Use it when building @json-render/solid catalogs or registries, wiring Renderer providers, implementing bindings or actions, or troubleshooting Solid-specific reactivity patterns. It suits developers integrating json-render specs into a SolidJS application.
Requirements
Requires the @json-render/solid and @json-render/core packages plus zod, and a SolidJS project with JSX/TSX support. Instructions only; no scripts are shipped.

@json-render/solid

@json-render/solid renders json-render specs into Solid component trees with fine-grained reactivity.

Quick Start

tsx
import { Renderer, JSONUIProvider } from "@json-render/solid";import type { Spec } from "@json-render/solid";import { registry } from "./registry";
export function App(props: { spec: Spec | null }) {  return (    <JSONUIProvider registry={registry} initialState={{}}>      <Renderer spec={props.spec} registry={registry} />    </JSONUIProvider>  );}

Create a Catalog

typescript
import { defineCatalog } from "@json-render/core";import { schema } from "@json-render/solid/schema";import { z } from "zod";
export const catalog = defineCatalog(schema, {  components: {    Button: {      props: z.object({        label: z.string(),        variant: z.enum(["primary", "secondary"]).nullable(),      }),      description: "Clickable button",    },    Card: {      props: z.object({ title: z.string() }),      description: "Card container",    },  },  actions: {    submit: { description: "Submit data" },  },});

Define Components

Components receive ComponentRenderProps from the renderer:

ts
interface ComponentRenderProps<P = Record<string, unknown>> {  element: UIElement<string, P>;  children?: JSX.Element;  emit: (event: string) => void;  on: (event: string) => EventHandle;  bindings?: Record<string, string>;  loading?: boolean;}

Example:

tsx
import type { BaseComponentProps } from "@json-render/solid";
export function Button(props: BaseComponentProps<{ label: string }>) {  return (    <button onClick={() => props.emit("press")}>{props.props.label}</button>  );}

Create a Registry

typescript
import { defineRegistry } from "@json-render/solid";import { catalog } from "./catalog";import { Card } from "./Card";import { Button } from "./Button";
const { registry, handlers, executeAction } = defineRegistry(catalog, {  components: {    Card,    Button,  },  actions: {    submit: async (params, setState, state) => {      // custom action logic    },  },});

Spec Structure

json
{  "root": "card1",  "elements": {    "card1": {      "type": "Card",      "props": { "title": "Hello" },      "children": ["btn1"]    },    "btn1": {      "type": "Button",      "props": { "label": "Click me" },      "on": {        "press": { "action": "submit" }      }    }  }}

Providers

  • StateProvider: state model read/write and controlled mode via store
  • VisibilityProvider: evaluates visible conditions
  • ValidationProvider: field validation + validateForm integration
  • ActionProvider: runs built-in and custom actions
  • JSONUIProvider: combined provider wrapper

Hooks

  • useStateStore, useStateValue, useStateBinding
  • useVisibility, useIsVisible
  • useActions, useAction
  • useValidation, useOptionalValidation, useFieldValidation
  • useBoundProp
  • useUIStream, useChatUI

Built-in Actions

Handled automatically by ActionProvider:

  • setState
  • pushState
  • removeState
  • validateForm

Dynamic Props and Bindings

Nested lists can set repeat.statePath to { "$item": "field" } inside an enclosing repeat.

Supported expression forms include:

  • {"$state": "/path"}
  • {"$bindState": "/path"}
  • {"$bindItem": "field"}
  • {"$template": "Hi ${/user/name}"}
  • {"$computed": "fn", "args": {...}}
  • {"$cond": <condition>, "$then": <value>, "$else": <value>}

Use useBoundProp in components for writable bound values:

tsx
import { useBoundProp } from "@json-render/solid";
function Input(props: BaseComponentProps<{ value?: string }>) {  const [value, setValue] = useBoundProp(    props.props.value,    props.bindings?.value,  );  return (    <input      value={String(value() ?? "")}      onInput={(e) => setValue(e.currentTarget.value)}    />  );}

useStateValue, useStateBinding, and the state / errors / isValid fields from useFieldValidation are reactive accessors in Solid. Call them as functions inside JSX, createMemo, or createEffect.

Solid Reactivity Rules

  • Do not destructure component props in function signatures when values need to stay reactive.
  • Keep changing reads inside JSX expressions, createMemo, or createEffect.
  • Context values are exposed through getter-based objects so consumers always observe live signals.

Streaming UI

tsx
import { useUIStream, Renderer } from "@json-render/solid";
const stream = useUIStream({ api: "/api/generate-ui" });await stream.send("Create a support dashboard");
<Renderer  spec={stream.spec}  registry={registry}  loading={stream.isStreaming}/>;

Use useChatUI for chat + UI generation flows.

Source and attribution

Source:vercel-labs/json-renderinskills/solidat commitfc2a696

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal