Recipes
Writing multi-variant styles with recipes in Chakra.
Overview
Chakra provides a way to write CSS-in-JS with better performance, developer experience, and composability. One of its key features is the ability to create multi-variant styles with a type-safe runtime API.
A recipe consists of these properties:
className
: The className to attach to the componentbase
: The base styles for the componentvariants
: The different visual styles for the componentcompoundVariants
: The different combinations of variants for the componentdefaultVariants
: The default variant values for the component
Defining the recipe
Use the defineRecipe
identity function to create a recipe.
button.recipe.ts
import { defineRecipe } from "@chakra-ui/react"
export const buttonRecipe = defineRecipe({
base: {
display: "flex",
},
variants: {
visual: {
solid: { bg: "red.200", color: "white" },
outline: { borderWidth: "1px", borderColor: "red.200" },
},
size: {
sm: { padding: "4", fontSize: "12px" },
lg: { padding: "8", fontSize: "24px" },
},
},
})
Using the recipe
There are two ways to use the recipe in a component:
- Directly in the component with
useRecipe
- Creating a component (recommended) with the
chakra
factory
"use client"
directive is required since it relies on
react hooks like useContext
and useInsertionEffect
under the hood.Directly in component
Use the useRecipe
hook to get the recipe for a component. Then, call the
recipe with its variant props to get the styles.
button.tsx
"use client"
import { chakra, useRecipe } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
export const Button = (props) => {
const { visual, size, ...restProps } = props
const recipe = useRecipe({ recipe: buttonRecipe })
const styles = recipe({ visual, size })
return <chakra.button css={styles} {...restProps} />
}
splitVariantProps
Notice how the visual
and size
props were 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.
button.tsx
"use client"
import { chakra, useRecipe } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
export const Button = (props) => {
const recipe = useRecipe({ recipe: buttonRecipe })
const [recipeProps, restProps] = recipe.splitVariantProps(props)
const styles = recipe(recipeProps)
// ...
}
TypeScript
To infer the recipe variant prop types, use the RecipeVariantProps
type
helper.
button.tsx
import type { RecipeVariantProps } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
type ButtonVariantProps = RecipeVariantProps<typeof buttonRecipe>
export interface ButtonProps
extends React.PropsWithChildren<ButtonVariantProps> {}
Creating a component
Use the chakra
function to create a component from a recipe.
button.tsx
"use client"
import { chakra } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
export const Button = chakra("button", buttonRecipe)
Next, use the component and pass recipe properties to it.
app.tsx
import { Button } from "./button"
const App = () => {
return (
<Button visual="solid" size="lg">
Click Me
</Button>
)
}
Default Variants
The defaultVariants
property is used to set the default variant values for the
recipe. This is useful when you want to apply a variant by default.
button.tsx
"use client"
import { chakra } from "@chakra-ui/react"
const Button = chakra("button", {
base: {
display: "flex",
},
variants: {
visual: {
solid: { bg: "red.200", color: "white" },
outline: { borderWidth: "1px", borderColor: "red.200" },
},
size: {
sm: { padding: "4", fontSize: "12px" },
lg: { padding: "8", fontSize: "24px" },
},
},
defaultVariants: {
visual: "solid",
size: "lg",
},
})
Compound Variants
Use the compoundVariants
property to define a set of variants that are applied
based on a combination of other variants.
button.tsx
"use client"
import { chakra } from "@chakra-ui/react"
const button = cva({
base: {
display: "flex",
},
variants: {
visual: {
solid: { bg: "red.200", color: "white" },
outline: { borderWidth: "1px", borderColor: "red.200" },
},
size: {
sm: { padding: "4", fontSize: "12px" },
lg: { padding: "8", fontSize: "24px" },
},
},
compoundVariants: [
{
size: "small",
visual: "outline",
css: {
borderWidth: "2px",
},
},
],
})
When you use the size="small"
and visual="outline"
variants together, the
compoundVariants
will apply the css
property to the component.
app.tsx
<Button size="small" visual="outline">
Click Me
</Button>
Theme Usage
To use the recipe in a reusable manner, move it to the system theme and add it
to theme.recipes
property.
theme.ts
import { createSystem, defineConfig } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
const config = defineConfig({
theme: {
recipes: {
button: buttonRecipe,
},
},
})
export default createSystem(config)
TypeScript
Use the CLI to generate the types for the recipe.
npx @chakra-ui/cli typegen ./theme.ts
Then, import the generated types in your component.
button.tsx
import type { RecipeVariantProps } from "@chakra-ui/react"
import { buttonRecipe } from "./button.recipe"
type ButtonVariantProps = RecipeVariantProps<typeof buttonRecipe>
export interface ButtonProps
extends React.PropsWithChildren<ButtonVariantProps> {}
Update code
If you use the recipe directly in your component, update the useRecipe
to use
the key
property to get the recipe from the theme.
button.tsx
const Button = () => {
- const recipe = useRecipe({ recipe: buttonRecipe })
+ const recipe = useRecipe({ key: "button" })
// ...
}