Overview#
Each example is self-contained and builds on the previous one. They all share the same base setup — a Form instance with four field types and a simple renderRoot.
#Shared setup
The code below is the foundation for every example on this page. Field components follow the same contract: receive everything via useField, render UI, pass values back via onChange.
form.setup.tsx
InputField
TextAreaField
SelectField
CheckboxField
import { setupForm, defineMapping } from "react-headless-form";
import InputField from "./fields/InputField";
import CheckboxField from "./fields/CheckboxField";
import TextAreaField from "./fields/TextAreaField";
import SelectField from "./fields/SelectField";
export const [Form, defineConfig, Section] = setupForm({
renderRoot: ({ children, onSubmit }) => (
<div className="blueform-example">
<form onSubmit={onSubmit}>{children}</form>
</div>
),
fieldMapping: defineMapping({
text: InputField,
longText: TextAreaField,
checkbox: CheckboxField,
select: SelectField,
}),
});
export default Form;
import { useField } from "react-headless-form";
import { FunctionComponent } from "react";
interface InputFieldProps extends React.InputHTMLAttributes<HTMLInputElement> {}
const InputField: FunctionComponent<InputFieldProps> = (props) => {
const {
id,
name,
value,
onChange,
errorMessage,
label,
description,
required,
disabled,
readOnly,
ref,
} = useField();
return (
<div id={id}>
<span style={{ marginRight: 10 }}>
{label} {required && "*"}
</span>
<input
{...props}
ref={ref}
name={name}
value={value}
onChange={onChange}
disabled={disabled}
readOnly={readOnly}
placeholder={props.placeholder ?? label}
aria-required={required}
aria-invalid={Boolean(errorMessage)}
/>
{description && <div className="fieldDescription">{description}</div>}
{errorMessage && <div className="fieldError">{errorMessage}</div>}
</div>
);
};
export default InputField;
import { useField } from "react-headless-form";
import { FunctionComponent } from "react";
interface TextAreaFieldProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
const TextAreaField: FunctionComponent<TextAreaFieldProps> = (props) => {
const {
id,
value = "",
onChange,
errorMessage,
label,
description,
required,
disabled,
readOnly,
ref,
} = useField();
return (
<div id={id}>
<span style={{ marginRight: 10 }}>{label}</span>
<textarea
{...props}
ref={ref}
value={value}
onChange={onChange}
disabled={disabled}
readOnly={readOnly}
placeholder={props.placeholder ?? label}
aria-required={required}
aria-invalid={Boolean(errorMessage)}
/>
{description && <div className="fieldDescription">{description}</div>}
{errorMessage && <div className="fieldError">{errorMessage}</div>}
</div>
);
};
export default TextAreaField;
import { useField } from "react-headless-form";
import { FunctionComponent } from "react";
interface SelectFieldProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
options: { label: string; value: string }[];
}
const SelectField: FunctionComponent<SelectFieldProps> = ({
options,
...props
}) => {
const { id, value, onChange, label, disabled, ref } = useField();
return (
<div id={id}>
<span>{label}</span>
<select
{...props}
ref={ref}
value={value ?? ""}
onChange={(e) => onChange?.(e.target.value)}
disabled={disabled}
>
{options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
);
};
export default SelectField;
import { useField } from "react-headless-form";
import { FunctionComponent } from "react";
interface CheckboxFieldProps extends React.InputHTMLAttributes<HTMLInputElement> {}
const CheckboxField: FunctionComponent<CheckboxFieldProps> = (props) => {
const {
id,
value,
onChange,
label,
description,
errorMessage,
disabled,
readOnly,
ref,
} = useField();
return (
<div id={id}>
<label>
<input
ref={ref}
type="checkbox"
checked={Boolean(value)}
onChange={(e) => onChange?.(e.target.checked)}
disabled={disabled}
readOnly={readOnly}
{...props}
/>
{label}
</label>
{description && <div className="fieldDescription">{description}</div>}
{errorMessage && <div className="fieldError">{errorMessage}</div>}
</div>
);
};
export default CheckboxField;
#Examples
| # | Example | What it covers |
|---|---|---|
| 1 | Login Form | Flat model, basic validation, props passthrough |
| 2 | Registration Form | Multiple field types, cross-field validation, disabled as function |
| 3 | Nested Form | section + nested: true, defineConfig, reusable config fragments |
| 4 | Array Fields | array type, useArrayField, add / remove / duplicate |
| 5 | Conditional Fields | visible as function, conditional validation with validate |
| 6 | Computed Fields | onFieldChange + setValue for derived values — slug, BMI, full name |
| 7 | Multi-step Wizard | section composition, shared RHF state across steps, Section component |
Note
The forms in these examples are styled via shared CSS applied to the field components and wrapper. BlueForm itself is fully headless — it ships no styles, no UI primitives, and has no opinion on how your fields look.

