Getting Started
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,
},
],
},
],
},
],
};
FormSchema:id,nome,status,stepsFormStep:id,titulo,ordem,containersFormContainer:id,ordem,camposFormField: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
| Goal | Where to go |
|---|---|
| Available field types | Core Types |
| File upload, ZIP-code lookup | FormRenderer API |
| Custom validators | Validation |
| Fields that appear based on others | Conditionals |
| Create forms visually | Studio |
| Integrate the builder into your app | DnD Setup |
| Swap a field for a custom component | Component Mapper |
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,
},
],
},
],
},
],
};
FormSchema:id,nome,status,stepsFormStep:id,titulo,ordem,containersFormContainer:id,ordem,camposFormField: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
| Objetivo | Onde ir |
|---|---|
| Tipos de campo disponíveis | Core Types |
| Upload de arquivo, lookup de CEP | FormRenderer API |
| Validadores customizados | Validação |
| Campos que aparecem com base em outros | Condicionais |
| Criar formulários visualmente | Studio |
| Integrar o builder na sua app | DnD Setup |
| Trocar um campo por componente próprio | Component 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
- FormRenderer API reference — all props documented
- Injecting dependencies — uploadFile, cepLookup, terms PDF
- Custom templates — themes and CSS variables