Upgrading to v2
What changed in Panda v2, how to install the beta, and the breaking changes to fix as you migrate.
v2 is in beta (2.0.0-beta). The authoring API is stable, but the package layout and a few CLI surfaces are still
moving. Install it with the @beta tag; a plain install stays on v1.
v2 keeps the framework you already know and rewrites the compiler underneath it. You write the same css(), recipes,
patterns, tokens, conditions, and JSX props. What changed is how Panda turns them into CSS.
What v2 is
v1 ran extraction and evaluation through ts-morph and ts-evaluator in Node. v2 replaces that hot path with a native
engine built on Oxc (opens in a new tab), shipped two ways:
@pandacss/compiler— a native binding. The CLI and bundler plugins use it.@pandacss/compiler-wasm— the same engine compiled to WASM for the browser. The playground runs on it.
Both wrap the same Rust crates, so Node and browser builds produce the same CSS. You get faster extraction — one parse
per file, no TypeScript program in the hot path — and a smaller install, since the ts-morph dependency tree is gone.
Output stays in parity with v1 except for the deliberate changes below.
See The compiler engine for the pipeline, and How Panda works for the model it keeps.
Try the beta
Release channels
v1 and v2 ship side by side on npm. Install without a tag and you stay on stable v1.
| Channel | Version | Install |
|---|---|---|
latest | v1 (1.x) | @pandacss/dev |
beta | v2 (2.0.0-beta) | @pandacss/dev@beta |
All @pandacss/* packages move on one version. Don't mix a v1 package with a v2 one.
Install
Most projects only need @pandacss/dev:
pnpm add -D @pandacss/dev@beta
Add integrations on the same tag when you need them: @pandacss/postcss@beta, @pandacss/vite@beta,
@pandacss/webpack@beta, or @pandacss/rollup@beta.
v2 is ESM only and needs Node 22 or newer. Set "type": "module" (or use .mjs) so your panda.config.ts loads as ESM.
Want reproducible installs? Pin an exact version like @pandacss/dev@2.0.0-beta.14. @beta always resolves to the
newest pre-release.
Build
Your v1 panda.config.ts carries over. Regenerate:
panda build # codegen + cssgen in one pass
panda dev # rebuild on change
The panda and pandacss binaries are the same as v1.
Breaking changes to fix
These are the changes you have to act on when you move a project from v1.
ESM only
There's no CommonJS build. If your config or tooling used require():
// ❌ v1
const { defineConfig } = require('@pandacss/dev')
// ✅ v2
import { defineConfig } from '@pandacss/dev'
Set "type": "module", use .mjs, or run through an ESM-aware bundler. The postcss.config.cjs that
panda init --postcss writes is CommonJS on purpose and still works.
Hooks moved to plugins
Hooks still exist, but they live on named plugins now, not a root hooks object:
// ❌ v1
export default defineConfig({
hooks: {
'cssgen:done': ({ content }) => content,
},
})
// ✅ v2
export default defineConfig({
plugins: [
{
name: 'local',
hooks: {
'parser:before': {
filter: { id: '**/*.{jsx,tsx}' },
handler: ({ content }) => content,
},
},
},
],
})
Supported hooks: config:resolved, preset:resolved, parser:before, codegen:prepare, codegen:done, and
cssgen:done. cssgen:done is observe-only now — it runs after the final CSS with { artifact, content, path? } and
can't rewrite the string. Used it to strip unused tokens or keyframes? Reach for optimize.removeUnusedTokens /
removeUnusedKeyframes instead. The v1 engine hooks (context:created, parser:after, tokens:created,
utility:created, parser:before.configure(...), …) are gone. See Hooks.
createStyleContext is now two helpers
createStyleContext is gone from styled-system/jsx. Use one helper per recipe kind:
// ❌ v1 — one helper for both
import { createStyleContext } from 'styled-system/jsx'
// ✅ v2 — slot recipe (sva)
import { createSlotRecipeContext } from 'styled-system/jsx'
const { withRootProvider, withProvider, withContext } = createSlotRecipeContext(card)
// ✅ v2 — config recipe (cva)
import { createRecipeContext } from 'styled-system/jsx'
const { withContext } = createRecipeContext(button)
withRootProvider is new — use it for a slot recipe's root when the root doesn't render a slot of its own. See
JSX style context.
MCP moved to its own package
The MCP server left the CLI. Run it from @pandacss/mcp with the panda-mcp binary:
# ❌ v1
panda mcp
panda init-mcp
# ✅ v2 — run it directly, nothing to install
npx -y @pandacss/mcp
See MCP server.
Packages folded into the compiler
These v1 internals are gone; their work lives in @pandacss/compiler now. Drop direct imports of @pandacss/core,
@pandacss/extractor, @pandacss/generator, @pandacss/node, @pandacss/parser, @pandacss/token-dictionary,
@pandacss/is-valid-prop, @pandacss/logger, @pandacss/reporter, and the Astro @pandacss/studio. If you only use
@pandacss/dev plus Vite or PostCSS, you're fine.
Config options removed
Ten options are gone: studio (and its sub-options), eject, emitTokensOnly, gitignore, clean, watch, poll,
lightningcss, and browserslist. forceConsistentTypeExtension is replaced by forceImportExtension — different
semantics, not a rename. outExtension gains a 'ts' value. See Config.
CLI commands and flags
panda inspect, panda validate, and panda info are removed. Use panda doctor (add --json for scripts).
Logging flags are consolidated: --log-level silent|error|warn|info|debug replaces --silent, --quiet, and
--verbose. --profile replaces --cpu-prof and covers time in the Rust engine, not just the Node side. Shared flags
are kebab-case (--max-warnings, --watch-debounce, …). See CLI.
Border overrides sort by property, not source order
v2 orders atomic rules by property breadth, deterministically. All the border shorthands sit in one tier, so an all-sides shorthand always wins over a per-side one, no matter the merge order. Composing an all-sides border with a per-side override no longer opens that side:
// v1: renders an open bracket — the override was declared last
// v2: renders a closed box — borderWidth re-applies the inline-end side
cx(css({ borderWidth: '1px', borderStyle: 'solid' }), css({ borderInlineEnd: '0' }))
Reach for the longhand when the override has to win — longhands rank above every shorthand, so they always land last:
cx(css({ borderWidth: '1px', borderStyle: 'solid' }), css({ borderInlineEndWidth: '0' }))
Padding, margin, and every other property group sort the same way. See Border.
scrollbarWidth takes keywords, not tokens
v1 mapped scrollbarWidth to sizes tokens, so scrollbarWidth: '4' emitted var(--sizes-4) and browsers dropped it.
It's now auto | thin | none:
// ❌ v1 — type-checked, invalid CSS
css({ scrollbarWidth: '4' })
// ✅ v2
css({ scrollbarWidth: 'thin' })
If you passed a single color to scrollbarColor, move it to scrollbarThumb. scrollbarColor is now a raw two-value
string ('red transparent').
No universal variable reset
v1 seeded --translate-x, --blur, --gradient-from-position and friends through a *, ::before, ::after, ::backdrop
rule — 34 declarations on every element, used or not. v2 registers those variables with @property instead, so they
carry their own defaults and only ship when you use the utility. A page that uses none of them gets an empty base layer.
This needs @property (Chrome 85+, Safari 16.4+, Firefox 128+); older browsers drop the affected utilities rather than
mis-render them. Set optimize.propertyFallback: true to also seed the defaults as plain declarations for the variables
your project uses.
What's new you'll want
Beyond parity, v2 adds features worth turning on:
- The
optimizeblock. Opt-in CSS cleanup:removeUnusedTokens,removeUnusedKeyframes,smartCompoundVariants,treeshakeDesignSystem, andpropertyFallback. It replaces the common v1cssgen:donecleanup. - New utilities and conditions. Mask helpers (Masks), scrollbar utilities, pointer and
validity conditions (
_pointerFine,_userValid,_inert), and raw CSS keywords liketextWrap: 'pretty'andjustifyContent: 'safe center'. viewTransition(). Style the View Transitions API and get a stable class back. See View transitions.- Cross-file composition and source transforms. Compose
css.raw()styles across files, and let bundler plugins rewrite staticcss()calls withtransform: true. - Smaller
.d.ts.cva/svareturn types key on a clean props type, which unblocksisolatedDeclarations. See Isolated declarations. - Design systems.
panda libpublishes a component library; apps consume it with thedesignSystemconfig field. It replacespanda ship. See Building a design system and Consuming a design system. - Linting on the v2 engine. The ESLint & oxlint plugin lints against the same extraction the build uses.
Still being finalized
Honest gaps in the beta. Expect them to change before stable:
- Studio. The Astro-based
@pandacss/studiois gone. A lighter, CLI-generated studio is planned. See Studio in v2. - PostCSS plugin.
@pandacss/postcssv2 is experimental. If it misbehaves, use the Vite plugin orpanda build. - CSS minification.
minify: trueworks in the native emitter; full parity with the v1 LightningCSS path is still open. - Some presets and plugins. A few v1 community presets aren't ported yet. Check engine coverage before you rely on them.
See also
- The compiler engine — the Rust pipeline underneath v2.
- Config — the full v2 config reference.
- CLI — every command and flag.
- How Panda works — the mental model v2 keeps.