Dynamic form
In this example, we render a dynamic form with the fields coming from the backend.
type FieldType = 'string' | 'email' | 'int'
type Field = { name: string; type: FieldType }
const getFields = () => {
const fields: Field[] = [
{ name: 'firstName', type: 'string' },
{ name: 'email', type: 'email' },
{ name: 'age', type: 'int' },
]
return fields
}
const typeSchemas = {
string: z.string(),
email: z.string().email(),
int: z.number().int(),
}
const fieldSchema = (type: FieldType) => typeSchemas[type]
const fieldsSchema = (fields: Field[]) =>
z.object(
fields.reduce(
(obj, field) => ({ ...obj, [field.name]: fieldSchema(field.type) }),
{},
),
)
const mutation = applySchema(fieldsSchema(getFields()))(
async (values) => values,
)
export function loader() {
return { fields: getFields() }
}
export const action = async ({ request }: Route.ActionArgs) =>
formAction({ request, schema: fieldsSchema(getFields()), mutation })
export default ({ loaderData }: Route.ComponentProps) => {
return <SchemaForm schema={fieldsSchema(loaderData.fields)} />
}