Skip to main content

Builder

The visual builder lets users create FormSchemas via drag-and-drop. It is composed of:

  • BuilderWrapper — the main wrapper that initializes builder state
  • BuilderDndContext — wraps the dnd-kit drag-and-drop context
  • useBuilder() — hook to access and manipulate state
  • UI components: Palette, Canvas, ConfigPanel, LivePreview

Import

import {
BuilderWrapper,
BuilderDndContext,
Canvas,
ConfigPanel,
Palette,
LivePreview,
useBuilder,
builderToFormSchema,
formSchemaToBuilder,
} from "@schema-forms-data/builder";

Minimal setup

npm install @schema-forms-data/builder @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities
import {
BuilderWrapper,
BuilderDndContext,
Palette,
Canvas,
ConfigPanel,
LivePreview,
useBuilder,
builderToFormSchema,
} from "@schema-forms-data/builder";

function SaveSchema() {
const { containers, configs, stepConfig } = useBuilder();

const save = () => {
const schema = builderToFormSchema({ containers }, configs, {
nome: "My Form",
status: "ativo",
stepConfig,
});
console.log(schema); // FormSchema ready to persist
};

return <button onClick={save}>Save</button>;
}

export function MyBuilder() {
return (
<BuilderWrapper schema={null}>
<BuilderDndContext>
<div style={{ display: "flex", height: "100vh" }}>
<Palette />
<Canvas />
<ConfigPanel />
<LivePreview />
<SaveSchema />
</div>
</BuilderDndContext>
</BuilderWrapper>
);
}

BuilderWrapper Props

PropTypeDescription
schemaFormSchema | nullExisting schema to edit. null to start from scratch.
childrenReact.ReactNode
uploadTermsPdf(file: File, schemaId: string, onProgress?: (pct: number) => void) => Promise<string>Upload a PDF for terms fields.
deleteTermsPdf(uploadId: string, schemaId: string) => Promise<void>Remove a terms PDF.

Loading an existing schema

import { BuilderWrapper } from "@schema-forms-data/builder";
import { schema } from "./mySchema"; // FormSchema

<BuilderWrapper
schema={schema}
uploadTermsPdf={async (file, schemaId, onProgress) => {
return await myApi.uploadPdf(file, schemaId, onProgress);
}}
deleteTermsPdf={async (uploadId, schemaId) => {
await myApi.deletePdf(uploadId, schemaId);
}}
>
{/* ...builder UI */}
</BuilderWrapper>;

useBuilder()

Hook that returns the complete builder state. Must be used inside BuilderWrapper.

const builder = useBuilder();

State

PropertyTypeDescription
selectedIdstring | nullID of the currently selected item on the canvas
configsRecord<string, ItemConfig>Configuration for all items (steps, containers, fields)
containersContainersTreeDnD tree with the structural layout
showPreviewbooleanWhether the live preview is visible
canUndobooleanWhether undo history is available
canRedobooleanWhether redo history is available
schemaIdstring | undefinedID of the schema being edited
previewTemplateIdstringTemplate selected in the preview
stepConfigFormStepConfigVisual settings for the steps

Selectors

FunctionDescription
getConfig(id)Returns the ItemConfig for an item by ID
getAllFieldIds()Lists IDs of all fields

Configuration mutations

FunctionDescription
setSelected(id)Selects an item on the canvas
updateConfig(id, partial)Updates configuration properties of an item
setShowPreview(v)Toggles the live preview

Structural mutations

FunctionDescription
addItem(type, containerId, atIndex?)Adds a step, container, or field
addPresetBlock(block, stepId)Adds a preset block to the specified step
addPresetStepBlock(block)Adds a full preset step block
moveCanvasItem(id, from, to, atIndex?)Moves an item between containers
reorderInContainer(id, newChildren)Reorders children within a container
removeItem(id)Removes an item and its children
moveItem(id, dir)Moves an item up or down in its container

History and template

FunctionDescription
undo()Undoes the last action
redo()Redoes the last undone action
pushSnapshot()Saves a manual snapshot for undo
setPreviewTemplateId(id)Changes the preview template
updateStepConfig(partial)Updates step visual settings

Converting state to FormSchema

import { builderToFormSchema } from "@schema-forms-data/builder";

const schema = builderToFormSchema(
{ containers }, // ContainersTree
configs, // Record<string, ItemConfig>
{
nome: "Registration Form",
status: "ativo",
stepConfig,
id: "form-001", // optional
},
);

Internally resolves conditionals (from DnD IDs → field names).

findParentContainer

import { findParentContainer } from "@schema-forms-data/builder";

const parentId = findParentContainer(containers, itemId);
// returns string | null

UI Components

ComponentDescription
<Palette />Sidebar listing all draggable field types and preset blocks
<Canvas />Main drag-and-drop area — displays steps, containers, and fields
<ConfigPanel />Configuration panel for the selected item
<LivePreview />Live preview of the form using <FormRenderer>
<BuilderDndContext />Wraps DndContext + DragOverlay + drag-end logic
<TemplateSelector />Template selector for the preview

See the full guide at DnD Setup.

Builder constants

import {
FIELD_TYPES,
STRUCTURE_TYPES,
EVENT_VARIABLES,
} from "@schema-forms-data/builder";

// FIELD_TYPES: metadata for each field type used in the palette
// STRUCTURE_TYPES: metadata for step and container types
// EVENT_VARIABLES: context variables available for interpolation ({{evento.nome}}, etc.)
const {
updateConfig, // (id: string, partial: Partial<ItemConfig>) => void
addItem, // adds a field/container/step
removeItem, // removes an item and its descendants
undo, // undoes the last change
redo, // redoes the last undone change
} = useBuilder();