Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"@json-render/svelte",
"@json-render/solid",
"@json-render/react-three-fiber",
"@json-render/yaml"
"@json-render/yaml",
"@json-render/astro"
]
],
"linked": [],
Expand Down
18 changes: 18 additions & 0 deletions .changeset/v0-15-astro-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"@json-render/core": minor
"@json-render/astro": minor
---

Add @json-render/astro renderer

### New:

- **@json-render/astro**: Astro renderer with `defineRegistry` + `<Renderer />` for `.astro` components. Same API pattern as React, Vue, Svelte, and Solid renderers.
- `defineRegistry(catalog, { components })`: create a typed registry from a catalog with Astro components
- `<Renderer />` (`@json-render/astro/Renderer.astro`): walks the spec tree and renders each element using real `.astro` files with `<slot />` for children
- `<ElementRenderer />` (`@json-render/astro/ElementRenderer.astro`): recursive tree walker using `Astro.self`
- `schema`: element schema with Astro-specific default rules (static HTML, semantic HTML, no interactive actions)
- Works in SSG (build time, no adapter) and SSR (request time, with any adapter: Cloudflare, Netlify, Node, Vercel)
- Astro Islands pattern: static content via `@json-render/astro` + interactive islands via framework renderers (`@json-render/react`, `/vue`, `/svelte`, `/solid`) with `client:*` directives
- Full support for `$state`, `$cond`, `$item`, `$index`, `visible`, and `repeat` expressions
- Astro example project with full static demo and hybrid islands demo (React counter)
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ coverage

# Build Outputs
.next/
.astro/
out/
build
dist
Expand Down
151 changes: 130 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ npm install @json-render/core @json-render/vue
npm install @json-render/core @json-render/svelte
# or for SolidJS
npm install @json-render/core @json-render/solid
# or for SSR HTML (Astro, Cloudflare Workers, edge)
npm install @json-render/core @json-render/astro
# or for 3D scenes
npm install @json-render/core @json-render/react-three-fiber @react-three/fiber @react-three/drei three
```
Expand Down Expand Up @@ -115,27 +117,28 @@ function Dashboard({ spec }) {

## Packages

| Package | Description |
| --------------------------- | ---------------------------------------------------------------------- |
| `@json-render/core` | Schemas, catalogs, AI prompts, dynamic props, SpecStream utilities |
| `@json-render/react` | React renderer, contexts, hooks |
| `@json-render/vue` | Vue 3 renderer, composables, providers |
| `@json-render/svelte` | Svelte 5 renderer with runes-based reactivity |
| `@json-render/solid` | SolidJS renderer with fine-grained reactive contexts |
| `@json-render/shadcn` | 36 pre-built shadcn/ui components (Radix UI + Tailwind CSS) |
| `@json-render/react-three-fiber` | React Three Fiber renderer for 3D scenes (19 built-in components) |
| `@json-render/react-native` | React Native renderer with standard mobile components |
| `@json-render/remotion` | Remotion video renderer, timeline schema |
| `@json-render/react-pdf` | React PDF renderer for generating PDF documents from specs |
| `@json-render/react-email` | React Email renderer for HTML/plain-text emails from specs |
| `@json-render/image` | Image renderer for SVG/PNG output (OG images, social cards) via Satori |
| `@json-render/codegen` | Utilities for generating code from json-render UI trees |
| `@json-render/redux` | Redux / Redux Toolkit adapter for `StateStore` |
| `@json-render/zustand` | Zustand adapter for `StateStore` |
| `@json-render/jotai` | Jotai adapter for `StateStore` |
| `@json-render/xstate` | XState Store (atom) adapter for `StateStore` |
| `@json-render/mcp` | MCP Apps integration for Claude, ChatGPT, Cursor, VS Code |
| `@json-render/yaml` | YAML wire format with streaming parser, edit modes, AI SDK transform |
| Package | Description |
| -------------------------------- | ---------------------------------------------------------------------- |
| `@json-render/core` | Schemas, catalogs, AI prompts, dynamic props, SpecStream utilities |
| `@json-render/react` | React renderer, contexts, hooks |
| `@json-render/vue` | Vue 3 renderer, composables, providers |
| `@json-render/svelte` | Svelte 5 renderer with runes-based reactivity |
| `@json-render/solid` | SolidJS renderer with fine-grained reactive contexts |
| `@json-render/shadcn` | 36 pre-built shadcn/ui components (Radix UI + Tailwind CSS) |
| `@json-render/react-three-fiber` | React Three Fiber renderer for 3D scenes (19 built-in components) |
| `@json-render/react-native` | React Native renderer with standard mobile components |
| `@json-render/remotion` | Remotion video renderer, timeline schema |
| `@json-render/react-pdf` | React PDF renderer for generating PDF documents from specs |
| `@json-render/react-email` | React Email renderer for HTML/plain-text emails from specs |
| `@json-render/image` | Image renderer for SVG/PNG output (OG images, social cards) via Satori |
| `@json-render/codegen` | Utilities for generating code from json-render UI trees |
| `@json-render/redux` | Redux / Redux Toolkit adapter for `StateStore` |
| `@json-render/zustand` | Zustand adapter for `StateStore` |
| `@json-render/jotai` | Jotai adapter for `StateStore` |
| `@json-render/xstate` | XState Store (atom) adapter for `StateStore` |
| `@json-render/astro` | Astro renderer for `.astro` components |
| `@json-render/mcp` | MCP Apps integration for Claude, ChatGPT, Cursor, VS Code |
| `@json-render/yaml` | YAML wire format with streaming parser, edit modes, AI SDK transform |

## Renderers

Expand Down Expand Up @@ -224,6 +227,111 @@ const { registry } = defineRegistry(catalog, {
<Renderer spec={spec} registry={registry} />;
```

