React-declarative gives you two complementary ways to customize what renders inside a form. FieldType.Component lets you drop any React component into a specific slot in your schema — a one-off injection with full access to the current form data. OneSlotFactory lets you replace a built-in field renderer globally, so every FieldType.Text (or any other type) across your entire application uses your custom component instead.
FieldType.ComponentAdd a FieldType.Component entry to your fields array and provide an element function that returns a React component. The component receives a ComponentFieldInstance object as its props.
import { FieldType, TypedField } from 'react-declarative';
const fields: TypedField[] = [
{
type: FieldType.Paper,
fields: [
{
type: FieldType.Component,
element: (props) => <Logger {...props} />,
},
],
},
];
elementThe component you supply receives the full field instance props. The most commonly used ones are:
| Prop | Type | Description |
|---|---|---|
data |
Data |
The current form data object |
payload |
Payload |
The external payload passed to <One /> |
onChange |
(data: Partial<Data>) => void |
Merge partial data back into the form |
name |
string |
The name property from the field schema |
invalid |
string | null |
Current validation error message |
disabled |
boolean |
Whether the field is disabled |
readonly |
boolean |
Whether the field is read-only |
interface IStatusBadgeProps {
data: { status: string };
payload: { theme: 'light' | 'dark' };
}
const StatusBadge = ({ data, payload }: IStatusBadgeProps) => (
<span
style={{
color: data.status === 'active' ? 'green' : 'red',
background: payload.theme === 'dark' ? '#333' : '#eee',
}}
>
{data.status}
</span>
);
// In your schema:
{
type: FieldType.Component,
element: (props) => <StatusBadge {...props} />,
}
Note: The
elementfunction is called on every render. Keep it a thin wrapper — put your real logic inside the component you reference, not insideelementitself.
Call onChange with a partial data object to update specific fields:
const PrefillButton = ({ onChange }: { onChange: (d: any) => void }) => (
<button
onClick={() =>
onChange({ firstName: 'Jane', lastName: 'Smith' })
}
>
Prefill demo data
</button>
);
{
type: FieldType.Component,
element: (props) => <PrefillButton onChange={props.onChange} />,
}
OneSlotFactoryOneSlotFactory is a React context provider that accepts replacement components for any built-in field type. Wrap your <One /> component (or your entire application) in <OneSlotFactory> to apply overrides everywhere inside it.
import { OneSlotFactory, One } from 'react-declarative';
import MyInput from './MyInput';
import MyCheckBox from './MyCheckBox';
const App = () => (
<OneSlotFactory
Text={MyInput}
CheckBox={MyCheckBox}
>
<One fields={fields} handler={handler} />
</OneSlotFactory>
);
Every FieldType.Text and FieldType.Checkbox field inside the provider now uses your components.
The following slot names correspond to FieldType values you can override:
Input slots
Text, Date, Time, Slider, Rating, Progress
Selection slots
CheckBox, Switch, YesNo, Radio, Combo, Items, Complete, Tree, Dict, Choose
Action slots
Button, Icon
Layout and display slots
Line, Typography, File
Each slot component receives a typed props interface. For example, a custom Text component receives ITextSlot, a custom CheckBox component receives ICheckBoxSlot. These interfaces are exported from react-declarative:
import { ITextSlot } from 'react-declarative';
const MyInput = ({ value, onChange, disabled, readonly, title, description, invalid }: ITextSlot) => (
<div className="my-input-wrapper">
<label>{title}</label>
<input
value={value ?? ''}
disabled={disabled}
readOnly={readonly}
onChange={(e) => onChange(e.target.value)}
aria-invalid={!!invalid}
/>
{invalid && <span className="error">{invalid}</span>}
{description && <small>{description}</small>}
</div>
);
Component vs OneSlotFactoryUse FieldType.Component when...
onChangeUse OneSlotFactory when...
react-declarative-mantineIf you are using the Mantine component library, react-declarative-mantine provides a complete OneSlotFactory with all field types redesigned to match Mantine's aesthetic — without requiring any changes to your JSON schemas.
npm install react-declarative-mantine
import { MantineSlotFactory } from 'react-declarative-mantine';
import { One } from 'react-declarative';
const App = () => (
<MantineSlotFactory>
<One fields={fields} handler={handler} />
</MantineSlotFactory>
);
Your existing field schemas remain unchanged. The library swaps in Mantine-styled renderers for every field type automatically.
Tip: You can preview how the Mantine theme looks without installing anything at the Mantine theme playground.