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 typegen command to generate the types for the recipe. See the CLI docs for how to run typegen in postinstall, CI, and monorepos.

npx @chakra-ui/cli typegen