### Astro (UI)

Static content ships zero JS. Interactive islands use framework renderers hydrated client-side.

```astro
---
import Renderer from "@json-render/astro/Renderer.astro";
import { defineRegistry } from "@json-render/astro";
import { catalog } from "../lib/catalog";
import Card from "../components/Card.astro";
import Text from "../components/Text.astro";

const { registry } = defineRegistry(catalog, {
components: { Card, Text },
});
---

<Renderer spec={spec} registry={registry} />
```

#### Astro + React Island

```tsx
// Counter.tsx — interactive React island using @json-render/react
import { useState, useRef, useMemo } from "react";
import {
StateProvider, ActionProvider, VisibilityProvider,
Renderer, defineRegistry,
} from "@json-render/react";
import { schema } from "@json-render/react/schema";
import { defineCatalog, type Spec } from "@json-render/core";
import { z } from "zod";

const catalog = defineCatalog(schema, {
components: {
Text: { props: z.object({ content: z.string() }), description: "Text" },
Button: { props: z.object({ label: z.string() }), description: "Button" },
},
actions: {
increment: { description: "Increment counter" },
},
});

const { registry, handlers: createHandlers } = defineRegistry(catalog, {
components: {
Text: ({ props }) => <span>{String(props.content ?? "")}</span>,
Button: ({ props, emit }) => (
<button onClick={() => emit("press")}>{props.label}</button>
),
},
actions: {
increment: async (_params, setState) => {
setState((prev) => ({ ...prev, count: Number(prev.count || 0) + 1 }));
},
},
});

const spec: Spec = {
root: "root",
state: { count: 0 },
elements: {
root: { type: "Text", props: { content: { $state: "/count" } }, children: ["btn"] },
btn: { type: "Button", props: { label: "+" }, on: { press: { action: "increment" } } },
},
};

export default function Counter() {
const [state, setState] = useState<Record<string, unknown>>(spec.state ?? {});
const stateRef = useRef(state);
const setStateRef = useRef(setState);
stateRef.current = state;
setStateRef.current = setState;

const actionHandlers = useMemo(
() => createHandlers(() => setStateRef.current, () => stateRef.current),
[],
);

return (
<StateProvider initialState={state}>
<VisibilityProvider>
<ActionProvider handlers={actionHandlers}>
<Renderer spec={spec} registry={registry} />
</ActionProvider>
</VisibilityProvider>
</StateProvider>
);
}
```

```astro
---
import Renderer from "@json-render/astro/Renderer.astro";
import Counter from "../components/Counter";
---

<!-- Static SSR content (zero JS) -->
<Renderer spec={staticSpec} registry={registry} />

<!-- Interactive React island (hydrated client-side) -->
<Counter client:visible />
```

Islands also work with Vue (`@json-render/vue`), Svelte (`@json-render/svelte`), and Solid (`@json-render/solid`).

### shadcn/ui (Web)

```tsx
Expand Down Expand Up @@ -608,6 +716,7 @@ pnpm dev
- Vue Example: run `pnpm dev` in `examples/vue`
- Vite Renderers (React + Vue + Svelte + Solid): run `pnpm dev` in `examples/vite-renderers`
- React Native example: run `npx expo start` in `examples/react-native`
- Astro SSR Example: run `pnpm dev` in `examples/astro`

## How It Works

Expand Down
Loading