Skip to main content

Getting Started

Fastest way to start

Run npx @schema-forms-data/create my-app to get a full Studio with drag-and-drop builder, JSON viewer and live preview — no manual configuration. See the Studio guide.

1. Installation

npm install @schema-forms-data/renderer react-hook-form lucide-react

If you also need the builder:

npm install @schema-forms-data/builder @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities

To install everything at once (renderer + builder):

npm install @schema-forms-data/react react-hook-form lucide-react @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities

2. Define a schema

A FormSchema follows the structure: Schema → Steps → Containers → Fields.

import type { FormSchema } from "@schema-forms-data/core";
import { FieldType } from "@schema-forms-data/core";

const schema: FormSchema = {
id: "contact-form",
nome: "Contact Form",
status: "ativo",
steps: [
{
id: "step-1",
titulo: "Your information",
ordem: 1,
containers: [
{
id: "container-1",
ordem: 1,
campos: [
{
id: "f-nome",
nome: "nome", // key in the submitted values object
label: "Full name",
tipo: FieldType.TEXTO,
obrigatorio: true,
tamanho: 6, // span in the 12-column grid
ordem: 1,
},
{
id: "f-email",
nome: "email",
label: "E-mail",
tipo: FieldType.EMAIL,
obrigatorio: true,
tamanho: 6,
ordem: 2,
},
{
id: "f-msg",
nome: "mensagem",
label: "Message",
tipo: FieldType.TEXTAREA,
obrigatorio: true,
tamanho: 12,
ordem: 3,
},
],
},
],
},
],
};
Required fields per type
  • FormSchema: id, nome, status, steps
  • FormStep: id, titulo, ordem, containers
  • FormContainer: id, ordem, campos
  • FormField: id, nome, label, tipo, obrigatorio, tamanho, ordem

3. Render the form

import { FormRenderer } from "@schema-forms-data/renderer";

export default function ContactPage() {
return (
<FormRenderer
schema={schema}
formTitle="Contact us"
template="moderno"
onComplete={async (values) => {
// values = { nome: '...', email: '...', mensagem: '...' }
await fetch("/api/contact", {
method: "POST",
body: JSON.stringify(values),
});
}}
/>
);
}

FormRenderer handles:

  • Rendering each field type with the correct component
  • Native validation (required, regex, min/max) and async custom validators
  • Multi-step navigation with animated progress indicator
  • Conditional visibility of fields and containers
  • Styling via the template's CSS Variables

4. Multi-step form

Add more steps — each step is submitted independently via onSubmitStep and the final step calls onComplete:

<FormRenderer
schema={multiStepSchema}
formTitle="Registration"
template="moderno"
onSubmitStep={async (stepIndex, values) => {
// Called when user clicks Next on each step
await api.saveStepDraft(stepIndex, values);
}}
onComplete={async (allValues) => {
// Called on the last step with ALL accumulated values
await api.finalize(allValues);
}}
/>

5. Add file upload (optional)

The renderer never makes HTTP calls directly — you inject your own:

<FormRenderer
schema={schema}
uploadFile={async (file, fieldName) => {
const formData = new FormData();
formData.append("file", file);
const res = await fetch("/api/upload", { method: "POST", body: formData });
const { url } = await res.json();
return url; // stored as the field value
}}
onComplete={async (values) => console.log(values)}
/>

6. Observe form state

Use <FormSpy> inside the renderer tree to react to any field change:

import { FormSpy } from "@schema-forms-data/renderer";

// Inside a component rendered within <FormRenderer>:
<FormSpy
onChange={(state) => {
if (state.dirty)
sessionStorage.setItem("draft", JSON.stringify(state.values));
}}
/>;

Or use the onValuesChange prop for a simpler callback:

<FormRenderer
schema={schema}
onValuesChange={(values) => console.log("live:", values)}
onComplete={handleComplete}
/>

7. Use the Builder

Let users create their own schemas visually:

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

export default function BuilderPage() {
return (
<BuilderWrapper schema={null} onSave={(schema) => api.save(schema)}>
<BuilderDndContext>
<div style={{ display: "flex", height: "100vh" }}>
<Palette />
<Canvas />
<ConfigPanel />
</div>
</BuilderDndContext>
</BuilderWrapper>
);
}

See the full Builder docs.

8. Next steps

GoalWhere to go
Available field typesCore Types
File upload, ZIP-code lookupFormRenderer API
Custom validatorsValidation
Fields that appear based on othersConditionals
Create forms visuallyStudio
Integrate the builder into your appDnD Setup
Swap a field for a custom componentComponent Mapper
Forma mais rápida de começar

Execute npx @schema-forms-data/create my-app para ter um Studio completo com builder drag-and-drop, visualizador JSON e preview ao vivo — sem configuração manual. Veja o guia do Studio.

1. Instalação

npm install @schema-forms-data/renderer react-hook-form lucide-react

Se você também precisar do builder:

npm install @schema-forms-data/builder @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities

Para usar tudo de uma vez (renderer + builder):

npm install @schema-forms-data/react react-hook-form lucide-react @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities

2. Defina um schema

