|
|
3 kuukautta sitten | |
|---|---|---|
| .. | ||
| src | 4 kuukautta sitten | |
| CHANGELOG.md | 3 kuukautta sitten | |
| README.md | 4 kuukautta sitten | |
| package.json | 3 kuukautta sitten | |
| tsconfig.json | 4 kuukautta sitten | |
| tsup.config.ts | 4 kuukautta sitten | |
SolidJS renderer for json-render. Turn JSON specs into Solid components with data binding, visibility, actions, validation, and streaming.
npm install @json-render/core @json-render/solid zod
Peer dependencies: solid-js ^1.9.0 and zod ^4.0.0.
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/solid/schema";
import { z } from "zod";
export const catalog = defineCatalog(schema, {
components: {
Card: {
props: z.object({
title: z.string(),
description: z.string().nullable(),
}),
description: "A card container",
},
Button: {
props: z.object({
label: z.string(),
action: z.string(),
}),
description: "A clickable button",
},
Input: {
props: z.object({
value: z.union([z.string(), z.record(z.unknown())]).nullable(),
label: z.string(),
placeholder: z.string().nullable(),
}),
description: "Text input with optional state binding",
},
},
actions: {
submit: { description: "Submit the form" },
cancel: { description: "Cancel and close" },
},
});
defineRegistry conditionally requires the actions field only when the catalog declares actions.
import { defineRegistry, useBoundProp } from "@json-render/solid";
import { catalog } from "./catalog";
export const { registry } = defineRegistry(catalog, {
components: {
Card: (renderProps) => (
<div class="card">
<h3>{renderProps.element.props.title as string}</h3>
{renderProps.children}
</div>
),
Button: (renderProps) => (
<button onClick={() => renderProps.emit("press")}>
{renderProps.element.props.label as string}
</button>
),
Input: (renderProps) => {
const [value, setValue] = useBoundProp(
renderProps.element.props.value,
renderProps.bindings?.value,
);
return (
<label>
{renderProps.element.props.label as string}
<input
value={String(value() ?? "")}
placeholder={String(renderProps.element.props.placeholder ?? "")}
onInput={(e) => setValue(e.currentTarget.value)}
/>
</label>
);
},
},
actions: {
submit: async () => {},
cancel: async () => {},
},
});
import { Renderer, StateProvider, ActionProvider } from "@json-render/solid";
import { registry } from "./registry";
export function App(props: { spec: any }) {
return (
<StateProvider initialState={{ form: { name: "" } }}>
<ActionProvider handlers={{ submit: () => console.log("submit") }}>
<Renderer spec={props.spec} registry={registry} />
</ActionProvider>
</StateProvider>
);
}
@json-render/solid uses the same flat element map format as React/Vue:
interface Spec {
root: string;
elements: Record<string, UIElement>;
state?: Record<string, unknown>;
}
interface UIElement {
type: string;
props: Record<string, unknown>;
children?: string[];
visible?: VisibilityCondition;
watch?: Record<string, ActionBinding | ActionBinding[]>;
}
| Provider | Purpose |
|---|---|
StateProvider |
State model and JSON Pointer read/write APIs |
ActionProvider |
Action dispatch, loading tracking, confirmation flow |
VisibilityProvider |
Visibility condition evaluation from current state |
ValidationProvider |
Field-level and full-form validation |
RepeatScopeProvider |
Repeat context ($item, $index, $bindItem) |
JSONUIProvider |
Combined provider wiring for renderer trees |
Pass a StateStore to StateProvider, JSONUIProvider, or the component returned by createRenderer.
When store is provided, initialState and onStateChange are ignored.
import { createStateStore, StateProvider } from "@json-render/solid";
const store = createStateStore({ count: 0 });
<StateProvider store={store}>{/* ... */}</StateProvider>;
| Hook | Purpose |
|---|---|
useStateStore() |
Access state, get, set, update, getSnapshot |
useStateValue(path) |
Read a value by JSON Pointer path via accessor |
useStateBinding(path) |
Legacy two-way binding helper returning an accessor |
useVisibility() / useIsVisible() |
Visibility context and checks |
useActions() / useAction() |
Action context and single-action helper |
useValidation() / useOptionalValidation() |
Validation context (throwing/non-throwing) |
useFieldValidation(path, config) |
Field state accessors plus validate/touch/clear |
useBoundProp(value, binding) |
Fine-grained two-way binding helper |
useUIStream(options) |
Stream UI specs from an endpoint |
useChatUI(options) |
Chat-style spec generation hook |
These actions are available in the Solid schema and handled by ActionProvider:
setStatepushStateremoveStatevalidateFormsetState/pushState/removeState mutate the state model. validateForm validates registered fields and writes { valid, errors } to state (/formValidation by default).
Components can use either emit("event") or on("event").
emit fires named event bindings directly.on returns an EventHandle with emit, bound, and shouldPreventDefault.This mirrors the React package API while preserving Solid's fine-grained reactivity.
useUIStream and useChatUI support JSON patch streaming and mixed text/spec data parts.
import { useUIStream } from "@json-render/solid";
const stream = useUIStream({ api: "/api/generate" });
await stream.send("Build me a dashboard");
| Export | Purpose |
|---|---|
defineRegistry |
Create catalog-aware component and action registry helpers |
Renderer |
Render a Spec with a component registry |
createRenderer |
Build an app-level renderer with provider wiring |
JSONUIProvider |
Combined provider tree (state + visibility + validation + actions) |
schema |
Solid element schema with built-in actions |
createStateStore |
Framework-agnostic in-memory StateStore |
| Export | Purpose |
|---|---|
ComponentContext |
Catalog-aware component context type |
BaseComponentProps |
Catalog-agnostic component props type |
EventHandle |
Event metadata (emit, bound, shouldPreventDefault) |
StateStore |
Controlled state backend interface |
StateModel |
Renderer state model type |
SolidSchema / SolidSpec |
Solid schema/spec types |
@json-render/reactMost APIs are intentionally aligned, but there are runtime behavior differences due to Solid:
createMemo, or createEffect.Full docs: json-render.dev/docs/api/solid
Apache-2.0