@dontdoit/json-form
v0.0.1
Published
JSON-based form builder for React
Maintainers
Readme
@kimdontdoit/json-form
Turn a Zod schema into an editable form. Change the schema, the form changes with it — no hand-written field JSX.
Built for developers who want a small, non-magic, schema-driven building block (the audience of tools like json-render.dev), not a heavyweight form framework: it's a fully controlled component, so you own the state.
The UI isn't a stack of bordered inputs — it renders as one continuous monospace, object-literal-like block, where each value is an inline editable space blended into the surrounding punctuation. Closer to an always-editable code/JSON editor than a typical form.
Install
npm install @kimdontdoit/json-form zodreact, react-dom, and zod are peer dependencies — nothing else is bundled.
Usage
import { useState } from "react";
import { z } from "zod";
import { JsonForm, getDefaults } from "@kimdontdoit/json-form";
import "@kimdontdoit/json-form/json-form.css";
const schema = z.object({
name: z.string(),
age: z.number().optional(),
role: z.enum(["admin", "member"]),
});
function Example() {
const [value, setValue] = useState(() => getDefaults(schema));
return <JsonForm schema={schema} value={value} onChange={setValue} />;
}API
<JsonForm schema value onChange fields? className? />— the form.value/onChangemake it fully controlled;fieldsis an escape hatch (see below).getDefaults(schema)— builds an initial value from a schema, for seedinguseState.walkSchema(schema)— the raw schema →FieldNodeconversion, exported for advanced/debugging use.
Overriding a field's rendering
<JsonForm
schema={schema}
value={value}
onChange={setValue}
fields={[
{ match: "role", component: MyRoleDropdown },
{ match: (node) => node.kind === "enum", component: MyEnumControl },
]}
/>match is either the field's dot-joined path ("address.zip") or a predicate over the FieldNode. Overrides are checked in order; the first match wins.
Styling
Import @kimdontdoit/json-form/json-form.css and override its custom properties (--jf-font, --jf-accent, --jf-error, --jf-muted) to theme it — no Tailwind or design-system dependency.
Known limitations / v2 candidates
- No
z.union/z.discriminatedUnion/z.intersection—walkSchemathrows a clear error naming the unsupported type and path. - No refinement/transform-driven field-type branching (validation still runs via
safeParse; the rendered field type is the pre-refinement shape). - No self-referential/recursive schemas.
- Arrays support add/remove only — no drag-to-reorder.
- No
z.record/z.map/z.tuple. - The override system is a flat, in-order array — no scoped/contextual registry.
- No i18n — object keys are shown as-is.
License
MIT
