Polymorphic components

Some of the Mantine components (Button, Text, etc.) accept special component prop that lets you change the root element. This guide will explain how to correctly use them with HTML elements, custom components, Next.js Link and TypeScript.

With HTML elements

You can use any HTML element in component prop. For example, a tag will render an anchor:

import { Button } from '@mantine/core';
import { IconExternalLink } from '@tabler/icons';
function Demo() {
return (
<Button component="a" href="#" variant="outline" leftIcon={<IconExternalLink size={14} />}>
Open in new tab
</Button>
);
}

With other components

You can also use polymorphic components with any other component. For example, with react-router-dom Link:

import { Link } from 'react-router-dom';
import { Button } from '@mantine/core';
function Demo() {
return (
<Button component={Link} to="/react-router">
React router link
</Button>
);
}

With Next.js Link

Next.js link does not work in the same way as other similar components in all Next.js version:

// For Next.js 12 and below
import Link from 'next/link';
import { Button } from '@mantine/core';
function Demo() {
return (
<Link href="/hello" passHref>
<Button component="a">Next link button</Button>
</Link>
);
}
// For Next.js 13 and above
import Link from 'next/link';
import { Button } from '@mantine/core';
function Demo() {
return (
<Button component={Link} href="/hello">
Next link button
</Button>
);
}

With TypeScript

Polymorphic components cannot infer ref and events correctly in some cases. When this happens – provide generic component type:

import { Button } from '@mantine/core';
function Demo() {
return (
<Button<'a'>
component="a"
href="#"
// events and ref types cannot be inferred when component prop is set
// unless you set type explicitly
onClick={(event) => console.log(event)}
ref={(node) => console.log(node)}
>
Open in new tab
</Button>
);
}

Create your own polymorphic components

You can create your own polymorphic components using createPolymorphicComponent function and Box component:

import { forwardRef } from 'react';
import { Box, createPolymorphicComponent } from '@mantine/core';
interface ButtonProps {
children: React.ReactNode;
}
// Create intermediate component with default ref type and props
const _Button = forwardRef<HTMLButtonElement, ButtonProps>(({ children, ...others }, ref) => (
<Box
// define default component, you will be able to override it with `component` prop from ...others
component="button"
ref={ref}
{...others}
>
{children}
</Box>
));
// createPolymorphicComponent accepts two types: default element and component props
// all other props will be added to component type automatically
export const Button = createPolymorphicComponent<'button', ButtonProps>(_Button);