UI Adapters
A UIAdapter maps field types to React components and controls how labels, errors, and descriptions are rendered. It is completely independent of state management.
Built-in UI Adapters
| Export | Library | Description |
|---|---|---|
RavenShadcnUIAdapter | ShadCN / Radix | Tailwind-based accessible components |
RavenAntDUIAdapter | Ant Design | Ant Design 5.x components |
UIAdapter Interface
interface UIAdapter {
/** Field type → component mapping */
components: Partial<Record<FieldType | string, ComponentType<UIFieldProps>>>;
/** Optional wrapper that renders label + error + description */
FormItem?: ComponentType<UIFormItemProps>;
/**
* Field types that render inline (bypassing FormItem wrapping).
* Defaults to ['checkbox', 'switch'].
*/
inlineTypes?: Array<FieldType | string>;
/** Fallback component for unmapped field types */
fallback?: ComponentType<UIFieldProps>;
}
UIFieldProps
interface UIFieldProps {
value: unknown;
onChange: (value: unknown) => void;
onBlur: () => void;
placeholder?: string;
disabled?: boolean;
error?: string;
options?: Array<{ label: string; value: string }>;
min?: number;
max?: number;
// ...plus any extra field-level props passed through schema
}
UIFormItemProps
interface UIFormItemProps {
label?: string;
error?: string;
description?: string;
required?: boolean;
children: React.ReactNode;
}
Creating a Custom UIAdapter
Use createUIAdapter to register your own components:
import {
createUIAdapter,
type UIFieldProps,
type UIFormItemProps,
} from "raven-form-engine";
// Your custom input component
function MyInput({ value, onChange, onBlur, placeholder, error }: UIFieldProps) {
return (
<input
value={String(value ?? "")}
placeholder={placeholder}
className={error ? "error" : ""}
onChange={(e) => onChange(e.target.value)}
onBlur={onBlur}
/>
);
}
// Your form item wrapper
function MyFormItem({
label,
error,
description,
required,
children,
}: UIFormItemProps) {
return (
<div className="form-item">
{label && (
<label>
{label}
{required && " *"}
</label>
)}
{description && <small>{description}</small>}
{children}
{error && <p className="error">{error}</p>}
</div>
);
}
// Register the adapter
export const MyUIAdapter = createUIAdapter({
components: {
text: MyInput,
email: MyInput,
password: MyInput,
// ... add more field types
},
FormItem: MyFormItem,
inlineTypes: ["checkbox", "switch"],
});
Swapping UI per Form
You can override the global UI adapter on a single form without touching the form adapter:
<RavenForm
schema={schema}
onSubmit={handleSubmit}
ui={MyUIAdapter} // only this form uses a different UI
/>
Adapter Validation Utilities
import { validateUIAdapter } from "raven-form-engine";
// In development, logs warnings for missing required fields
validateUIAdapter(myUIAdapter);