Um FormSchema tem a estrutura: Schema → Steps → Containers → Fields.

import type { FormSchema } from "@schema-forms-data/core";
import { FieldType } from "@schema-forms-data/core";

const schema: FormSchema = {
id: "formulario-contato",
nome: "Formulário de Contato",
status: "ativo",
steps: [
{
id: "step-1",
titulo: "Suas informações",
ordem: 1,
containers: [
{
id: "container-1",
ordem: 1,
campos: [
{
id: "f-nome",
nome: "nome", // chave no objeto de values submetido
label: "Nome completo",
tipo: FieldType.TEXTO,
obrigatorio: true,
tamanho: 6, // span no grid de 12 colunas
ordem: 1,
},
{
id: "f-email",
nome: "email",
label: "E-mail",
tipo: FieldType.EMAIL,
obrigatorio: true,
tamanho: 6,
ordem: 2,
},
{
id: "f-msg",
nome: "mensagem",
label: "Mensagem",
tipo: FieldType.TEXTAREA,
obrigatorio: true,
tamanho: 12,
ordem: 3,
},
],
},
],
},
],
};
Campos obrigatórios de cada tipo
  • FormSchema: id, nome, status, steps
  • FormStep: id, titulo, ordem, containers
  • FormContainer: id, ordem, campos
  • FormField: id, nome, label, tipo, obrigatorio, tamanho, ordem

3. Renderize o formulário

import { FormRenderer } from "@schema-forms-data/renderer";

export default function ContatoPage() {
return (
<FormRenderer
schema={schema}
formTitle="Fale conosco"
template="moderno"
onComplete={async (values) => {
// values = { nome: '...', email: '...', mensagem: '...' }
await fetch("/api/contato", {
method: "POST",
body: JSON.stringify(values),
});
}}
/>
);
}

O FormRenderer cuida de:

  • Renderizar cada tipo de campo com o componente correto
  • Validação nativa (required, regex, min/max) e validadores customizados async
  • Navegação multi-step com indicador de progresso animado
  • Visibilidade condicional de campos e containers
  • Estilização via CSS Variables do template

4. Próximos passos

ObjetivoOnde ir
Tipos de campo disponíveisCore Types
Upload de arquivo, lookup de CEPFormRenderer API
Validadores customizadosValidação
Campos que aparecem com base em outrosCondicionais
Criar formulários visualmenteStudio
Integrar o builder na sua appDnD Setup
Trocar um campo por componente próprioComponent Mapper

4. Multi-step form

Add more steps — each step is submitted independently via onSubmitStep and the final step calls onComplete:

<FormRenderer
schema={multiStepSchema}
formTitle="Registration"
template="moderno"
onSubmitStep={async (stepIndex, values) => {
// Called when user clicks Next on each step
await api.saveStepDraft(stepIndex, values);
}}
onComplete={async (allValues) => {
// Called on the last step with ALL accumulated values
await api.finalize(allValues);
}}
/>

5. Add file upload (optional)

The renderer never makes HTTP calls directly — you inject your own:

<FormRenderer
schema={schema}
uploadFile={async (file, fieldName) => {
const formData = new FormData();
formData.append("file", file);
const res = await fetch("/api/upload", { method: "POST", body: formData });
const { url } = await res.json();
return url; // stored as the field value
}}
onComplete={async (values) => console.log(values)}
/>

6. Observe form state

Use <FormSpy> inside the renderer tree to react to any field change:

import { FormSpy } from "@schema-forms-data/renderer";

// Inside a component rendered within <FormRenderer>:
<FormSpy
onChange={(state) => {
if (state.dirty)
sessionStorage.setItem("draft", JSON.stringify(state.values));
}}
/>;

Or use the onValuesChange prop for a simpler callback:

<FormRenderer
schema={schema}
onValuesChange={(values) => console.log("live:", values)}
onComplete={handleComplete}
/>

7. Use the Builder

Let users create their own schemas visually:

import {
BuilderProvider,
Canvas,
Palette,
ConfigPanel,
} from "@schema-forms-data/builder";

export default function BuilderPage() {
return (
<BuilderProvider onSchemaChange={(schema) => api.save(schema)}>
<div style={{ display: "flex", height: "100vh" }}>
<Palette />
<Canvas />
<ConfigPanel />
</div>
</BuilderProvider>
);
}

See the full Builder docs.

return url; // return the uploaded file's URL or ID

}} onSubmit= />


## 5. Use the visual builder

Install the builder package:

```bash
npm install @schema-forms-data/builder lucide-react

Mount the builder:

import {
BuilderProvider,
Canvas,
ConfigPanel,
Palette,
} from "@schema-forms-data/builder";

export default function BuilderPage() {
return (
<BuilderProvider
onSchemaChange={(schema) => {
localStorage.setItem("my-schema", JSON.stringify(schema));
}}
>
<div style={{ display: "flex", height: "100vh" }}>
<Palette />
<Canvas />
<ConfigPanel />
</div>
</BuilderProvider>
);
}

The user can drag fields from the palette onto the canvas, configure them in the panel, and the onSchemaChange callback fires on every change with the resulting FormSchema.

Next steps