Form Wire
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

PropTypeDescription
fieldFormFieldField metadata
idstringDOM id
namestringInput name
type"text" | "email" | "number" | "date"Input type
defaultValuestringUncontrolled default
valuestringControlled value
onChangeChangeEventHandlerChange handler
requiredbooleanRequired flag
disabledbooleanDisabled flag
multilinebooleanUse textarea
invalidbooleanHas validation error
describedBystringaria-describedby
classNamestringCSS class

FieldWrapperProps

PropTypeDescription
fieldFormFieldField metadata
controlIdstringHTML id of the input
descriptionIdstringHTML id of the description
errorIdstringHTML id of the error
invalidbooleanHas validation error
requiredbooleanRequired flag
inputReactNodeThe rendered input control
errorstringError message text
classNamestringCSS class

SubmitProps

PropTypeDescription
pendingbooleanForm is submitting
disabledbooleanShould be disabled
validatingbooleanAsync validation running
draftLoadingbooleanDraft is loading

Accessibility

Each field receives proper ARIA attributes:

  • aria-describedby pointing to description and error elements
  • aria-invalid when the field has errors
  • aria-label from field config
  • Labels use htmlFor linked to the input's id
  • Errors use role="alert"

Your mapper should forward these attributes to preserve accessibility.

On this page