# @json-render/shadcn
Pre-built [shadcn/ui](https://ui.shadcn.com/) components for json-render. Drop-in catalog definitions and React implementations for 36 components built on Radix UI + Tailwind CSS.
## Installation
```bash
npm install @json-render/shadcn @json-render/core @json-render/react zod
```
## Quick Start
### 1. Create a Catalog
Import standard definitions from `@json-render/shadcn/catalog` and pass them to `defineCatalog`:
```typescript
import { defineCatalog } from "@json-render/core";
import { schema } from "@json-render/react/schema";
import { shadcnComponentDefinitions } from "@json-render/shadcn/catalog";
const catalog = defineCatalog(schema, {
components: {
// Pick the components you need
Card: shadcnComponentDefinitions.Card,
Stack: shadcnComponentDefinitions.Stack,
Heading: shadcnComponentDefinitions.Heading,
Button: shadcnComponentDefinitions.Button,
Input: shadcnComponentDefinitions.Input,
},
actions: {},
});
```
> **Note:** State actions (`setState`, `pushState`, `removeState`) are built into the React schema and handled automatically by `ActionProvider`. You don't need to declare them in your catalog.
### 2. Create a Registry
Import standard implementations from `@json-render/shadcn` and pass them to `defineRegistry`:
```typescript
import { defineRegistry } from "@json-render/react";
import { shadcnComponents } from "@json-render/shadcn";
const { registry } = defineRegistry(catalog, {
components: {
Card: shadcnComponents.Card,
Stack: shadcnComponents.Stack,
Heading: shadcnComponents.Heading,
Button: shadcnComponents.Button,
Input: shadcnComponents.Input,
},
});
```
### 3. Render
```tsx
import { Renderer } from "@json-render/react";
function App({ spec }) {
return ;
}
```
## Extending with Custom Components
Pick standard components as a base and add your own alongside them:
```typescript
import { z } from "zod";
// Catalog
const catalog = defineCatalog(schema, {
components: {
// Standard
Card: shadcnComponentDefinitions.Card,
Stack: shadcnComponentDefinitions.Stack,
Button: shadcnComponentDefinitions.Button,
// Custom
Metric: {
props: z.object({
label: z.string(),
value: z.string(),
trend: z.enum(["up", "down", "neutral"]).nullable(),
}),
description: "KPI metric display",
},
},
actions: {},
});
// Registry
const { registry } = defineRegistry(catalog, {
components: {
// Standard
Card: shadcnComponents.Card,
Stack: shadcnComponents.Stack,
Button: shadcnComponents.Button,
// Custom
Metric: ({ props }) => (
{props.label}
{props.value}
),
},
});
```
## Standard Components
### Layout
| Component | Description |
|-----------|-------------|
| `Card` | Container card with optional title and description |
| `Stack` | Flex container (horizontal/vertical) with gap, alignment, justify |
| `Grid` | Grid layout (1-6 columns) |
| `Separator` | Visual separator line |
### Navigation
| Component | Description |
|-----------|-------------|
| `Tabs` | Tabbed navigation |
| `Accordion` | Collapsible accordion sections |
| `Collapsible` | Single collapsible section with trigger |
| `Pagination` | Page navigation |
### Overlay
| Component | Description |
|-----------|-------------|
| `Dialog` | Modal dialog |
| `Drawer` | Bottom drawer |
| `Tooltip` | Hover tooltip |
| `Popover` | Click-triggered popover |
| `DropdownMenu` | Dropdown menu with selectable items |
### Content
| Component | Description |
|-----------|-------------|
| `Heading` | Heading text (h1-h4) |
| `Text` | Paragraph text with variants (body, caption, muted, lead, code) |
| `Image` | Placeholder image |
| `Avatar` | User avatar with fallback initials |
| `Badge` | Status badge |
| `Alert` | Alert banner |
| `Carousel` | Horizontally scrollable carousel |
| `Table` | Data table with columns and rows |
### Feedback
| Component | Description |
|-----------|-------------|
| `Progress` | Progress bar |
| `Skeleton` | Loading placeholder |
| `Spinner` | Loading spinner |
### Input
| Component | Description |
|-----------|-------------|
| `Button` | Clickable button with variants |
| `Link` | Anchor link |
| `Input` | Text input with label, validation, and `validateOn` timing |
| `Textarea` | Multi-line text input with validation and `validateOn` |
| `Select` | Dropdown select with validation and `validateOn` |
| `Checkbox` | Checkbox input with validation and `validateOn` |
| `Radio` | Radio button group with validation and `validateOn` |
| `Switch` | Toggle switch with validation and `validateOn` |
| `Slider` | Range slider |
| `Toggle` | Toggle button |
| `ToggleGroup` | Group of toggle buttons |
| `ButtonGroup` | Group of buttons with selected state |
## Built-in Actions
State actions (`setState`, `pushState`, `removeState`, `validateForm`) are built into the `@json-render/react` schema and handled automatically by `ActionProvider`. They are included in prompts without needing to be declared in your catalog.
| Action | Description |
|--------|-------------|
| `setState` | Set a value at a state path |
| `pushState` | Push a value onto an array in state |
| `removeState` | Remove an item from an array in state |
| `validateForm` | Validate all fields and write result to state |
### Validation Timing (`validateOn`)
All form components support the `validateOn` prop to control when validation runs:
| Value | Description | Default For |
|-------|-------------|-------------|
| `"change"` | Validate on every input change | Select, Checkbox, Radio, Switch |
| `"blur"` | Validate when field loses focus | Input, Textarea |
| `"submit"` | Validate only on form submission | — |
## Exports
| Entry Point | Exports |
|-------------|---------|
| `@json-render/shadcn` | `shadcnComponents` |
| `@json-render/shadcn/catalog` | `shadcnComponentDefinitions` |
The `/catalog` entry point contains only Zod schemas (no React dependency), so it can be used in server-side code for prompt generation.