Form Builder

Visual form builder component for shadcn/ui projects

Overview

The Form Builder is a shadcn/ui component for building out forms interactively. It generates JSON Schema and JSON Forms UI Schema. It can be used independently of the Form fragment.

Note that this component does not implement everything in the JSON Forms specification, like all layouts and array fields.

Installation

Install it using the shadcn CLI:

npx shadcn@latest add https://fragno.dev/forms/form-builder.json

The components will be installed to components/ui/form-builder.

components/admin/create-form.tsx
import { FormBuilder, type GeneratedSchemas } from "@/components/ui/form-builder";

export function CreateForm() {
  const [schemas, setSchemas] = useState<GeneratedSchemas | null>(null);

  return <FormBuilder onChange={setSchemas} />;
}

Using with the Form Fragment

Combine the form builder with the useCreateForm hook to create forms dynamically:

components/admin/create-form.tsx
import { useState } from "react";
import { FormBuilder, FormMetadataEditor } from "@/components/ui/form-builder";
import type { GeneratedSchemas, FormMetadata } from "@/components/ui/form-builder";
import { formsClient } from "@/lib/forms-client";

export function CreateFormPage() {
  const [schemas, setSchemas] = useState<GeneratedSchemas | null>(null);
  const [metadata, setMetadata] = useState<FormMetadata>({
    title: "",
    description: "",
    status: "draft",
  });
  const { mutate: createForm, loading } = formsClient.useCreateForm();

  const handleSave = async () => {
    if (!schemas) return;

    const result = await createForm({
      body: {
        title: metadata.title,
        description: metadata.description,
        status: metadata.status,
        slug: metadata.title.toLowerCase().replace(/\s+/g, "-"),
        dataSchema: schemas.dataSchema,
        uiSchema: schemas.uiSchema,
      },
    });

    if (result.success) {
      console.log("Form created with ID:", result.data);
    }
  };

  return (
    <div className="space-y-6">
      <FormMetadataEditor value={metadata} onChange={setMetadata} />
      <FormBuilder onChange={setSchemas} />
      <button onClick={handleSave} disabled={loading || !schemas}>
        {loading ? "Saving..." : "Save Form"}
      </button>
    </div>
  );
}

Form Metadata Editor

The FormMetadataEditor component is specific to the Form fragment and provides fields for title, description, and status. If you're using the form builder standalone (without the Form fragment), you can remove this component from your project.

import { FormMetadataEditor } from "@/components/ui/form-builder";
import type { FormMetadata } from "@/components/ui/form-builder";

const [metadata, setMetadata] = useState<FormMetadata>({
  title: "Contact Form",
  description: "Get in touch with us",
  status: "open",
});

<FormMetadataEditor value={metadata} onChange={setMetadata} />;