# How to Type Custom Components with TypeScript in Material-UI: A Complete Guide to Polymorphic Components

> Master typing custom Material-UI components with TypeScript using OverridableComponent for type-safe root element overrides via the component prop.

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

---

**Material-UI provides a polymorphic typing system centered around the `OverridableComponent` interface that enables you to type custom components with TypeScript while maintaining full type safety when overriding the root element via the `component` prop.**

When you type custom components with TypeScript in Material-UI, you leverage the same polymorphic architecture that powers every component in the library. The `mui/material-ui` repository implements this system through the `OverridableComponent` interface and auxiliary type helpers located in [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts). These utilities ensure that props like `component`, `ref`, and `sx` remain fully typed regardless of which root element you render, providing autocomplete and compile-time validation across the entire component tree.

## Understanding the Polymorphic Type System

Material-UI's type system is built on the concept of **polymorphic components**—components that can render as different root elements while preserving type safety. The foundation of this system is the `OverridableComponent` interface, which lives in [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts).

This interface uses TypeScript function overloads to provide two distinct call signatures:

- One signature for when you supply a `component` prop to override the default root.
- One signature for when you use the component with its default root element.

The system relies on several interconnected types defined in the same file:

| Type | Purpose | Location |
|------|---------|----------|
| `OverridableComponent<TypeMap>` | Declares a component with swappable root elements using function overloads. | [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts) |
| `OverrideProps<TypeMap, RootComponent>` | Computes props when `component={RootComponent}` is used, merging component props with root props while excluding duplicates. | [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts) |
| `DefaultComponentProps<TypeMap>` | Props available when using the default root element (no `component` prop). | [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts) |
| `BaseProps<TypeMap>` | Union of component-specific props and generic Material-UI props (`CommonProps`). | [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts) |
| `CommonProps` | Shared props like `className`, `style`, and `sx`. | [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts) |
| `OverridableTypeMap` | Shape required for a component's type map, defining `props` and `defaultComponent`. | [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts) |

## Step-by-Step Guide to Type Custom Components with TypeScript

To properly type custom components with TypeScript in Material-UI, you follow a three-step pattern that mirrors how the library's own components are built. This approach ensures your components support the `component` prop, forward refs correctly, and maintain full type safety.

### Step 1: Define the TypeMap

First, create a **TypeMap** interface that describes your component's props and its default root element. This interface must conform to `OverridableTypeMap`.

```typescript
import * as React from 'react';

interface MyButtonTypeMap<P = {}, D extends React.ElementType = 'button'> {
  props: P & {
    /** Custom color variant */
    myColor?: 'primary' | 'secondary';
    /** Click handler */
    onClick?: React.MouseEventHandler<HTMLButtonElement>;
  };
  defaultComponent: D;
}

```

The `props` field contains your custom props merged with an optional generic `P`. The `defaultComponent` field specifies the default HTML element or component to render.

### Step 2: Declare the OverridableComponent

Next, use the `OverridableComponent` type to declare your component. This creates the function overloads that handle both the default component case and the override case.

```typescript
import { OverridableComponent } from '@mui/material/OverridableComponent';

declare const MyButton: OverridableComponent<MyButtonTypeMap>;

```

This declaration tells TypeScript that `MyButton` accepts either the default props or override props depending on whether the `component` prop is provided.

### Step 3: Implement with ForwardRef

Finally, implement the component using `React.forwardRef` to ensure refs are properly forwarded. Use `OverrideProps` to type the props in the implementation.

```tsx
import * as React from 'react';
import { OverrideProps } from '@mui/material/OverridableComponent';
import { Button as MuiButton } from '@mui/material';

const MyButton = React.forwardRef<HTMLButtonElement, OverrideProps<MyButtonTypeMap, 'button'>>(
  (props, ref) => {
    const { component: Component = 'button', myColor = 'primary', ...others } = props;
    
    return (
      <MuiButton
        component={Component as any}
        ref={ref}
        color={myColor}
        {...others}
      />
    );
  },
) as typeof MyButton;

export default MyButton;

```

The cast `as typeof MyButton` applies the full polymorphic type definition to the implementation, ensuring the `component` prop works correctly with various element types.

## Using Your Typed Custom Component

Once implemented, your custom component provides full type safety when used with different root elements. The TypeScript compiler correctly infers available props based on the `component` prop value.

