# How to Create Custom Variants and Slots for Material-UI Components

> Learn to create custom Material-UI variants and slots using your theme's components configuration and the slots API. Extend MUI components easily.

- Repository: [MUI/material-ui](https://github.com/mui/material-ui)
- Tags: how-to-guide
- Published: 2026-02-26

---

**You can extend Material-UI components by defining custom variants in your theme's components configuration and by replacing internal elements through the slots API using the `components` and `componentsProps` props.**

Material-UI (MUI) provides two powerful extension mechanisms that let you customize component appearance without forking the library. According to the mui/material-ui source code, **custom variants** allow you to add new stylistic "flavors" selectable via the `variant` prop, while **slots** let you swap or augment internal sub-components like root elements or icons. Both systems leverage the `styled` foundation in `packages/mui-material/src/styles` to ensure type safety and theme consistency.

## Understanding Custom Variants in Material-UI

Custom variants let you introduce new visual styles that behave like the built-in `contained`, `outlined`, or `text` variants. The variant system lives in [`packages/mui-material/src/styles/variantUtils.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/styles/variantUtils.js), where the `getVariantStyle` function matches your component's props against theme-defined selectors.

### How Variant Resolution Works

When a component renders, MUI's styling pipeline calls `useThemeProps` → `getThemeProps` → variant handling. The system checks `theme.components[ComponentName].variants` for an entry whose `props` selector matches the current component props. When a match is found, the associated `style` object merges into the final CSS.

In [`packages/mui-material/src/Button/Button.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/Button/Button.js), this logic determines which variant styles to apply based on the `variant` prop value.

### Adding a Custom "Danger" Button Variant

Define the variant in your theme by extending the component's variants array:

```typescript
// theme.ts
import { createTheme } from '@mui/material/styles';

export const theme = createTheme({
  components: {
    MuiButton: {
      variants: [
        {
          props: { variant: 'danger' },
          style: {
            color: '#fff',
            backgroundColor: '#d32f2f',
            '&:hover': {
              backgroundColor: '#b71c1c',
            },
          },
        },
      ],
    },
  },
});

```

Then use it in your JSX:

```tsx
// App.tsx
import { ThemeProvider } from '@mui/material/styles';
import Button from '@mui/material/Button';
import { theme } from './theme';

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Button variant="danger">Delete</Button>
    </ThemeProvider>
  );
}

```

## Working with Component Slots

Slots refer to the internal sub-components that make up a composite component—such as the `root`, `label`, or `input` elements. MUI exposes these through the `components` and `componentsProps` props, allowing you to replace default elements or pass additional props without reimplementing the entire component.

### Slot Architecture in the Source

Each component declares its available slots in a `slots` object. For example, [`packages/mui-material/src/Card/Card.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/Card/Card.js) defines slots like `root` that you can target. The resolution logic uses [`packages/mui-material/src/utils/composeClasses.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/utils/composeClasses.js) (referencing [`unstable_composeClasses.js`](https://github.com/mui/material-ui/blob/main/unstable_composeClasses.js)) to generate class names, while [`packages/mui-material/src/utils/appendOwnerState.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/utils/appendOwnerState.js) merges user-supplied `componentsProps` with the component's owner state before forwarding.

### Replacing the Card Root Slot

Here's how to swap the default `Card` root element with a styled `Paper` component:

```tsx
import Card from '@mui/material/Card';
import Paper from '@mui/material/Paper';
import { styled } from '@mui/material/styles';

const ElevatedPaper = styled(Paper)(({ theme }) => ({
  padding: theme.spacing(2),
  borderRadius: theme.shape.borderRadius,
}));

function CustomCard() {
  return (
    <Card
      components={{ Root: ElevatedPaper }}
      componentsProps={{ root: { elevation: 8 } }}
    >
      Content here
    </Card>
  );
}

```

The `components` prop maps slot names to replacement components, while `componentsProps` forwards props to those slots.

## Combining Custom Variants with Slots

Custom variants and slots are composable. You can define a variant that targets a specific slot's class name (like `& .MuiButton-startIcon`), or you can pass a custom `variant` prop to a slotted component for deeper theming.

### Creating a Rounded TextField with Custom Input Slot

This example applies a custom "rounded" variant to an `OutlinedInput` slot within a `TextField`:

```typescript
// theme.ts
export const theme = createTheme({
  components: {
    MuiOutlinedInput: {
      variants: [
        {
          props: { variant: 'rounded' },
          style: {
            borderRadius: '24px',
            '& .MuiInputBase-input': { padding: '12px 16px' },
          },
        },
      ],
    },
  },
});

```

```tsx
import TextField from '@mui/material/TextField';
import InputBase from '@mui/material/InputBase';
import { ThemeProvider } from '@mui/material/styles';
import { theme } from './theme';

function RoundedField() {
  return (
    <ThemeProvider theme={theme}>
      <TextField
        variant="outlined"
        label="Rounded"
        components={{ Input: InputBase }}
        componentsProps={{ input: { variant: 'rounded' } }}
      />
    </ThemeProvider>
  );
}

```

The [`packages/mui-material/src/TextField/TextField.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/TextField/TextField.js) source demonstrates how the component forwards props to its nested `Input` slot, allowing the custom variant to cascade through the component tree.

## Key Implementation Files in the MUI Source

These files in the mui/material-ui repository contain the core logic for variants and slots:

- **[`packages/mui-material/src/styles/variantUtils.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/styles/variantUtils.js)**: Contains `getVariantStyle`, the utility that matches `props` selectors and merges variant styles into the component's CSS.

- **[`packages/mui-material/src/Button/Button.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/Button/Button.js)**: Demonstrates how the `variant` prop is read and merged with theme-defined variants.

- **[`packages/mui-material/src/Card/Card.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/Card/Card.js)**: Shows the `slots` object declaration and the `components`/`componentsProps` API implementation.

- **[`packages/mui-material/src/utils/composeClasses.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/utils/composeClasses.js)**: Generates class names for each slot, enabling slot-specific styling and composition.

- **[`packages/mui-material/src/utils/appendOwnerState.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/utils/appendOwnerState.js)**: Merges user-supplied `componentsProps` with the owner state before forwarding to slot components.

- **[`packages/mui-material/src/TextField/TextField.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/TextField/TextField.js)**: Illustrates how complex components forward custom variants to nested slots.

## Summary

- **Custom variants** are defined in `theme.components[ComponentName].variants` using a `props` selector and `style` block, processed by [`variantUtils.js`](https://github.com/mui/material-ui/blob/main/variantUtils.js).
- **Slots** expose internal component parts via the `components` and `componentsProps` props, resolved through [`composeClasses.js`](https://github.com/mui/material-ui/blob/main/composeClasses.js) and [`appendOwnerState.js`](https://github.com/mui/material-ui/blob/main/appendOwnerState.js).
- You can reference actual source files like [`Button.js`](https://github.com/mui/material-ui/blob/main/Button.js) and [`Card.js`](https://github.com/mui/material-ui/blob/main/Card.js) to understand how MUI implements these patterns.
- Combining variants with slots allows you to create reusable component skins that remain type-safe and theme-aware.

## Frequently Asked Questions

### How do I add a custom variant to a Material-UI Button?

Extend the `MuiButton` configuration in your theme's `components` section with a new entry in the `variants` array. Each variant requires a `props` object that acts as a selector (e.g., `{ variant: 'danger' }`) and a `style` object containing the CSS. When you use `<Button variant="danger">`, MUI's styling engine automatically merges your custom styles via the logic in [`packages/mui-material/src/styles/variantUtils.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/styles/variantUtils.js).

### What is the difference between components and componentsProps in MUI slots?

The `components` prop accepts an object mapping slot names to React components, allowing you to replace the default element for that slot (e.g., `components={{ Root: CustomPaper }}`). The `componentsProps` prop accepts an object of props to forward to those slots (e.g., `componentsProps={{ root: { elevation: 4 } }}`). Internally, MUI uses `appendOwnerState` to merge these with the component's internal state before rendering.

### Can I target custom variants when styling component slots?

Yes. Custom variants can target slot-specific class names using the nested selector syntax in your style definition. For example, a custom Button variant can style the `startIcon` slot using `& .MuiButton-startIcon`. Additionally, slots themselves can receive custom variants through `componentsProps`, allowing the slotted component to apply its own themed variations.

### Where does the variant style merging logic live in the MUI source code?

The core variant resolution logic resides in [`packages/mui-material/src/styles/variantUtils.js`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/styles/variantUtils.js), specifically in the `getVariantStyle` function. This utility is called during the styling pipeline (after `useThemeProps` and `getThemeProps`) to match the component's current props against the theme's variant definitions and merge the appropriate styles into the final CSS output.