Build faster with Premium Chakra UI Components 💎

Learn more

Handling dynamic styling in recipes

January 3, 2025

Suppose that you need to change the padding of a button based on some pressed state.

const App = () => {  const [isPressed, setPressed] = useState(false)  // How do style the button separately based on the pressed state?  return <Button>Click Me</Button> }

You might be tempted to do something like this:

import { defineRecipe } from "@chakra-ui/react"  export const buttonRecipe = defineRecipe({  base: {  display: "flex",  },  variants: {  size: {  sm: ({ isPressed }) => ({  padding: isPressed ? "8" : "4",  fontSize: "12px",  }),  },  }, })

This doesn't work because Chakra doesn't support functions in recipes. We require recipes to be serializable.

There are two ways to handle this:

Using data-* attributes

First, apply the dynamic values to the component using the data-* attribute.

const App = () => {  const [isPressed, setPressed] = useState(false)  return <Button data-pressed={isPressed || undefined}>Click Me</Button> }

Next, style the recipe using the data-* attribute.

export const buttonRecipe = defineRecipe({  base: {  display: "flex",  },  variants: {  size: {  sm: {  padding: "4",  fontSize: "12px",  "&[data-pressed]": {  padding: "8",  },  },  },  }, })

Using compoundVariants

Compound variants allow you to create style overrides based on variant combinations.

import { defineRecipe } from "@chakra-ui/react"  export const buttonRecipe = defineRecipe({  base: {  display: "flex",  },  variants: {  size: {  sm: {  padding: "4",  fontSize: "12px",  },  },  isPressed: {  true: {},  false: {},  },  },  compoundVariants: [  {  size: "sm",  isPressed: true,  css: {  padding: "8px",  fontSize: "12px",  },  },  ], })

Then, you can pass the isPressed variant to the component as props.

<Button visual="solid" isPressed={isPressed}>  Click Me </Button>
note
If you use TypeScript, don't forget to run the npx @chakra-ui/cli typegen command to generate the types for the recipe.