Components

Form Builder

A drag-and-drop form designer with a field palette, canvas, properties panel, live preview and code export for TanStack Form, React Hook Form, Zod and raw useState.

Client Only

Installation

pnpm dlx @docyrus/cli add @docyrus/ui-form-builder
Required Packages(3 packages)
pnpm add @dnd-kit/core @dnd-kit/sortable nanoid
UI Primitives(8 components)
npx shadcn@latest add button input label switch select tabs scroll-area collapsible

Usage

import { FormBuilder } from "@docyrus/ui/components/form-builder";

export function FormBuilderPage() {
  return (
    <div className="h-[720px]">
      <FormBuilder />
    </div>
  );
}

The component fills its parent (flex h-full flex-col), so wrap it in a sized container or pass a className with explicit dimensions.

Reading builder state

useBuilderContext is exposed for consumers that want to render their own toolbar, persist drafts, or react to selection. It must be used inside a <BuilderProvider>, which <FormBuilder> already renders internally — to read state from outside the default UI, render <BuilderProvider> yourself and place <FormBuilder> (or its sub-components) underneath.

import {
  BuilderProvider,
  useBuilderContext
} from "@docyrus/ui/components/form-builder";

function FieldCount() {
  const { state } = useBuilderContext();

  return <span>{state.fields.length} fields</span>;
}

Built-in field types

The palette ships with 9 categories covering 30+ field types from the Docyrus form-fields library. Each type maps to a DynamicFormField renderer, so what users build in the canvas is exactly what they get at runtime.

CategoryField types
Text & Inputfield-text, field-textarea, field-email, field-url, field-phone, field-color
Numbersfield-number, field-money, field-percent, field-rating, field-duration, field-currency
Selectionfield-select, field-multiSelect, field-tagSelect, field-radioGroup, field-enum, field-status, field-approvalStatus
Date & Timefield-date, field-dateTime, field-time, field-dateRange
Togglefield-switch, field-checkbox
Visual & Mediafield-icon, field-avatar, field-file, field-image
Relation & Userfield-userSelect, field-userMultiSelect, field-relation, field-locationSelect
Rich Contentfield-htmlEditor, field-codeEditor, field-docEditor, field-emailEditor
Data Structurefield-taskList, field-schemaRepeater, field-queryBuilder

Code export

Switch the toolbar's view mode to Code to render generated TSX for the current form. Four templates are available:

TemplateOutput
tanstackuseForm from @tanstack/react-form driving DynamicFormField
rhfuseForm from react-hook-form with native inputs
zodreact-hook-form + zodResolver and a generated z.object schema
rawuseState per field, plain <input> markup

The generated code uses user-project import paths (@/components/docyrus/form-fields, @/components/ui/button) so it drops straight into a project that has installed @docyrus/ui-form-fields and the matching shadcn primitives. Adjust the prefix if your project uses a different alias.

API Reference

<FormBuilder>

PropTypeDefaultDescription
classNamestringflex h-full flex-colOverride the root container classes. Useful for setting an explicit height or max width.
initialStatePartial<BuilderState>–Seed document, shallow-merged over the built-in defaults (selectedFieldId forced to null). Takes precedence over the persisted draft.
onChange(state: BuilderState) => void–Called with the full document after every change, skipping the initial mount.
persistbooleaninitialState === undefinedRestore from / save to the persist key. On by default for the standalone tool, off by default whenever initialState is supplied so a seeded builder never overwrites the standalone draft.
persistKeystringdocyrus-form-builder-statelocalStorage key backing persistence. Give each builder its own key when more than one can be mounted (or more than one form edited) in the same browser.

useBuilderContext()

Returns the live builder state and dispatchers. Throws if called outside a <BuilderProvider>.

