Integrations
Custom Mapper
Build your own FormWireMapper for any UI library
The FormWireMapper interface is small and well-defined. You can implement it for any React component library.
Minimal Mapper
Only the string field is required:
import type { FormWireMapper, StringFieldProps } from "@form-wire/react";
const myMapper: FormWireMapper = {
string: ({ name, type, placeholder, required, disabled, className }: StringFieldProps) => (
<input
name={name}
type={type}
placeholder={placeholder}
required={required}
disabled={disabled}
className={className}
/>
),
};Full Mapper
Implement every key for complete coverage:
import type {
FormWireMapper,
StringFieldProps,
BooleanFieldProps,
SelectFieldProps,
FileFieldProps,
FieldWrapperProps,
GroupProps,
SubmitProps,
StatusProps,
MessageProps,
} from "@form-wire/react";
const myMapper: FormWireMapper = {
string: (props: StringFieldProps) => { /* render text input */ },
boolean: (props: BooleanFieldProps) => { /* render checkbox */ },
select: (props: SelectFieldProps) => { /* render select */ },
file: (props: FileFieldProps) => { /* render file input */ },
field: (props: FieldWrapperProps) => {
// Wraps each field: label, description, input, error
return (
<div>
<label htmlFor={props.controlId}>
{props.field.label}
{props.required ? " *" : ""}
</label>
{props.field.description && <p>{props.field.description}</p>}
{props.input}
{props.error && <span role="alert">{props.error}</span>}
</div>
);
},
group: (props: GroupProps) => { /* render fieldset */ },
submit: (props: SubmitProps) => {
const label = props.pending ? "Saving..." : "Submit";
return <button type="submit" disabled={props.disabled}>{label}</button>;
},
status: (props: StatusProps) => { /* render loading state */ },
message: (props: MessageProps) => { /* render success/error */ },
};Prop Types Overview
StringFieldProps
| Prop | Type | Description |
|---|---|---|
field | FormField | Field metadata |
id | string | DOM id |
name | string | Input name |
type | "text" | "email" | "number" | "date" | Input type |
defaultValue | string | Uncontrolled default |
value | string | Controlled value |
onChange | ChangeEventHandler | Change handler |
required | boolean | Required flag |
disabled | boolean | Disabled flag |
multiline | boolean | Use textarea |
invalid | boolean | Has validation error |
describedBy | string | aria-describedby |
className | string | CSS class |
FieldWrapperProps
| Prop | Type | Description |
|---|---|---|
field | FormField | Field metadata |
controlId | string | HTML id of the input |
descriptionId | string | HTML id of the description |
errorId | string | HTML id of the error |
invalid | boolean | Has validation error |
required | boolean | Required flag |
input | ReactNode | The rendered input control |
error | string | Error message text |
className | string | CSS class |
SubmitProps
| Prop | Type | Description |
|---|---|---|
pending | boolean | Form is submitting |
disabled | boolean | Should be disabled |
validating | boolean | Async validation running |
draftLoading | boolean | Draft is loading |
Accessibility
Each field receives proper ARIA attributes:
aria-describedbypointing to description and error elementsaria-invalidwhen the field has errorsaria-labelfrom field config- Labels use
htmlForlinked to the input'sid - Errors use
role="alert"
Your mapper should forward these attributes to preserve accessibility.