---
url: https://formhaus.dev/guide/integrations/mui.md
description: >-
  Render Formhaus JSON forms with Material UI: map TextField, Select,
  Autocomplete, Checkbox and other MUI inputs to field types through the
  components prop.
---

# Material UI

Map Material UI inputs to Formhaus field types through the `components` prop of `FormRenderer`.

[Open in StackBlitz](https://stackblitz.com/github/ignsm/formhaus/tree/main/examples/react-mui) · [Source](https://github.com/ignsm/formhaus/tree/main/examples/react-mui)

## Install

```bash
npm install @formhaus/core @formhaus/react @mui/material @emotion/react @emotion/styled
```

## Component 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`](https://github.com/ignsm/formhaus/tree/main/examples/react-mui/src/fields) and [`src/actions`](https://github.com/ignsm/formhaus/tree/main/examples/react-mui/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](/guide/integrations/nextjs#how-server-re-validation-works) for a complete route handler.
