KiForms - Build dynamic React forms from JSON — with zero setup.
See the codeBuild dynamic React forms from JSON — with zero setup.
Stop wiring forms manually. Define them as data.
Interactive playground — every feature (conditionals, themes, conversational mode, the live schema editor) running the real library. No install needed.
Schema Studio — design forms visually: drag-and-drop fields, edit properties with live preview, then copy the schema JSON or a ready-to-paste React component.
import { KiForm } from "ki-forms"
import "ki-forms/styles.css"
export default function App() {
return (
<KiForm
fields={[
"email",
"password",
{
name: "role",
options: ["User", "Admin"]
},
{
name: "company",
showIf: { field: "role", equals: "Admin" }
}
]}
onSubmit={(data) => console.log(data)}
/>
)
}
Building forms in React is repetitive and inefficient:
Even popular libraries like React Hook Form require setup and mental overhead.
ki-forms lets you build forms using simple JSON.
Just describe your form → it renders automatically.
npm install ki-forms
import { KiForm } from "ki-forms"
import "ki-forms/styles.css"
<KiForm
fields={["email", "password"]}
onSubmit={(data) => console.log(data)}
/>
{
name: "email",
type: "email", // "text" | "textarea" | "email" | "password" | "number" | "select" | "checkbox" | "tel" | "url" | "date"
required: true,
label: "Email",
placeholder: "Enter your email",
helperText: "We never share your email"
}
Show fields dynamically based on other values:
{
name: "company",
showIf: {
field: "role",
equals: "Admin"
}
}
{
name: "taxId",
required: true,
showIf: {
all: [
{ field: "country", equals: "US" },
{ field: "plan", equals: "Pro" }
]
}
}
Use all (every condition must match) or any (at least one). They combine
with a top-level field condition via AND. Hidden fields skip validation.
ki-forms automatically:
email, password)firstName → First Name)Enter your email)fields={["email", "password"]}
Run logic when field value changes:
{
name: "role",
options: ["User", "Admin"],
onChange: (value, values) => {
console.log("Selected:", value)
console.log("All values:", values)
}
}
Default styles included:
import "ki-forms/styles.css"
Override using className:
{
name: "email",
className: "my-custom-input"
}
Use the form hook directly:
import { useKiForm, KiForm } from "ki-forms"
const form = useKiForm({
fields: ["email", "password"]
})
<KiForm form={form} />
text, email, password, number, tel, url, date, textarea, select, and checkbox.
Replace or extend built-in renderers with the components prop. A custom component receives the field definition, current value, validation error, and an onChange callback:
function RatingField({ field, value, onChange, error }) {
return (
<div>
<input
type="range"
min="1"
max="5"
value={value ?? 1}
onChange={(event) => onChange(Number(event.target.value))}
/>
{error && <p>{error}</p>}
</div>
)
}
<KiForm
fields={[{ name: "rating", type: "text" }]}
components={{ text: RatingField }}
/>
For the complete public API, see the exported TypeScript types: Field, KiFormProps, FormApi, KiTheme, and SubmitEndpointResult.
Pass visual tokens — they become --ki-* CSS variables on the form element:
<KiForm
fields={["email", "password"]}
theme={{
accentColor: "#8b5cf6",
radius: "12px",
borderColor: "#334155"
}}
/>
Available tokens: accentColor, borderColor, errorColor, helperColor,
radius, surfaceColor, textColor, fontFamily. Defaults match the
built-in styles, so no theme = zero visual change.
Generate a validation schema from the same field config — using your own zod:
import { KiForm } from "ki-forms"
import { buildZodSchema } from "ki-forms/zod"
import { z } from "zod"
const fields = [
{ name: "email", type: "email", required: true },
{ name: "company", showIf: { field: "role", equals: "Admin" } }
]
<KiForm
fields={fields}
schema={buildZodSchema(fields, {
zod: z,
requiredWhen: { company: { field: "role", equals: "Admin" } }
})}
onSubmit={save}
/>
requiredWhen reuses showIf semantics: the field becomes required exactly
when it would be shown. Works with zod v3 and v4.
One question at a time, Typeform-style — with a progress bar, Back/Next, Enter-to-advance, and automatic jump-back to a failing step:
<KiForm
fields={fields}
variant="conversational"
stepLabels={{ next: "Continue", submit: "Send it" }}
onSubmit={save}
/>
Hidden conditional fields are skipped automatically. Enter inside a textarea inserts a newline instead of advancing.
No backend? Add endpoint and every valid submit is POSTed as JSON — no server
actions, no wiring:
<KiForm
fields={fields}
endpoint="https://script.google.com/macros/s/…/exec"
submitLabel="Sign up"
/>
{ values, meta } — meta carries submittedAt, pageUrl, referrer, userAgent.hideSubmitStatus)./exec URL, and every submission lands as a row. (Apps Script can't answer CORS preflights, so ki-forms automatically sends those endpoints as text/plain — no config needed.)onSubmit still fires as before; onSubmitted(result) reports the request outcome.New props: endpoint, method (default POST), headers, submitLabel, submittingLabel, successLabel, errorLabel, hideSubmitStatus, onSubmitted.
| Feature | ki-forms | React Hook Form |
|---|---|---|
| Setup | Zero | Medium |
| Boilerplate | Low | Medium |
| Dynamic forms | Built-in | Manual |
| Learning curve | Very Low | Medium |
21 commits
TypeScript
96.6%
CSS
3.2%
KiForms - Build dynamic React forms from JSON — with zero setup.
See the codeBuild dynamic React forms from JSON — with zero setup.
Stop wiring forms manually. Define them as data.
Interactive playground — every feature (conditionals, themes, conversational mode, the live schema editor) running the real library. No install needed.
Schema Studio — design forms visually: drag-and-drop fields, edit properties with live preview, then copy the schema JSON or a ready-to-paste React component.
import { KiForm } from "ki-forms"
import "ki-forms/styles.css"
export default function App() {
return (
<KiForm
fields={[
"email",
"password",
{
name: "role",
options: ["User", "Admin"]
},
{
name: "company",
showIf: { field: "role", equals: "Admin" }
}
]}
onSubmit={(data) => console.log(data)}
/>
)
}
Building forms in React is repetitive and inefficient:
Even popular libraries like React Hook Form require setup and mental overhead.
ki-forms lets you build forms using simple JSON.
Just describe your form → it renders automatically.
npm install ki-forms
import { KiForm } from "ki-forms"
import "ki-forms/styles.css"
<KiForm
fields={["email", "password"]}
onSubmit={(data) => console.log(data)}
/>
{
name: "email",
type: "email", // "text" | "textarea" | "email" | "password" | "number" | "select" | "checkbox" | "tel" | "url" | "date"
required: true,
label: "Email",
placeholder: "Enter your email",
helperText: "We never share your email"
}
Show fields dynamically based on other values:
{
name: "company",
showIf: {
field: "role",
equals: "Admin"
}
}
{
name: "taxId",
required: true,
showIf: {
all: [
{ field: "country", equals: "US" },
{ field: "plan", equals: "Pro" }
]
}
}
Use all (every condition must match) or any (at least one). They combine
with a top-level field condition via AND. Hidden fields skip validation.
ki-forms automatically:
email, password)firstName → First Name)Enter your email)fields={["email", "password"]}
Run logic when field value changes:
{
name: "role",
options: ["User", "Admin"],
onChange: (value, values) => {
console.log("Selected:", value)
console.log("All values:", values)
}
}
Default styles included:
import "ki-forms/styles.css"
Override using className:
{
name: "email",
className: "my-custom-input"
}
Use the form hook directly:
import { useKiForm, KiForm } from "ki-forms"
const form = useKiForm({
fields: ["email", "password"]
})
<KiForm form={form} />
text, email, password, number, tel, url, date, textarea, select, and checkbox.
Replace or extend built-in renderers with the components prop. A custom component receives the field definition, current value, validation error, and an onChange callback:
function RatingField({ field, value, onChange, error }) {
return (
<div>
<input
type="range"
min="1"
max="5"
value={value ?? 1}
onChange={(event) => onChange(Number(event.target.value))}
/>
{error && <p>{error}</p>}
</div>
)
}
<KiForm
fields={[{ name: "rating", type: "text" }]}
components={{ text: RatingField }}
/>
For the complete public API, see the exported TypeScript types: Field, KiFormProps, FormApi, KiTheme, and SubmitEndpointResult.
Pass visual tokens — they become --ki-* CSS variables on the form element:
<KiForm
fields={["email", "password"]}
theme={{
accentColor: "#8b5cf6",
radius: "12px",
borderColor: "#334155"
}}
/>
Available tokens: accentColor, borderColor, errorColor, helperColor,
radius, surfaceColor, textColor, fontFamily. Defaults match the
built-in styles, so no theme = zero visual change.
Generate a validation schema from the same field config — using your own zod:
import { KiForm } from "ki-forms"
import { buildZodSchema } from "ki-forms/zod"
import { z } from "zod"
const fields = [
{ name: "email", type: "email", required: true },
{ name: "company", showIf: { field: "role", equals: "Admin" } }
]
<KiForm
fields={fields}
schema={buildZodSchema(fields, {
zod: z,
requiredWhen: { company: { field: "role", equals: "Admin" } }
})}
onSubmit={save}
/>
requiredWhen reuses showIf semantics: the field becomes required exactly
when it would be shown. Works with zod v3 and v4.
One question at a time, Typeform-style — with a progress bar, Back/Next, Enter-to-advance, and automatic jump-back to a failing step:
<KiForm
fields={fields}
variant="conversational"
stepLabels={{ next: "Continue", submit: "Send it" }}
onSubmit={save}
/>
Hidden conditional fields are skipped automatically. Enter inside a textarea inserts a newline instead of advancing.
No backend? Add endpoint and every valid submit is POSTed as JSON — no server
actions, no wiring:
<KiForm
fields={fields}
endpoint="https://script.google.com/macros/s/…/exec"
submitLabel="Sign up"
/>
{ values, meta } — meta carries submittedAt, pageUrl, referrer, userAgent.hideSubmitStatus)./exec URL, and every submission lands as a row. (Apps Script can't answer CORS preflights, so ki-forms automatically sends those endpoints as text/plain — no config needed.)onSubmit still fires as before; onSubmitted(result) reports the request outcome.New props: endpoint, method (default POST), headers, submitLabel, submittingLabel, successLabel, errorLabel, hideSubmitStatus, onSubmitted.
| Feature | ki-forms | React Hook Form |
|---|---|---|
| Setup | Zero | Medium |
| Boilerplate | Low | Medium |
| Dynamic forms | Built-in | Manual |
| Learning curve | Very Low | Medium |
21 commits
TypeScript
96.6%
CSS
3.2%