Quick Start
Build a working form in minutes with Forma React.
Overview
Forma React is a headless form library. You provide the UI components, and Forma handles:
- Form state management
- Field visibility and validation
- Computed values
- Multi-page navigation
Step 1: Define Your Components
Create components for each field type you need. Components receive a field prop with all state and handlers:
import type {
ComponentMap,
TextComponentProps,
BooleanComponentProps,
} from "@fogpipe/forma-react";
// Text input component
const TextInput = ({ field }: TextComponentProps) => (
<div className="field">
<label htmlFor={field.name}>{field.label}</label>
<input
id={field.name}
type="text"
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
onBlur={field.onBlur}
placeholder={field.placeholder}
disabled={!field.enabled}
required={field.required}
/>
{field.description && <p className="help">{field.description}</p>}
{field.errors.map((error, i) => (
<p key={i} className="error">
{error.message}
</p>
))}
</div>
);
// Checkbox component
const Checkbox = ({ field }: BooleanComponentProps) => (
<div className="field">
<label>
<input
type="checkbox"
checked={field.value || false}
onChange={(e) => field.onChange(e.target.checked)}
disabled={!field.enabled}
/>
{field.label}
</label>
</div>
);
// Component map - maps field types to components
const components: ComponentMap = {
text: TextInput,
email: TextInput,
boolean: Checkbox,
};
Step 2: Create a Forma Specification
Define your form structure:
import type { Forma } from "@fogpipe/forma-core";
const contactForm: Forma = {
version: "1.0",
meta: {
id: "contact-form",
title: "Contact Us",
},
schema: {
type: "object",
properties: {
name: { type: "string" },
email: { type: "string", format: "email" },
subscribe: { type: "boolean" },
},
required: ["name", "email"],
},
fields: {
name: { type: "text", label: "Your Name", placeholder: "Enter your name" },
email: {
type: "email",
label: "Email Address",
placeholder: "you@example.com",
},
subscribe: { type: "boolean", label: "Subscribe to newsletter" },
},
fieldOrder: ["name", "email", "subscribe"],
};
Step 3: Render the Form
Use FormRenderer to display the form:
import { FormRenderer } from "@fogpipe/forma-react";
function ContactPage() {
const handleSubmit = (data: Record<string, unknown>) => {
console.log("Form submitted:", data);
// Send to your API
};
return (
<div>
<h1>Contact Us</h1>
<FormRenderer
spec={contactForm}
components={components}
onSubmit={handleSubmit}
/>
</div>
);
}
Complete Example
import { FormRenderer } from "@fogpipe/forma-react";
import type { Forma } from "@fogpipe/forma-core";
import type {
ComponentMap,
TextComponentProps,
BooleanComponentProps,
} from "@fogpipe/forma-react";
// Components
const TextInput = ({ field }: TextComponentProps) => (
<div style={{ marginBottom: "1rem" }}>
<label style={{ display: "block", marginBottom: "0.25rem" }}>
{field.label}
{field.required && <span style={{ color: "red" }}> *</span>}
</label>
<input
type={field.type === "email" ? "email" : "text"}
value={field.value || ""}
onChange={(e) => field.onChange(e.target.value)}
onBlur={field.onBlur}
placeholder={field.placeholder}
style={{ width: "100%", padding: "0.5rem" }}
/>
{field.errors.map((error, i) => (
<p key={i} style={{ color: "red", margin: "0.25rem 0" }}>
{error.message}
</p>
))}
</div>
);
const Checkbox = ({ field }: BooleanComponentProps) => (
<div style={{ marginBottom: "1rem" }}>
<label>
<input
type="checkbox"
checked={field.value || false}
onChange={(e) => field.onChange(e.target.checked)}
/>{" "}
{field.label}
</label>
</div>
);
const components: ComponentMap = {
text: TextInput,
email: TextInput,
boolean: Checkbox,
};
// Form specification
const contactForm: Forma = {
version: "1.0",
meta: { id: "contact", title: "Contact Form" },
schema: {
type: "object",
properties: {
name: { type: "string" },
email: { type: "string", format: "email" },
subscribe: { type: "boolean" },
},
required: ["name", "email"],
},
fields: {
name: { type: "text", label: "Name", placeholder: "Your name" },
email: { type: "email", label: "Email", placeholder: "you@example.com" },
subscribe: { type: "boolean", label: "Subscribe to updates" },
},
fieldOrder: ["name", "email", "subscribe"],
};
// App
function App() {
return (
<FormRenderer
spec={contactForm}
components={components}
onSubmit={(data) => alert(JSON.stringify(data, null, 2))}
/>
);
}
Next Steps
- Component Mapping - Build more field types
- Display Fields - Add read-only display content (headings, metrics, alerts)
- useForma Hook - Custom form implementations
- Wizard Forms - Multi-page forms
- Advanced Features - Adorners, readonly, variants, options visibility