Slot Recipes
Learn how to style multiple parts components with slot recipes.
Slot Recipes come in handy when you need to apply style variations to multiple parts of a component.
A slot recipe consists of these properties:
className
: The className prefix to attach to the component slotslots
: An array of component parts to stylebase
: The base styles per slotvariants
: The different visual styles for each slotdefaultVariants
: The default variant for the componentcompoundVariants
: The compound variant combination and style overrides for each slot.
Use the defineSlotRecipe
identity function to create a slot recipe.
checkbox.recipe.ts
import { defineSlotRecipe } from "@chakra-ui/react"
export const checkboxSlotRecipe = defineSlotRecipe({
slots: ["root", "control", "label"],
base: {
root: { display: "flex", alignItems: "center", gap: "2" },
control: { borderWidth: "1px", borderRadius: "sm" },
label: { marginStart: "2" },
},
variants: {
size: {
sm: {
control: { width: "8", height: "8" },
label: { fontSize: "sm" },
},
md: {
control: { width: "10", height: "10" },
label: { fontSize: "md" },
},
},
},
})
There are two ways to use the recipe in a component:
- Directly in the component with
useSlotRecipe
- As a compound component (recommended) with
createSlotRecipeContext
"use client"
directive is required to use the useSlotRecipe
hook
or createSlotRecipeContext
function. This is because they rely on react hooks
like useContext
and useInsertionEffect
under the hood.Use the useSlotRecipe
hook to get the recipe for a component. Then, call the
recipe with its variant props to get the styles.
checkbox.tsx
"use client"
import { chakra, useSlotRecipe } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"
export const Checkbox = (props) => {
const { size, ...restProps } = props
const recipe = useSlotRecipe({ recipe: checkboxSlotRecipe })
const styles = recipe({ size })
return (
<chakra.label css={styles.root}>
<chakra.input type="checkbox" css={styles.control} {...restProps} />
<chakra.span css={styles.label}>Checkbox Label</chakra.span>
</chakra.label>
)
}
Notice how the size
prop was destructured from the props to be passed to the
recipe. A smarter approach would be to automatically split the recipe props from
the component props.
To do that, use the recipe.splitVariantProps
function to split the recipe
props from the component props.
checkbox.tsx
"use client"
import { chakra, useSlotRecipe } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"
export const Checkbox = (props) => {
const recipe = useSlotRecipe({ recipe: checkboxSlotRecipe })
const [recipeProps, restProps] = recipe.splitVariantProps(props)
const styles = recipe(recipeProps)
//...
}
To infer the recipe variant prop types, use the RecipeVariantProps
type
helper.
checkbox.tsx
import type { RecipeVariantProps } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"
type CheckboxVariantProps = RecipeVariantProps<typeof checkboxSlotRecipe>
export interface CheckboxProps
extends React.PropsWithChildren<CheckboxVariantProps> {}
Pass the recipe to the createSlotRecipeContext
function to create a slot
recipe context.
Then, use the withProvider
and withContext
functions to create the compound
components that share the same context.
checkbox.tsx
"use client"
import { createSlotRecipeContext } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"
const { withProvider, withContext } = createSlotRecipeContext({
recipe: checkboxSlotRecipe,
})
export const CheckboxRoot = withProvider("label", "root")
export const CheckboxControl = withContext("input", "control")
export const CheckboxLabel = withContext("span", "label")
Pass the variant props to the "root" component that to apply the styles.
Note: The root component is the one that used the withProvider
function.
app.tsx
const App = () => {
return (
<CheckboxRoot size="md">
<CheckboxControl />
<CheckboxLabel />
</CheckboxRoot>
)
}
This approach supports the use of the unstyled
prop to remove the styles
applied by the recipe.
checkbox.tsx
<CheckboxRoot unstyled>
<CheckboxControl />
<CheckboxLabel />
</CheckboxRoot>
To infer the recipe variant prop types, use the RecipeVariantProps
type
helper.
import type { RecipeVariantProps, UnstyledProp } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"
type CheckboxVariantProps = RecipeVariantProps<typeof checkboxSlotRecipe>
export interface CheckboxProps
extends React.PropsWithChildren<CheckboxVariantProps>,
UnstyledProp {}
Use the compoundVariants
property to define a set of variants that are applied
based on a combination of other variants.
checkbox.recipe.ts
import { defineSlotRecipe } from "@chakra-ui/react"
export const checkboxRecipe = defineSlotRecipe({
slots: ["root", "control", "label"],
base: {},
variants: {
size: {
sm: {},
md: {},
},
visual: {
contained: {},
outline: {},
},
},
compoundVariants: [
{
size: "sm",
visual: "outline",
css: {
control: { borderWidth: "1px" },
label: { color: "green.500" },
},
},
],
})
In some cases, targeting a slot by className might be needed.
- Set the
className
property in the config - The naming convention is
${className}__${slot}
checkbox.recipe.ts
import { defineSlotRecipe } from "@chakra-ui/react"
export const checkboxRecipe = defineSlotRecipe({
className: "checkbox",
slots: ["root", "control", "label"],
base: {
root: {
bg: "blue.500",
_hover: {
"& .checkbox__label": { color: "white" },
},
},
},
})
To use the recipe in a reusable manner, move it to the system theme and add it
to theme.slotRecipes
property.
No need to add the "use client"
directive when using the recipe in the
theme.
theme.ts
import { createSystem, defineConfig } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"
const config = defineConfig({
theme: {
slotRecipes: {
checkbox: checkboxSlotRecipe,
},
},
})
export default createSystem(config)
Use the CLI to generate the types for the recipe.
npx @chakra-ui/cli@next typegen ./theme.ts
Then, import the generated types in your component.
checkbox.tsx
import type { SlotRecipeProps, UnstyledProp } from "@chakra-ui/react"
export interface CheckboxProps
extends SlotRecipeProps<"checkbox">,
UnstyledProp {}
If you use the recipe directly in your component, update the useRecipe
to use
the key
property to get the recipe from the theme.
checkbox.tsx
const Checkbox = () => {
- const recipe = useRecipe({ recipe: checkboxRecipe })
+ const recipe = useRecipe({ key: "checkbox" })
// ...
}
If you create a compound component, update the createSlotRecipeContext
to use
the key
property.
checkbox.tsx
const { withProvider, withContext } = createSlotRecipeContext({
- recipe: checkboxRecipe,
+ key: "checkbox",
})