@fillament/codegen
v0.2.0
Published
Generate a Fillament form from a JSON Schema or Zod schema — the inverse of the agent contract. Produces a FieldsRenderer config you can render or hand-edit, plus an LLM authoring guide for writing Fillament forms.
Maintainers
Readme
@fillament/codegen
Generate a Fillament form from a JSON Schema or Zod schema — and a shipped LLM authoring guide for writing Fillament forms by hand. The inverse of @fillament/agent: describeForm reads a contract out of a form; formFromSchema turns a schema into a form.
pnpm add @fillament/codegenTree-shakeable, side-effect-free. formFromSchema is a pure function that returns plain objects — no React at runtime; it produces a FieldConfig[] you render with <FieldsRenderer> or hand-edit.
formFromSchema(source, options?)
import { z } from "zod";
import { zodAdapter } from "@fillament/zod";
import { formFromSchema } from "@fillament/codegen";
import { useForm, Form, FieldsRenderer } from "@fillament/react";
const Signup = z.object({
email: z.string().email(),
password: z.string().min(8),
accountType: z.enum(["personal", "business"]),
address: z.object({ city: z.string(), zip: z.string() }),
contacts: z.array(z.object({ name: z.string(), email: z.string().email() })),
});
const fields = formFromSchema(zodAdapter(Signup), {
exclude: ["password"],
overrides: { "contacts[].email": { label: "Work email" } },
});
function SignupForm() {
const form = useForm({ schema: zodAdapter(Signup), defaultValues: {/* … */} });
return (
<Form form={form} onSubmit={save}>
<FieldsRenderer fields={fields} />
<button type="submit">Sign up</button>
</Form>
);
}What it generates
| Schema | → FieldConfig |
| --- | --- |
| string | { type: "text" } — or email / date / datetime-local / time / url / tel / password inferred from format or the field name |
| number / integer | { type: "number" } |
| boolean | { type: "checkbox" } |
| enum | { type: "select", options: [...] } |
| nested object | { type: "group", fields: [...] } |
| array of objects | { type: "array", itemFields: [...], addLabel } |
| array of primitives | { type: "array", itemFields: [{ name: "value", ... }] } |
required comes from each schema level's required[]. Labels come from the schema title, else a humanized field name. default becomes defaultValue.
Input
source is either a JSON Schema object or any Fillament validation adapter (zod / yup / json-schema adapters all implement introspect()):
formFromSchema({ type: "object", properties: { /* … */ } }); // raw JSON Schema
formFromSchema(zodAdapter(MySchema)); // Zod
formFromSchema(yupAdapter(MySchema)); // Yup
formFromSchema(jsonSchemaAdapter(mySchema)); // JSON Schema adapterA raw Zod schema (not wrapped in zodAdapter) throws with a hint — wrap it so the existing, well-tested converter is used.
Options
| Option | Description |
| --- | --- |
| exclude | Dot-paths to skip. Array items use a [] segment: ["password", "address.zip", "contacts[].id"]. |
| overrides | Per-field patches merged over the generated config, keyed by dot-path. Set a component (as), options, placeholder, visibleWhen, type: "textarea", etc. |
| labels | (path, schema) => string \| undefined — custom labels; return undefined to fall back. |
| order | Reorder top-level fields by name. |
Generation is a starting point, not a constraint. The output is plain data — read it, diff it, commit it, hand-edit it. Conditional logic (
visibleWhen), design-system components, and copy are exactly whatoverrides(or a manual edit) are for.
fillamentAuthoringGuide
A self-contained instruction guide for an LLM writing Fillament forms — the mental model, useForm/Form/Field, validation adapters, conditional fields, arrays, FieldsRenderer, accessibility, the agent contract, naming conventions, and do/don't rules. Drop it into a system prompt:
import Anthropic from "@anthropic-ai/sdk";
import { fillamentAuthoringGuide } from "@fillament/codegen";
await client.messages.create({
model: "claude-opus-4-8",
system: fillamentAuthoringGuide,
messages: [{ role: "user", content: "Write a checkout form with shipping + billing addresses." }],
});It's a plain string — tree-shaken away if you only import formFromSchema. The prose version lives in docs/llms.md.
License
MIT © headlessButSmart
