npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@json-render/solid

v0.16.0

Published

SolidJS renderer for @json-render/core. JSON becomes Solid components.

Readme

@json-render/solid

SolidJS renderer for json-render. Turn JSON specs into Solid components with data binding, visibility, actions, validation, and streaming.

Installation

npm install @json-render/core @json-render/solid zod

Peer dependencies: solid-js ^1.9.0 and zod ^4.0.0.

Quick Start

1. Create a Catalog

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" },
  },
});

2. Define Component Implementations

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 () => {},
  },
});

3. Render Specs

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>
  );
}

Spec Format

@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[]>;
}

Providers

| 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 |

External Store (Controlled Mode)

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>;

Hooks

| 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 |

Built-in Actions

These actions are available in the Solid schema and handled by ActionProvider:

  • setState
  • pushState
  • removeState
  • validateForm

setState/pushState/removeState mutate the state model. validateForm validates registered fields and writes { valid, errors } to state (/formValidation by default).

Events and Action Binding

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.

Streaming

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");

Key Exports

| 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 |

Types

| 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 |

Differences from @json-render/react

Most APIs are intentionally aligned, but there are runtime behavior differences due to Solid:

  • Solid components run once, then update via signals.
  • Keep changing reads inside JSX expressions, createMemo, or createEffect.
  • Avoid props destructuring in component signatures when values should remain reactive.
  • Hooks that read changing state return accessors; call them inside JSX or effects.

Documentation

Full docs: json-render.dev/docs/api/solid

License

Apache-2.0