```tsx
import React from 'react';
import MyButton from './MyButton';
import { Link } from '@mui/material';

export function Demo() {
  return (
    <>
      {/* Default button element - myColor and onClick available */}
      <MyButton myColor="secondary" onClick={() => alert('Clicked!')}>
        Default Button
      </MyButton>

      {/* Override to Link - href prop becomes available */}
      <MyButton component={Link} href="/home" myColor="primary">
        As Link
      </MyButton>

      {/* Override to div - standard HTML div props available */}
      <MyButton component="div" myColor="primary">
        Rendered as div
      </MyButton>
    </>
  );
}

```

When `component={Link}` is specified, TypeScript knows that `href` is a valid prop. When `component="div"` is used, standard HTML div attributes are accepted alongside your custom `myColor` prop.

## Extending Existing Material-UI Components

You can also extend existing Material-UI components while preserving their polymorphic capabilities. This pattern is useful when you want to add custom props to built-in components like `Button`.

```tsx
import * as React from 'react';
import { OverridableComponent, OverrideProps } from '@mui/material/OverridableComponent';
import { Button as MuiButton, ButtonTypeMap, ButtonProps } from '@mui/material/Button';
import { styled } from '@mui/material/styles';

// Extend the original TypeMap with custom props
type MyButtonProps = ButtonProps & { 
  myVariant?: 'ghost' | 'solid' 
};

type MyButtonTypeMap = ButtonTypeMap & { 
  props: MyButtonProps 
};

// Create the styled component
const StyledButton = styled(MuiButton, {
  shouldForwardProp: (prop) => prop !== 'myVariant',
})<{ myVariant?: 'ghost' | 'solid' }>(({ theme, myVariant }) => ({
  ...(myVariant === 'ghost' && {
    backgroundColor: 'transparent',
    border: `1px solid ${theme.palette.primary.main}`,
  }),
}));

// Declare the overridable component
declare const MyButton: OverridableComponent<MyButtonTypeMap>;

const MyButton = React.forwardRef<any, OverrideProps<MyButtonTypeMap, 'button'>>(
  (props, ref) => <StyledButton ref={ref} {...props} />,
) as typeof MyButton;

export default MyButton;

```

Because `MyButtonTypeMap` inherits from `ButtonTypeMap`, all default `Button` overloads stay intact, and the new `myVariant` prop is added to the component's own props. This approach leverages the same architecture found in [`packages/mui-material/src/Button/Button.d.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/Button/Button.d.ts).

## Summary

- **Material-UI's polymorphic system** relies on the `OverridableComponent` interface in [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts) to enable type-safe component overrides.
- **TypeMap structure** requires defining both `props` (component-specific properties) and `defaultComponent` (default root element) to satisfy the `OverridableTypeMap` interface.
- **Implementation pattern** involves declaring the component as `OverridableComponent`, then implementing using `React.forwardRef` with `OverrideProps` for the props parameter, and casting the result to preserve overloads.
- **Extending existing components** works by intersecting the base component's TypeMap with your custom props, preserving all polymorphic capabilities while adding new functionality.

## Frequently Asked Questions

### What is the OverridableComponent interface in Material-UI?

The `OverridableComponent` interface is the core TypeScript utility that enables polymorphic behavior in Material-UI. Defined in [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts), it uses function overloads to provide distinct type signatures for when you use the default root element versus when you override it with the `component` prop, ensuring full type safety across both scenarios.

### How do I forward refs in typed custom Material-UI components?

Use `React.forwardRef` with the `OverrideProps` type from `@mui/material/OverridableComponent`. The generic parameters should specify the element type (e.g., `HTMLButtonElement`) and `OverrideProps<YourTypeMap, 'defaultElement'>`. After defining the forwardRef component, cast it `as typeof YourComponent` to apply the full `OverridableComponent` overloads.

### Can I extend existing Material-UI components while keeping polymorphic types?

Yes, you can extend existing components by creating a new TypeMap that intersects with the base component's TypeMap, such as `type MyTypeMap = ButtonTypeMap & { props: MyCustomProps }`. This preserves all existing polymorphic capabilities from the base component while adding your custom props, allowing the `component` prop to work exactly as it does in the original Material-UI component.

### Where are the TypeScript definitions for polymorphic components located?

The core TypeScript definitions for polymorphic components are located in [`packages/mui-material/src/OverridableComponent/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/OverridableComponent/index.ts) within the `mui/material-ui` repository. Additional type utilities are available in [`packages/mui-types/src/index.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-types/src/index.ts). For reference implementations showing how built-in components use these types, examine [`packages/mui-material/src/Button/Button.d.ts`](https://github.com/mui/material-ui/blob/main/packages/mui-material/src/Button/Button.d.ts) and the test specifications in [`packages/mui-material/test/typescript/OverridableComponent.spec.tsx`](https://github.com/mui/material-ui/blob/main/packages/mui-material/test/typescript/OverridableComponent.spec.tsx).