Guides

Dynamic styling

While Panda is mainly focused on the statically analyzable styles, you might need to handle dynamic styles in your project.

💡

We recommend that you avoid relying on runtime values for your styles . Consider using recipes, css variables or data-* attributes instead.

Here are some ways you can handle dynamic styles in Panda:

Runtime values

Using a value that is not statically analyzable at build-time will not work in Panda due to the inability to determine the style values.

App.tsx
import { css } from '../styled-system/css'
 
const App = () => {
  const [color, setColor] = useState('red.300')
 
  return (
    <div 
      className={css({ 
        // ❌ Avoid: Panda can't determine the value of color at build-time
        color 
      })} 
    />
  )
}

The example above will not work because Panda can't determine the value of color at build-time. Here are some ways to fix this:

Using Static CSS

Panda supports a staticCss option in the config you can use to pre-generate some styles ahead of time.

panda.config.ts
import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  staticCss: {
    css: [{ 
      properties: {
        // ✅ Good: Pre-generate the styles for the color
        color: ['red.300'] 
      } 
    }]
  }
})
Button.tsx
import { useState } from 'react'
import { styled } from '../styled-system/jsx'
 
export const Button = () => {
  const [color, setColor] = useState('red.300')
 
  // ✅ Good: This will work because `red.300` is pre-generated using `staticCss` config
  return <styled.button color={color} />
}

Using token()

The token() function is generated by Panda and contains an object of all tokens by dot-path, allowing you to query for token's raw value at runtime.

App.tsx
import { css } from '../styled-system/css'
import { token } from '../styled-system/tokens'
 
const Component = props => {
  return (
    <div
      
      className={css({
        // ✅ Good: Store the value in a CSS custom property
        color: 'var(--color)' 
      })}
      
      style={{
        // ✅ Good: Handle the runtime value in the style attribute
        '--color': token(`colors.${props.color}`)
      }}
    >
      Dynamic color with runtime value
    </div>
  )
}
 
// App.tsx
const App = () => {
  const [runtimeColor, setRuntimeColor] = useState('pink.300')
 
  return <Component color={runtimeColor} />
}

Using token.var()

You could also directly use the token.var() function to get a reference to the underling CSS custom property for a given token:

App.tsx
import { token } from '../styled-system/tokens'
 
const Component = props => {
  return (
    <div 
      style={{
        // ✅ Good: Dynamically generate CSS custom property from the token
        color: token.var(`colors.${props.color}`) 
      }}
    >
      Dynamic color with runtime value
    </div>
  )
}
 
const App = () => {
  const [runtimeColor, setRuntimeColor] = useState('yellow.300')
  
  return <Component color={runtimeColor} />
}

JSX Style Props

Panda supports forwarding JSX style properties to any element in your codebase.

For example, let's say we create a Card component that accepts a color prop:

Card.tsx
import { styled } from '../styled-system/jsx'
 
const Card = props => {
  return <styled.div px="4" py="3" {...props} />
}

Then you add more style props to the Card component in a different file:

App.tsx
const App = () => {
  return (
    <Card color="blue.300">
      <p>Some content</p>
    </Card>
  )
}

As long as all prop-value pairs are statically extractable, Panda will automatically generate the CSS, so avoid using runtime values:

App.tsx
const App = () => {
  const [color, setColor] = useState('blue.300')
 
  // ❌ Avoid: Panda can't determine the value of color at build-time
  return (
    <Card color={color}>
      <p>Some content</p>
    </Card>
  )
}

Runtime conditions

Even though we recommend that you first look for better alternatives (such as using recipe variants), you may still occasionally need runtime conditions.

When encountering a runtime condition, Panda will first try to resolve it statically. If it can't, it will fallback to the generating the corresponding CSS for each possible branches.

import { css } from '../styled-system/css'
import { Stack } from '../styled-system/jsx'
 
const App = () => {
  const [isHovered, setIsHovered] = useState(false)
 
  return (
    <Stack
      color={isHovered ? { _hover: 'red.100' } : 'red.200'}
      _hover={{
        color: { base: 'red.300', md: isHovered ? 'red.400' : undefined }
      }}
    >
      <div className={css({ color: isHovered ? 'red.500' : 'red.600' })} />
    </Stack>
  )
}

