Material UI
Map Material UI inputs to Formhaus field types through the components prop of FormRenderer.
Install
bash
npm install @formhaus/core @formhaus/react @mui/material @emotion/react @emotion/styledComponent map
ts
import type { FieldComponentMap } from '@formhaus/react';
import { AutocompleteField } from './fields/AutocompleteField';
import { CheckboxField } from './fields/CheckboxField';
import { FileField } from './fields/FileField';
import { RadioField } from './fields/RadioField';
import { SelectField } from './fields/SelectField';
import { SwitchField } from './fields/SwitchField';
import { TextField } from './fields/TextField';
import { TextareaField } from './fields/TextareaField';
export const components: FieldComponentMap = {
text: TextField,
email: TextField,
phone: TextField,
number: TextField,
password: TextField,
date: TextField,
datetime: TextField,
select: SelectField,
autocomplete: AutocompleteField,
multiselect: SelectField,
checkbox: CheckboxField,
radio: RadioField,
switch: SwitchField,
file: FileField,
textarea: TextareaField,
};Text field
One renderer covers text, email, phone, number, password, date and datetime fields:
tsx
import type { FieldComponentProps } from '@formhaus/react';
import MuiTextField from '@mui/material/TextField';
export function TextField({
field,
value,
error,
loading,
disabled,
onChange,
}: FieldComponentProps) {
const inputType =
field.type === 'email' ? 'email'
: field.type === 'password' ? 'password'
: field.type === 'number' ? 'number'
: field.type === 'date' ? 'date'
: field.type === 'datetime' ? 'datetime-local'
: field.type === 'phone' ? 'tel'
: 'text';
const shrinkLabel = field.type === 'date' || field.type === 'datetime';
return (
<MuiTextField
id={field.key}
label={field.label}
type={inputType}
value={value != null ? String(value) : ''}
placeholder={field.placeholder}
onChange={(e) => onChange(field.type === 'number' && e.target.value !== '' ? Number(e.target.value) : e.target.value)}
error={!!error}
helperText={error || field.helperText}
disabled={disabled || loading}
fullWidth
margin="normal"
slotProps={{
inputLabel: shrinkLabel ? { shrink: true } : undefined,
}}
/>
);
}App
tsx
import { FormRenderer } from '@formhaus/react';
import { Container, CssBaseline, ThemeProvider, createTheme } from '@mui/material';
import { FormActions } from './actions/FormActions';
import { StepProgress } from './actions/StepProgress';
import { components } from './component-map';
import definition from './definition.json';
const theme = createTheme();
export default function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Container maxWidth="sm" sx={{ py: 4 }}>
<FormRenderer
definition={definition}
components={components}
ActionsComponent={FormActions}
ProgressComponent={StepProgress}
onSubmit={(values) => console.log('Submitted:', values)}
/>
</Container>
</ThemeProvider>
);
}The other renderers are in src/fields and src/actions.
Server re-validation
The example validates in the browser only. To check the same rules on the server, build a FormEngine from the same definition and call validate(), then pass the returned errors to the errors prop. See Next.js for a complete route handler.