Get Started

shadcn-rjsf is a set of three copy-paste registry items that bring JSON Schema–driven forms and a visual form builder to any shadcn/ui project.

Overview

The registry ships three items that work together:

form-builder-typeslib

TypeScript types (FormSchema, FormFieldDefinition, etc.), the registerValidator API, and createCustomValidator. Both the renderer and builder import from here.

form-rendererui

A React component that accepts a FormSchema and renders it as a fully accessible, themed form using your existing shadcn/ui components.

form-builderui

A drag-and-drop visual editor with a live preview pane. Emits a FormSchema via onChange on every edit.

Prerequisites

  • shadcn/ui initialized in your project (npx shadcn@latest init)
  • Next.js 13+ (App Router) or React 18+ with a compatible bundler
  • Node.js 18+

Installation

Install in order — form-builder-types must be installed first because the other two depend on it.

1form-builder-types

TypeScript types, validators, and shared utilities

Install this first — the other two depend on it.

npx shadcn@latest add "https://raw.githubusercontent.com/henrynoowah/shadcn-rjsf-form-builder/main/apps/web/public/r/form-builder-types.json"
2form-renderer

Renders a FormSchema as a live, accessible form

npx shadcn@latest add "https://raw.githubusercontent.com/henrynoowah/shadcn-rjsf-form-builder/main/apps/web/public/r/form-renderer.json"
3form-builder

Drag-and-drop visual builder that emits a FormSchema

npx shadcn@latest add "https://raw.githubusercontent.com/henrynoowah/shadcn-rjsf-form-builder/main/apps/web/public/r/form-builder.json"

Quick Start: FormRenderer

Pass a FormSchema to FormRenderer and handle submissions via onSubmit.

import { FormRenderer } from '@/components/form-renderer/form-renderer';
import type { FormSchema } from '@/lib/form-builder-types/types';

const schema: FormSchema = {
  id: 'contact',
  title: 'Contact Us',
  fields: [
    { id: 'name', type: 'text', label: 'Name', required: true, order: 0 },
    { id: 'email', type: 'email', label: 'Email', required: true, order: 1 },
    { id: 'message', type: 'textarea', label: 'Message', order: 2 },
  ],
};

export default function ContactPage() {
  return (
    <FormRenderer
      schema={schema}
      locale="en-US"
      baseLocale="en-US"
      onSubmit={(data) => console.log(data)}
    />
  );
}

Quick Start: FormBuilder

Wire FormBuilder to a FormRenderer for a live preview. The builder emits a full FormSchema on every change.

'use client';

import { useState } from 'react';
import { FormBuilder } from '@/components/form-builder/form-builder';
import { FormRenderer } from '@/components/form-renderer/form-renderer';
import type { FormSchema } from '@/lib/form-builder-types/types';

const INITIAL: FormSchema = { id: 'my-form', title: 'My Form', fields: [] };

export default function BuilderPage() {
  const [schema, setSchema] = useState<FormSchema>(INITIAL);

  return (
    <div className="grid grid-cols-2 gap-6 h-screen">
      <FormBuilder
        initialSchema={schema}
        onChange={setSchema}
        locale="en-US"
        baseLocale="en-US"
        availableLocales={['en-US']}
        className="h-full"
      />
      <div className="p-6 border rounded-lg overflow-auto">
        <FormRenderer schema={schema} locale="en-US" baseLocale="en-US" />
      </div>
    </div>
  );
}

Locale Setup

Both components accept a locale and baseLocale prop.

PropTypeDescription
localestringThe active locale key (e.g. "en-US"). Used to resolve LocalizedString values.
baseLocalestringFallback locale when the active locale has no translation for a string.

If a LocalizedString is a plain string (not a locale map), it is always used as-is regardless of the active locale.