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.