Pular para o conteúdo principal

Conectando ao seu Backend

O FormRenderer é completamente headless em relação a dados — você injeta suas próprias funções para upload de arquivos, busca de CEP, recursos de termos e props dinâmicas de campos.

uploadFile

Chamado quando o usuário seleciona um arquivo em um campo FILE.

<FormRenderer
schema={schema}
uploadFile={async (file, fieldName) => {
const form = new FormData();
form.append('file', file);
form.append('campo', fieldName);

const res = await fetch('/api/upload', {
method: 'POST',
body: form,
headers: { Authorization: `Bearer ${token}` },
});

const { id } = await res.json();
return id; // deve retornar uma string (URL ou ID do upload)
}}
onComplete={...}
/>

O valor do campo armazenado será a string retornada pela função.

cepLookup

Chamado quando o usuário termina de digitar um CEP em um campo CEP. O renderer preenche automaticamente os campos irmãos logradouro, bairro, cidade e estado com o resultado.

<FormRenderer
schema={schema}
cepLookup={async (cep) => {
const res = await fetch(`https://viacep.com.br/ws/${cep}/json/`);
const data = await res.json();
return {
logradouro: data.logradouro,
bairro: data.bairro,
cidade: data.localidade,
estado: data.uf,
erro: !!data.erro,
};
}}
onComplete={...}
/>

Se cepLookup não for fornecido, o campo CEP usa a API pública ViaCEP como fallback.

resolveTermsUploadUrl

Chamado quando um campo TERMS tem um termoPdfUploadId — resolve o uploadId para uma URL pública de PDF.

<FormRenderer
schema={schema}
resolveTermsUploadUrl={async (uploadId) => {
const res = await fetch(`/api/uploads/${uploadId}/url`);
const { url } = await res.json();
return url;
}}
onComplete={...}
/>

fieldResolvers — Props dinâmicas de campos

Use fieldResolvers para carregar props de campo em tempo de render com base no estado do formulário ou em dados externos.

1. Defina a chave no schema

{
"nome": "tamanho_camiseta",
"tipo": "select",
"label": "Tamanho da camiseta",
"resolvePropsKey": "opcoes_camiseta"
}

2. Registre o resolver no renderer

<FormRenderer
schema={schema}
externalData={{ "evento.tamanhosCamiseta": ["P", "M", "G", "GG"] }}
fieldResolvers={{
opcoes_camiseta: (field, _values, external) => ({
opcoes: (external['evento.tamanhosCamiseta'] as string[])
?.map(t => ({ valor: t, label: t })) ?? field.opcoes,
}),
// Desabilitar campo com base em outro valor
forma_pagamento: (_field, values) => ({
isDisabled: values['pagamento_confirmado'] === true,
}),
}}
onComplete={...}
/>

O resolver recebe (field, formValues, externalData) e retorna um Partial<FormField> que é mesclado nas props do campo.

externalData — Dados de contexto externo

Use externalData para disponibilizar dados externos ao formulário — para interpolação de template vars, condicionais source: 'evento' e resolvers.

<FormRenderer
schema={schema}
externalData={{
"evento.nome": "Acampamento Verão 2026",
"evento.dataInicioEvento": "2026-01-15",
"evento.valor": 15000,
"evento.vagasTotal": 100,
"evento.permiteMenores": true,
}}
onComplete={...}
/>

No schema, referencie com {{evento.nome}} em labels/hints/títulos ou com source: "evento" em condicionais.

fieldErrors — Erros do servidor

Após um submit com erros 422 do servidor, passe os erros de volta para os campos:

const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});

async function handleComplete(values) {
const res = await fetch("/api/inscricao", {
method: "POST",
body: JSON.stringify(values),
});

if (res.status === 422) {
const { errors } = await res.json();
// { email: "E-mail já cadastrado", cpf: "CPF inválido" }
setFieldErrors(errors);
return;
}
}

<FormRenderer
schema={schema}
fieldErrors={fieldErrors}
onComplete={handleComplete}
/>;