Mantine Custom Components Skill
Component template
import {
Box,
BoxProps,
createVarsResolver,
ElementProps,
factory,
Factory,
getRadius,
MantineRadius,
StylesApiProps,
useProps,
useStyles,
} from "@mantine/core";
import classes from "./MyComponent.module.css";
export type MyComponentStylesNames = "root" | "inner";
export type MyComponentVariant = "filled" | "outline";
export type MyComponentCssVariables = { root: "--my-radius" };
export interface MyComponentProps
extends BoxProps, StylesApiProps<MyComponentFactory>, ElementProps<"div"> {
radius?: MantineRadius;
}
export type MyComponentFactory = Factory<{
props: MyComponentProps;
ref: HTMLDivElement;
stylesNames: MyComponentStylesNames;
vars: MyComponentCssVariables;
variant: MyComponentVariant;
}>;
const defaultProps = { radius: "md" } satisfies Partial<MyComponentProps>;
const varsResolver = createVarsResolver<MyComponentFactory>((_theme, { radius }) => ({
root: { "--my-radius": getRadius(radius) },
}));
export const MyComponent = factory<MyComponentFactory>((_props) => {
const props = useProps("MyComponent", defaultProps, _props);
const { classNames, className, style, styles, unstyled, vars, attributes, radius, ...others } =
props;
const getStyles = useStyles<MyComponentFactory>({
name: "MyComponent",
classes,
props,
className,
style,
classNames,
styles,
unstyled,
vars,
attributes,
varsResolver,
});
return <Box {...getStyles("root")} {...others} />;
});
MyComponent.displayName = "@mantine/core/MyComponent";
MyComponent.classes = classes;
Factory variant — which to use
| Scenario |
Factory function |
Type |
| Standard component |
factory() |
Factory<{}> |
Supports component prop (polymorphic) |
polymorphicFactory() |
PolymorphicFactory<{}> — add defaultComponent and defaultRef |
Props change based on a generic (e.g. multiple) |
genericFactory() |
Factory<{ signature: ... }> |
Use polymorphicFactory sparingly — it adds TypeScript overhead and slows IDE autocomplete.
Factory type fields
Factory<{
props: MyComponentProps; // required
ref: HTMLDivElement; // element type for the forwarded ref
stylesNames: 'root' | 'inner'; // union of Styles API selectors
vars: { root: '--my-var' }; // CSS variable map per selector
variant: 'filled' | 'outline'; // accepted variant strings
staticComponents: { // sub-components (compound pattern)
Item: typeof MyComponentItem;
};
compound?: boolean; // true = sub-component; disables theme classNames/styles/vars
ctx?: MyContextType; // passed to styles/vars resolvers as third arg
signature?: (...) => JSX.Element; // only for genericFactory
}>
Theme integration
Users and the theme can override defaults via Component.extend():
const theme = createTheme({
components: {
MyComponent: MyComponent.extend({
defaultProps: { radius: "xl" },
classNames: { root: "my-root" },
styles: { root: { color: "red" } },
vars: (_theme, props) => ({ root: { "--my-radius": getRadius(props.radius) } }),
}),
},
});
References
references/api.md — All imports: factory, useProps, useStyles, createVarsResolver, createSafeContext, StylesApiProps, CompoundStylesApiProps, BoxProps, ElementProps, theme helpers (getSize, getRadius, etc.)
references/patterns.md — Full examples: compound components with context, polymorphic component, generic component, theme integration
1---2name: mantine-custom-components3description: Build custom components that integrate with Mantine's theming, Styles API, and core features. Use this skill when: (1) creating a new component using factory(), polymorphicFactory(), or genericFactory(), (2) adding Styles API support (classNames, styles, vars, unstyled), (3) implementing CSS variables via createVarsResolver, (4) building compound components with sub-components and shared context, (5) registering a component with MantineProvider via Component.extend(), or (6) any task involving Factory, useProps, useStyles, BoxProps, StylesApiProps, or ElementProps in @mantine/core.4---56# Mantine Custom Components Skill78## Component template910```tsx11import {12 Box,13 BoxProps,14 createVarsResolver,15 ElementProps,16 factory,17 Factory,18 getRadius,19 MantineRadius,20 StylesApiProps,21 useProps,22 useStyles,23} from "@mantine/core";24import classes from "./MyComponent.module.css";2526export type MyComponentStylesNames = "root" | "inner";27export type MyComponentVariant = "filled" | "outline";28export type MyComponentCssVariables = { root: "--my-radius" };2930export interface MyComponentProps31 extends BoxProps, StylesApiProps<MyComponentFactory>, ElementProps<"div"> {32 radius?: MantineRadius;33}3435export type MyComponentFactory = Factory<{36 props: MyComponentProps;37 ref: HTMLDivElement;38 stylesNames: MyComponentStylesNames;39 vars: MyComponentCssVariables;40 variant: MyComponentVariant;41}>;4243const defaultProps = { radius: "md" } satisfies Partial<MyComponentProps>;4445const varsResolver = createVarsResolver<MyComponentFactory>((_theme, { radius }) => ({46 root: { "--my-radius": getRadius(radius) },47}));4849export const MyComponent = factory<MyComponentFactory>((_props) => {50 const props = useProps("MyComponent", defaultProps, _props);51 const { classNames, className, style, styles, unstyled, vars, attributes, radius, ...others } =52 props;5354 const getStyles = useStyles<MyComponentFactory>({55 name: "MyComponent",56 classes,57 props,58 className,59 style,60 classNames,61 styles,62 unstyled,63 vars,64 attributes,65 varsResolver,66 });6768 return <Box {...getStyles("root")} {...others} />;69});7071MyComponent.displayName = "@mantine/core/MyComponent";72MyComponent.classes = classes;73```7475## Factory variant — which to use7677| Scenario | Factory function | Type |78| ------------------------------------------------- | ---------------------- | ------------------------------------------------------------------ |79| Standard component | `factory()` | `Factory<{}>` |80| Supports `component` prop (polymorphic) | `polymorphicFactory()` | `PolymorphicFactory<{}>` — add `defaultComponent` and `defaultRef` |81| Props change based on a generic (e.g. `multiple`) | `genericFactory()` | `Factory<{ signature: ... }>` |8283Use `polymorphicFactory` sparingly — it adds TypeScript overhead and slows IDE autocomplete.8485## Factory type fields8687```ts88Factory<{89 props: MyComponentProps; // required90 ref: HTMLDivElement; // element type for the forwarded ref91 stylesNames: 'root' | 'inner'; // union of Styles API selectors92 vars: { root: '--my-var' }; // CSS variable map per selector93 variant: 'filled' | 'outline'; // accepted variant strings94 staticComponents: { // sub-components (compound pattern)95 Item: typeof MyComponentItem;96 };97 compound?: boolean; // true = sub-component; disables theme classNames/styles/vars98 ctx?: MyContextType; // passed to styles/vars resolvers as third arg99 signature?: (...) => JSX.Element; // only for genericFactory100}>101```102103## Theme integration104105Users and the theme can override defaults via `Component.extend()`:106107```ts108const theme = createTheme({109 components: {110 MyComponent: MyComponent.extend({111 defaultProps: { radius: "xl" },112 classNames: { root: "my-root" },113 styles: { root: { color: "red" } },114 vars: (_theme, props) => ({ root: { "--my-radius": getRadius(props.radius) } }),115 }),116 },117});118```119120## References121122- **[`references/api.md`](references/api.md)** — All imports: `factory`, `useProps`, `useStyles`, `createVarsResolver`, `createSafeContext`, `StylesApiProps`, `CompoundStylesApiProps`, `BoxProps`, `ElementProps`, theme helpers (`getSize`, `getRadius`, etc.)123- **[`references/patterns.md`](references/patterns.md)** — Full examples: compound components with context, polymorphic component, generic component, theme integration