Want to skip the docs? Check out pandamastery.com - the best way to learn Panda CSS

design systems
track usage

Track usage in wrapped components

Why renaming or wrapping a component can stop Panda from pre-generating its recipe variant CSS, and how to fix it.

Panda's static extraction reads your source code at build time, it doesn't run it. For a plain button({ size }) call with a literal value, that's straightforward. For a component your library wraps, re-exports, or renames, it needs a way to connect the JSX tag a user writes back to the recipe or pattern that should generate CSS for it. This page covers what breaks that connection and how to fix it.

Why a rename breaks tracking

Panda recognizes a JSX tag as using a given recipe or pattern by matching the tag's name (or a configured alias) at the call site. If your library exports a component under a different name than the recipe it wraps, Panda has no way to know <Random size="lg" /> should pull the button recipe's size="lg" styles:

// recipe is named "button", but the component is named "Random"
const Random = ({ size, children }) => {
  return (
    <button {...props} className={button({ size })}>
      {children}
    </button>
  )
}

The recipe still works. Direct calls like button({ size: 'lg' }) with a literal value are extracted regardless of what you name the wrapping component. What's lost is JSX-based tracking, so a user writing <Random size="lg" /> in their own code won't get that variant's CSS pre-generated from their app.

See Limitations for the full explanation, including the related case where the variant prop itself is renamed on the way through (size becoming buttonSize, for example).

Fixing it: match the name, or configure an alias

The straightforward fix is exporting your component under the same name as the recipe. When that's not practical, recipes and slot recipes both accept a jsx option to register additional names Panda should treat as equivalent:

card.recipe.ts

import { defineSlotRecipe } from '@pandacss/dev'
 
export const cardRecipe = defineSlotRecipe({
  className: 'card',
  jsx: ['Card.Root'], // also matches this JSX tag name
  slots: ['root', 'label']
  // ...
})

This is the same rule covered in Config Recipes for components built with createSlotRecipeContext, it applies just as much to a plain styled() wrapper with a non-matching name.

When the value is genuinely dynamic

Sometimes the variant value isn't known until runtime no matter what the component is named, for example a size prop threaded through from a parent your library doesn't control. No amount of tag or prop matching fixes that, Panda can't see a value it doesn't have. Pre-generate the variants you expect with staticCss instead. See Dynamic variant props for the pattern, and Ship the styled-system vs the CSS if you're deciding how that generated CSS reaches users in the first place.

Excluding false positives

The flip side: sometimes Panda tracks a prop you don't want it to, most often when wrapping a headless library whose props happen to share a name with a CSS property (Radix UI's Select.Content has a position prop that isn't a CSS position, for example). Excluding a specific tag or prop from extraction isn't configurable in the current engine — it's a known gap. If it causes a real problem, open an issue (opens in a new tab).