Optimization
Shrink the CSS Panda emits. Every optimize flag is opt-in.
Panda already extracts only the styles it finds in your source. The optimize block goes further: unused tokens,
unused keyframes, unused compound variants, unused design-system modules.
Nothing here changes how you write styles. All of it is off by default.
CSS cleanup
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
optimize: {
removeUnusedTokens: true,
removeUnusedKeyframes: true,
smartCompoundVariants: true,
treeshakeDesignSystem: true
}
})Unused tokens
By default the tokens layer ships every --* from your theme. With removeUnusedTokens, a variable stays only if
something uses it: extracted styles, recipes, staticCss, globalCss, and token() / token.var() calls Panda can
see.
panda.config.ts
export default defineConfig({
optimize: { removeUnusedTokens: true }
})import { token } from 'styled-system/tokens'
// ✅ Panda sees this — `--colors-red` stays
export const brand = token.var('colors.red')
// ❌ Built at runtime — invisible; use staticCss or leave the flag off
export const dynamic = token.var(path)staticCss.themes always keeps those theme variables, so a themed sheet doesn't get pruned empty.
Unused keyframes
Same idea for @keyframes. A name stays if animation or animation-name references it.
Compound variants
Once a recipe is used, Panda emits every compound variant for that recipe. smartCompoundVariants emits only the
compounds that match the variant combinations it extracted.
panda.config.ts
export default defineConfig({
optimize: { smartCompoundVariants: true },
// Runtime-only variants need an explicit list (one variant key per rule)
staticCss: {
recipes: {
button: [{ size: ['sm', 'md'] }, { variant: ['solid'] }]
}
}
})See Static CSS → Recipes.
Design systems
When you consume a library with designSystem, Panda hydrates every module from its build info.
treeshakeDesignSystem scans your include files and hydrates only the exports you import.
panda.config.ts
export default defineConfig({
designSystem: '@acme/ds',
optimize: { treeshakeDesignSystem: true }
})import * as DS from '@acme/ds', a side-effect import, or import() still hydrates everything. If the app imports
nothing from the package, nothing hydrates. See
Consume with Panda.
Older browsers
This is compatibility, not size. v2 registers transform and filter variables with @property instead of a reset on
every element. Safari < 16.4 and Firefox < 128 ignore @property and drop those utilities.
panda.config.ts
export default defineConfig({
optimize: { propertyFallback: true }
})That also writes the defaults as plain declarations, for the variables your project uses.
Profile the build
Don't guess. Add --profile to any Panda command:
panda --profileThat writes .panda/trace.json (open it in chrome://tracing or ui.perfetto.dev (opens in a new tab)) and a
.panda/timings.json summary. See Debugging.
Narrow include
Extraction time scales with how many files include matches. A file with no Panda
import is cheap after parse, but it still gets parsed. Keep the glob tight: skip tests, Storybook, and generated output
unless they call css() themselves.
panda.config.ts
export default defineConfig({
include: ['./src/**/*.{ts,tsx}'],
exclude: ['./src/**/*.stories.tsx', './src/**/*.test.tsx']
})panda debug lists every file a run scanned.
Less staticCss
staticCss is the escape hatch for values Panda can't extract. It generates those classes
unconditionally, so a wide * list grows both CSS and build time. Generate only the utilities and recipes you need.
panda.config.ts
export default defineConfig({
// ❌ Ships every color utility
// staticCss: { css: [{ properties: { color: ['*'] } }] }
// ✅ Only the values you can't extract
staticCss: {
css: [{ properties: { color: ['red.500', 'blue.500'] } }]
}
})Smaller JS bundle
The styling runtime (css(), cva(), the JSX factory) ships as a small fixed cost. The bundler plugins can
rewrite static calls to plain class strings so it drops out entirely. See
Source transforms.
Minify
minify: true strips whitespace from the emitted CSS. Native minify is not yet a full LightningCSS replacement — check
the result if you depended on that last few percent. Often enabled only in production. See
Environment-specific config.
panda.config.ts
export default defineConfig({
minify: process.env.NODE_ENV === 'production'
})Editor speed
If the IDE lags only inside css() calls or style props, that's TypeScript checking generated unions, not Panda
running.
strictTokens is the knob. Off, properties accept a token or a
string. On, they accept only the token union — safer, and a larger type for TypeScript to check.
panda.config.ts
export default defineConfig({
strictTokens: true
})Try turning it off before assuming something else is wrong.
See also
- Config → optimize for the full option list.
- Static CSS Generation for pre-generating what extraction can't see.
- Debugging for
--profileandpanda debug.