Extraction rules
Panda reads the source as written. The tag, the prop, and the value have to be names it already knows.
Panda's compiler matches the names in your source. The tag, the prop, and the value have to be names it already knows.
The usual miss:
- You rename
ButtontoRandom - You rename
sizetocircleSize - You pass a value from state
The class shows up. The CSS does not.
Matching the tag
<Button size="lg" /> only emits the button recipe if the tag is Button, or a name you listed in jsx.
export function Random(props) {
return <button className={button(props)} />
}
<Random size="lg" />Panda sees Random, not Button. Export it as Button, set jsx: ['Random'], or
pregenerate its variants with staticCss. That is
Tracking JSX.
cva and sva do not need a matching tag. Defining them already emits every variant.
Choosing which components Panda reads
Panda reads style props from every capitalized component, so your own wrappers like <Card px="4"> work. A prop whose
value can't be a style, such as columns={[{ header: 'Name' }]}, is skipped.
Use jsxMatchTag to skip components, or to limit the props Panda reads from them. The last rule that matches a
component wins.
export default defineConfig({
jsxMatchTag: [
// Never read props from a third-party package
{ from: '@tanstack/react-table', ignore: true },
// Read only `color` from components ending in Button
{ tag: /Button$/, props: ['color'] },
],
})tagmatches the name as written, such asButtonorUI.Button.frommatches the import path as written. Aliases and re-exports aren't followed.- A rule needs
tag,from, or both.
Matching the prop
Extract reads the prop on the tag. It does not follow a rename.
Style props
There is no config that turns elementWidth into width.
function Carousel({ elementWidth, ...props }) {
return <styled.div width={elementWidth} {...props} />
}
<Carousel elementWidth="300px" />Panda sees elementWidth, not width. Use a utility name, css, or a *Css prop at the call site:
<Carousel width="300px" />
<Carousel css={{ width: '300px' }} />
<Carousel trackCss={{ width: '300px' }} />Extract reads CSS utilities when jsxStyleProps is 'all', plus
css and props that end in Css. For any other name, wrap the object in .raw() so the call site is visible:
<Card style={css.raw({ bg: 'blue' })} />See passing styles to custom components.
Recipe and pattern props
circleSize is not size. buttonSize is not size.
function CustomCircle({ circleSize = '3', ...props }) {
return <Circle size={circleSize} {...props} />
}
<CustomCircle circleSize="4" />function Button({ buttonSize, children }) {
return <button className={button({ size: buttonSize })}>{children}</button>
}
<Button buttonSize="lg" />Panda sees circleSize / buttonSize, not size. Keep the key at the call site.
cva and sva already emit every variant, so a rename there is a value problem, not a missing class. See
Matching the value.
Matching the value
A runtime value is invisible, even when the tag and prop are right.
function Button({ size, ...props }) {
return <button className={button({ size })} {...props} />
}
<Button size={size} />The wrapper looks correct. Panda sees size={size}, not size="lg". You get defaultVariants only.
<Button size="lg" />Pre-generate with staticCss, or put a literal at the call site.
See Dynamic styles and Dynamic variants.
Composition patterns
Expose a name extract already knows, and merge inside the wrapper. Don't invent elementWidth.
Using css and *Css
The wrapper takes a style object. Extract reads css and any prop that ends in Css at the call site.
function Card({ css: cssProp, ...props }) {
return <div className={css({ bg: 'red' }, cssProp)} {...props} />
}
<Card css={{ bg: 'blue' }} />Named parts use rootCss, bodyCss, and so on. Same extract rule, one object per slot.
function Card({ rootCss, bodyCss, ...props }) {
return (
<div className={css({ bg: 'red' }, rootCss)} {...props}>
<p className={css(bodyCss)} />
</div>
)
}
<Card rootCss={{ bg: 'blue' }} bodyCss={{ color: 'white' }} />Using className
The consumer already has a class. The wrapper only concatenates.
function Card({ className, ...props }) {
return <div className={cx(card(), className)} {...props} />
}
<Card className={css({ bg: 'blue' })} />Panda sees the css() at the call site. The wrapper never has to know bg.
Use css / *Css when the wrapper should merge style objects. Use className when the consumer already has a class
(a recipe, css(), or something external).
See Merging styles.