solid
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese@json-render/solid
@json-render/solid
@json-render/solid@json-render/solidQuick 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>
);
}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" },
},
});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 from the renderer:
ComponentRenderPropsts
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>
);
}组件会从渲染器接收:
ComponentRenderPropsts
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;
}示例:
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
},
},
});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" }
}
}
}
}json
{
"root": "card1",
"elements": {
"card1": {
"type": "Card",
"props": { "title": "Hello" },
"children": ["btn1"]
},
"btn1": {
"type": "Button",
"props": { "label": "Click me" },
"on": {
"press": { "action": "submit" }
}
}
}
}Providers
提供者
- : state model read/write and controlled mode via
StateProviderstore - : evaluates
VisibilityProviderconditionsvisible - : field validation +
ValidationProviderintegrationvalidateForm - : runs built-in and custom actions
ActionProvider - : combined provider wrapper
JSONUIProvider
- :通过
StateProvider实现状态模型的读写与受控模式store - :评估
VisibilityProvider条件visible - :字段验证 +
ValidationProvider集成validateForm - :运行内置与自定义操作
ActionProvider - :组合式提供者包装器
JSONUIProvider
Hooks
Hooks
- ,
useStateStore,useStateValueuseStateBinding - ,
useVisibilityuseIsVisible - ,
useActionsuseAction - ,
useValidation,useOptionalValidationuseFieldValidation useBoundProp- ,
useUIStreamuseChatUI
- ,
useStateStore,useStateValueuseStateBinding - ,
useVisibilityuseIsVisible - ,
useActionsuseAction - ,
useValidation,useOptionalValidationuseFieldValidation useBoundProp- ,
useUIStreamuseChatUI
Built-in Actions
内置操作
Handled automatically by :
ActionProvidersetStatepushStateremoveStatevalidateForm
由自动处理:
ActionProvidersetStatepushStateremoveStatevalidateForm
Dynamic Props and Bindings
动态属性与绑定
Supported expression forms include:
{"$state": "/path"}{"$bindState": "/path"}{"$bindItem": "field"}{"$template": "Hi ${/user/name}"}{"$computed": "fn", "args": {...}}{"$cond": <condition>, "$then": <value>, "$else": <value>}
Use in components for writable bound values:
useBoundProptsx
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)}
/>
);
}useStateValueuseStateBindingstateerrorsisValiduseFieldValidationcreateMemocreateEffect支持的表达式形式包括:
{"$state": "/path"}{"$bindState": "/path"}{"$bindItem": "field"}{"$template": "Hi ${/user/name}"}{"$computed": "fn", "args": {...}}{"$cond": <condition>, "$then": <value>, "$else": <value>}
在组件中使用获取可写入的绑定值:
useBoundProptsx
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)}
/>
);
}useStateValueuseStateBindinguseFieldValidationstateerrorsisValidcreateMemocreateEffectSolid Reactivity Rules
Solid响应式规则
- Do not destructure component props in function signatures when values need to stay reactive.
- Keep changing reads inside JSX expressions, , or
createMemo.createEffect - Context values are exposed through getter-based objects so consumers always observe live signals.
- 当值需要保持响应式时,不要在函数签名中解构组件props。
- 将变化的读取操作放在JSX表达式、或
createMemo中。createEffect - 上下文值通过基于getter的对象暴露,因此消费者始终能观察到实时信号。
Streaming UI
流式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 for chat + UI generation flows.
useChatUItsx
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}
/>;对于聊天+UI生成流程,可使用。
useChatUI