Builder
The visual builder lets users create FormSchemas via drag-and-drop. It is composed of:
BuilderWrapper— the main wrapper that initializes builder stateBuilderDndContext— wraps the dnd-kit drag-and-drop contextuseBuilder()— 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
| Prop | Type | Description |
|---|---|---|
schema | FormSchema | null | Existing schema to edit. null to start from scratch. |
children | React.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
| Property | Type | Description |
|---|---|---|
selectedId | string | null | ID of the currently selected item on the canvas |
configs | Record<string, ItemConfig> | Configuration for all items (steps, containers, fields) |
containers | ContainersTree | DnD tree with the structural layout |
showPreview | boolean | Whether the live preview is visible |
canUndo | boolean | Whether undo history is available |
canRedo | boolean | Whether redo history is available |
schemaId | string | undefined | ID of the schema being edited |
previewTemplateId | string | Template selected in the preview |
stepConfig | FormStepConfig | Visual settings for the steps |
Selectors
| Function | Description |
|---|---|
getConfig(id) | Returns the ItemConfig for an item by ID |
getAllFieldIds() | Lists IDs of all fields |
Configuration mutations
| Function | Description |
|---|---|
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
| Function | Description |
|---|---|
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
| Function | Description |
|---|---|
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
| Component | Description |
|---|---|
<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();