PropertyTypeDescription
stateBuilderStateThe full reducer state (fields, selection, view mode, grid columns, form title).
dispatchDispatch<BuilderAction>Low-level dispatcher for every reducer action (including undo/redo, reorder, clear).
selectedFieldBuilderField | nullThe currently focused field, or null when nothing is selected.
addField(componentType: string, index?: number) => voidAppend (or insert at index) a new field of the given palette type.
removeField(id: string) => voidRemove a field by id.
duplicateField(id: string) => voidClone a field by id and insert it directly after the source.
selectField(id: string | null) => voidUpdate the selection.
canUndobooleantrue when there is at least one entry in the undo stack.
canRedobooleantrue when there is at least one entry in the redo stack.

<BuilderProvider>

Wraps children in the builder reducer and 50-step history; rendered automatically by <FormBuilder>. Accepts children plus the same initialState / onChange / persist / persistKey contract as <FormBuilder>.

Components

ComponentDescription
FormBuilderThe full builder experience (toolbar, palette, canvas, properties panel, preview, code export). Canvas and Preview render sections as real panels (own inner grid, panel width, optional hidden title) and apply the form-level label / size / style rules; Preview additionally honors readOnly and enforces every validation token. The properties panel has four tabs — General, Options (enum fields only), Validation, and Actions. The Validation tab supports required toggle, min/max/pattern constraints, and Custom Validations — JSONata-based rules evaluated on submit. Each custom validation rule has an expression (returns true to pass) and a message shown on failure. Supported field types: text, textarea, email, url, phone, number, money, percent, duration, date, dateTime, rating.
BuilderProviderReducer + 50-step undo/redo history provider.
useBuilderContextHook for reading builder state and triggering actions.
generateCodeStandalone helper that turns a BuilderState into a TSX string for any of the four templates.
FIELD_TYPE_CONFIGSRegistry map of every palette type → label, icon, default IField patch, seeded enum options and search tags.
PALETTE_CATEGORIESThe nine palette categories with their ordered items.
createDefaultBuilderFieldBuilds the default fieldConfig / enumOptions / customProps for a palette type (id + slug generation included).
getPropertySchemasProperty schema list (common + type-specific) driving the General tab for a field type.
hasEnumOptionsWhether a field type shows the Options tab.
ENUM_FIELD_TYPESSet of enum-backed field types.
TEXT_VALIDATION_TYPES / NUMBER_VALIDATION_TYPES / CUSTOM_VALIDATION_TYPESSets describing which validation controls a field type exposes (length/pattern, min/max, JSONata rules).

Type Exports

TypeDescription
FormBuilderPropsProps for <FormBuilder> (className?).
BuilderFieldA single field on the canvas: id, component type, IField config, optional enum options and colSpan.
BuilderStateTop-level reducer state — fields, selectedFieldId, viewMode, formTitle, formDescription, gridColumns.
BuilderActionUnion of every dispatchable action (add/remove/reorder/duplicate/update/select/undo/redo/clear/set-meta/set-view-mode).
PaletteCategoryA palette section (id, label, icon, items).
PaletteItemA single palette entry (component type, label, icon, default field type, search tags).
PropertySchemaMetadata for one editable property in the right-hand properties panel.
CodeTemplate'tanstack' | 'rhf' | 'zod' | 'raw' — accepted by generateCode.
FieldActionTrigger + ordered blocks definition stored on BuilderField.field.fieldActions. Authored through the Actions tab in the properties panel.
FieldActionBlockNamed block with if / else-if / else branches and unconditional steps.
FieldActionStepDiscriminated union of imperative commands: value writes, visibility, required, disabled, readOnly.
BuilderSectionA container panel: id, title?, colSpan?, gridColumns?, panelWidth?, collapsible?, hideLabel?. Fields join it via BuilderField.sectionId.
FormLayoutStyleForm-level styling patch — labelAlign / labelWidth / fieldSize / fieldVariant.
ViewMode'edit' | 'preview' | 'code'.
PropertyType'string' | 'number' | 'boolean' | 'select' — the control kind of a PropertySchema.
FieldTypeConfigOne entry of FIELD_TYPE_CONFIGS.

On this page