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 }
})Nested design systems are narrowed through the parent chain too. If a selected child module depends on a parent component, Panda keeps the parent CSS it needs.
import * as DS from '@acme/ds', a side-effect import, or a dynamic import() in the app still hydrates every module
from that package. A namespace or side-effect import inside a selected design-system module keeps all modules from that
parent. If the app imports nothing from the package, nothing is hydrated.
When a selected intermediate package has build info from before parent imports were recorded, Panda hydrates the full
parent rather than risk missing CSS. Run panda lib in that package to restore narrowing. See
Consume with Panda for the full setup.
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. Enable it in production only:
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.