Skip to main content

Integration with UI Libraries

El Form doesn't care what your inputs look like — it just needs each input wired to register (or to value + onChange). That makes it easy to drop in components from Material-UI, Ant Design, shadcn/ui, or your own design system.

The general pattern: build a small wrapper field that reads register and formState from useFormContext, then renders the library's component.

shadcn/ui

shadcn inputs accept standard input props, so you can spread register directly:

import { useFormContext } from "el-form-react-hooks";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

function Field({ name, label, ...props }) {
const { register, formState } = useFormContext();
return (
<div className="space-y-1">
<Label htmlFor={name}>{label}</Label>
<Input id={name} {...register(name)} {...props} />
{formState.errors[name] && (
<p className="text-sm text-red-500">{formState.errors[name]}</p>
)}
</div>
);
}

Material-UI (MUI)

MUI's TextField is controlled, so wire value and onChange from register and surface the error through MUI's own error / helperText props:

import { useFormContext } from "el-form-react-hooks";
import TextField from "@mui/material/TextField";

function MuiField({ name, label }) {
const { register, formState } = useFormContext();
const error = formState.errors[name];
return (
<TextField
label={label}
{...register(name)}
error={Boolean(error)}
helperText={error ?? ""}
fullWidth
/>
);
}

Ant Design

Ant Design's Input is also controlled — the same spread works, with Form.Item for layout and error text:

import { useFormContext } from "el-form-react-hooks";
import { Input, Form } from "antd";

function AntField({ name, label }) {
const { register, formState } = useFormContext();
const error = formState.errors[name];
return (
<Form.Item
label={label}
validateStatus={error ? "error" : ""}
help={error ?? ""}
>
<Input {...register(name)} />
</Form.Item>
);
}

Components that don't expose a native event

Some library inputs (custom selects, date pickers) give you a value via a callback instead of a DOM event. For those, drive the field with setValue and watch instead of register:

import { useFormContext } from "el-form-react-hooks";
import { DatePicker } from "some-ui-lib";

function DateField({ name, label }) {
const { watch, setValue, formState } = useFormContext();
return (
<div>
<label>{label}</label>
<DatePicker
value={watch(name)}
onChange={(date) => setValue(name, date)}
/>
{formState.errors[name] && <span>{formState.errors[name]}</span>}
</div>
);
}

Using these inside AutoForm

Any of these wrappers can be reused as an AutoForm field via component or componentMap — just adapt the wrapper to the AutoFormFieldProps shape (value, onChange, onBlur, error, touched).

Next steps