@json-render/solid
SolidJS components, providers, and hooks for rendering json-render specs.
Installation
npm install @json-render/core @json-render/solidPeer dependencies: solid-js ^1.9.0 and zod ^4.0.0.
npm install solid-js zodProviders
StateProvider
<StateProvider
initialState={{}}
onStateChange={(changes) => console.log(changes)}
>
{/* children */}
</StateProvider>| Prop | Type | Description |
|---|---|---|
store | StateStore | External store (controlled mode). When provided,
|
initialState | Record<string, unknown> | Initial state model for uncontrolled mode. |
onStateChange | (changes: Array<{ path: string; value: unknown }>) => void | Called for uncontrolled state updates. |
ActionProvider
<ActionProvider
handlers={{ submit: async (params) => {} }}
navigate={(path) => {}}
>
{/* children */}
</ActionProvider>VisibilityProvider
<VisibilityProvider>{/* children */}</VisibilityProvider>ValidationProvider
<ValidationProvider customFunctions={{ custom: (value) => Boolean(value) }}>
{/* children */}
</ValidationProvider>JSONUIProvider
Combined provider wrapper for state, visibility, validation, and actions.
<JSONUIProvider
registry={registry}
initialState={{}}
handlers={handlers}
validationFunctions={validationFunctions}
>
<Renderer spec={spec} registry={registry} />
</JSONUIProvider>defineRegistry
Create a typed component registry and action helpers from a catalog.
const { registry, handlers, executeAction } = defineRegistry(catalog, {
components: {
Card: (renderProps) => <div>{renderProps.children}</div>,
Button: (renderProps) => (
<button onClick={() => renderProps.emit("press")}>
{renderProps.element.props.label as string}
</button>
),
},
actions: {
submit: async (params, setState, state) => {
// custom action logic
},
},
});Components
Renderer
<Renderer spec={spec} registry={registry} loading={false} />Renders a Spec tree using your registry.
createRenderer
Build an app-level renderer from catalog + components:
const AppRenderer = createRenderer(catalog, {
Card: (renderProps) => <div>{renderProps.children}</div>,
});
<AppRenderer spec={spec} state={{}} onAction={(name, params) => {}} />;Hooks
useStateStore()useStateValue(path)- returns an accessoruseStateBinding(path)- returns[Accessor<T | undefined>, setValue]useVisibility()/useIsVisible(condition)useActions()/useAction(binding)useValidation()/useOptionalValidation()useFieldValidation(path, config)- returns accessor-backedstate,errors, andisValiduseBoundProp(value, bindingPath)useUIStream(options)useChatUI(options)
Built-in Actions
ActionProvider handles these built-in actions:
setStatepushStateremoveStatevalidateForm
Component Props
Registry components receive:
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;
}Use emit("event") to dispatch event bindings. Use on("event") to access EventHandle metadata (bound, shouldPreventDefault, emit).
Reactivity Notes
- Keep changing reads in JSX expressions,
createMemo, orcreateEffect. - Avoid props destructuring in component signatures when you need live updates.
StateProviderand other contexts expose getter-backed values so consumers read live signals.useStateValue,useStateBinding, anduseFieldValidationexpose reactive accessors; call them as functions.