Skip to content

Instantly share code, notes, and snippets.

@whoisYeshua
Created March 15, 2025 12:01
Show Gist options
  • Save whoisYeshua/670b70c31f7d0c46fefbf766919fd421 to your computer and use it in GitHub Desktop.
Save whoisYeshua/670b70c31f7d0c46fefbf766919fd421 to your computer and use it in GitHub Desktop.
Mantine + emotion (createStyled)
/* eslint-disable @typescript-eslint/no-explicit-any */
import { CSSObject } from '@emotion/serialize'
import { MantineTheme, useMantineTheme } from '@mantine/core'
import { getHelpers, useCss, type EmotionHelpers } from '@mantine/emotion'
import type { ComponentProps, ComponentType, JSX } from 'react'
export type IntrinsicElementsKeys = keyof JSX.IntrinsicElements
export function createStyled<
Type extends IntrinsicElementsKeys | ComponentType<any>,
Props = Type extends IntrinsicElementsKeys ? JSX.IntrinsicElements[Type] : ComponentProps<Type>,
Input extends CSSObject = CSSObject,
>(
Tag: Type,
input: ((theme: MantineTheme, props: Props, helpers: EmotionHelpers) => Input) | Input
) {
const getCssObject = typeof input === 'function' ? input : () => input
return function NewComponent(props: Props) {
const theme = useMantineTheme()
const helpers = getHelpers(theme)
const cssObject: Record<string, any> = getCssObject(theme, props, helpers)
const { css } = useCss()
const className = css(cssObject)
// @ts-expect-error -- dont know how to type
const combinedClasses = props?.className ? className.concat(' ', props.className) : className
// @ts-expect-error -- dont know how to type
return <Tag {...props} className={combinedClasses} />
}
}
import { createStyled } from './createStyled'
export const Demo = () => {
return (
<Container>
Box with emotion sx prop
<SubButtonStyled color="blue" radius={4} />
<SubButtonStyled color="violet" radius={40} />
</Container>
)
}
const Container = createStyled('div', {
padding: 40,
'&:hover': {
background: 'var(--mantine-primary-color-0)',
},
})
interface SubButtonProps {
color: 'blue' | 'violet'
radius: number
className?: string
}
const SubButton = ({ color, radius, className }: SubButtonProps) => {
return (
<div>
<button type="button" className={className}>
{color} button with {radius} radius
</button>
</div>
)
}
const SubButtonStyled = createStyled(SubButton, (theme, { color, radius }) => ({
color: theme.white,
backgroundColor: theme.colors[color][6],
borderRadius: radius,
border: 0,
padding: `${theme.spacing.sm} ${theme.spacing.lg}`,
cursor: 'pointer',
margin: theme.spacing.md,
// Use pseudo-classes just like you would in Sass
'&:hover': {
backgroundColor: theme.colors.blue[9],
},
}))
import '@mantine/core'
import type { EmotionStyles, EmotionSx } from '@mantine/emotion'
declare module '@mantine/core' {
export interface BoxProps {
sx?: EmotionSx
styles?: EmotionStyles
}
}
{
"name": "mantine-improve",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@emotion/cache": "^11.14.0",
"@emotion/react": "^11.14.0",
"@emotion/serialize": "^1.3.3",
"@emotion/utils": "^1.4.2",
"@mantine/emotion": "^7.17.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
"vite": "^6.2.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment