Custom field types
Field types are plugins, not a fixed list. Register your own and use its name as type in any definition.
1. Write the plugin
import { z } from "zod";
import type { FieldRendererProps, ReactFieldPlugin } from "@hardikrastogi/react";
function SliderInput({ props, value, onChange, onBlur, inputId, describedBy, invalid }: FieldRendererProps) {
const min = typeof props.min === "number" ? props.min : 0;
const max = typeof props.max === "number" ? props.max : 10;
const current = typeof value === "number" ? value : min;
return (
<input
id={inputId}
type="range"
min={min}
max={max}
value={current}
aria-invalid={invalid || undefined}
aria-describedby={describedBy}
onChange={(e) => onChange(Number(e.target.value))}
onBlur={onBlur}
/>
);
}
export const sliderPlugin: ReactFieldPlugin = {
type: "slider",
schema: z.number(),
defaultProps: { min: 0, max: 10 },
emptyValue: 0,
labelKind: "control",
Renderer: SliderInput,
};2. Register it
import { FormRenderer, createDefaultRegistry } from "@hardikrastogi/react";
// Create once, outside your component
const registry = createDefaultRegistry();
registry.register(sliderPlugin);
<FormRenderer definition={definition} registry={registry} />3. Use it in JSON
{ "id": "satisfaction", "type": "slider", "label": "How likely are you to recommend us?", "defaultProps": { "min": 0, "max": 10 } }The playground registers exactly this slider field. Pick the "Custom field" example to see it. (rating, country, currency, url and time all started this way and are now built in — see field types.)
The plugin object
| Key | Purpose |
|---|---|
type | The name used in definitions. |
schema | A Zod schema describing a valid answer. |
defaultProps | Defaults for the field's own defaultProps; the field's values win. |
Renderer | The React component that draws the input. |
labelKind | control (label tied to one input, the default), group (label names a group of controls) or inline (your component draws its own label). |
emptyValue | Initial answer when nothing else is set. |
validate | Optional. Receives the value and merged props, returns an object with a boolean valid and an errors array. Runs after the built-in rules, only for non-empty answers. |
What Renderer receives
| Prop | Meaning |
|---|---|
value, onChange, onBlur | The current answer and how to change it. |
field | The field's config (id, label, required, and so on). |
props | Plugin defaults merged with the field's defaultProps. |
inputId, labelId, describedBy | Ids to wire up the label, description and error for accessibility. |
invalid, disabled | Current state. |
classNames | The consumer's class name overrides. |
Give the element that owns the invalid state aria-invalid. The form uses it to focus the first problem after a failed submit.