Since none of the conditions above are statically extractable, Panda will generate css for all possible code path, resulting in a css that looks like this:

/* ... */
@layer utilities {
  .hover\:text_red\.100:where(:hover, [data-hover]) {
    color: var(--colors-red-100);
  }
 
  .text_red\.200 {
    color: var(--colors-red-200);
  }
 
  .hover\:text_red\.300:where(:hover, [data-hover]) {
    color: var(--colors-red-300);
  }
 
  @media screen and (min-width: 768px) {
    .hover\:md\:text_red\.400:where(:hover, [data-hover]) {
      color: var(--colors-red-400);
    }
  }
 
  .text_red\.500 {
    color: var(--colors-red-500);
  }
 
  .text_red\.600 {
    color: var(--colors-red-600);
  }
}
/* ... */

Referenced values

Although you should have your styles inlined most of the time, maybe you want to store a value in a variable and re-use in multiple places. This should be fine as long as you keep it statically analyzable.

Here's a short list of things to avoid:

  • Variables that are not defined in the same file
  • Variables resulting from a function call (e.g. const color = getColor())
💡

If you don't know what value a variable holds with a quick glance, Panda won't be able to either.

import { css } from './styled-system/css'
 
// ✅ Good: All values are statically extractable
const mainColor = 'red.300'
const sizes = { sm: '12px', md: '16px', '2xl': '42px' }
 
const App = () => {
  return (
    <div
      className={css({
        color: mainColor,
        fontSize: sizes.md,
        width: sizes['2xl']
      })}
    />
  )
}

Runtime reference on known objects

Using a more complex but still common example :

const colorByType = {
  primary: 'red.300',
  secondary: 'blue.300',
  tertiary: 'green.300'
}
 
const Section = () => {
  const [type, setType] = useState('primary')
 
  // ❌ Avoid: since only "gray.100" is statically extractable here
  // This will not work as expected, the color CSS won't be generated
  return (
    <section className={css({ color: colorByType[type] ?? 'gray.100' })}>
      ❌ Will not be extracted
    </section>
  )
}

Even though colorByType is statically analyzable, Panda does not yet support this kind of automatic extraction fallback. This is the perfect opportunity to use the recipes.

import { cva } from './styled-system/cva'
 
const sectionRecipe = cva({
  base: { color: 'gray.100' },
  variants: {
    type: {
      primary: { color: 'red.300' },
      secondary: { color: 'blue.300' },
      tertiary: { color: 'green.300' }
    }
  }
})
 
const Section = () => {
  const [type, setType] = useState('primary')
 
  // ✅ Good: This will work as expected
  return (
    <section className={sectionRecipe({ type })}>
      ✅ With a recipe
    </section>
  )
}

Not only did you get the same end result, but you also got a more readable and maintainable code !

You can now :

  • add more variants to your recipe
  • add more properties
  • use a shorthand or a condition

All of this with complete type-safety and without having to make drastic changes to the component.

💡

Note that you can also integrate this recipe directly into your theme if you want to only generate the CSS that you use, among other things

Summary

What you can do

// ✅ Good: Conditional styles
<styled.div color={{ base: "red.100", md: "red.200" }} />
 
// ✅ Good: Arbitrary value
<styled.div color="#121qsd" />
 
// ✅ Good: Arbitrary selector
<styled.div css={{ ["&[data-thing] > span": { color: "red.100" } }} />
 
// ✅ Good: Runtime value (with config.`staticCss`)
const Button = () => {
  const [color, setColor] = useState('red.300')
  return <styled.button color={color} />
}
 
// ✅ Good: Runtime condition
<styled.div color={{ base: "red.100", md: isHovered ? "red.200" : "red.300" }} />
 
// ✅ Good: Referenced value
<styled.div color={mainColor} />
 

What you can't do

// ❌ Avoid: Runtime value (without config.`staticCss`)
const Button = () => {
  const [color, setColor] = useState('red.300')
  return <styled.button color={color} />
}
 
// ❌ Avoid: Referenced value (not statically analyzable or from another file)
<styled.div color={getColor()} />
<styled.div color={colors[getColorName()]} />
<styled.div color={colors[colorFromAnotherFile]} />