Consume a design system
Point an app at a Panda design system with one config field.
Install the package. Add one field:
panda.config.ts
export default defineConfig({
designSystem: '@acme/ds'
})Panda loads the package's theme, reuses the styles it already extracted, and recognizes imports from both @acme/ds
and your local styled-system. You don't add the preset, importMap, or the library's build info by hand.
If you're authoring the package, start at Build a design system.
Add the dependency
package.json
{
"dependencies": {
"@acme/ds": "workspace:*"
},
"devDependencies": {
"@pandacss/dev": "^2.0.0",
"@pandacss/vite": "^2.0.0"
}
}Use the published version on npm when the design system isn't in the same repo.
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
designSystem: '@acme/ds',
include: ['src/**/*.tsx'],
outdir: 'styled-system',
theme: {
extend: {
tokens: {
spacing: {
6: { value: '1.5rem' }
}
}
}
}
})theme.extend layers on top of the design system's theme. The app value wins when both sides define the same token
path.
Wire up the bundler
With Vite, add the @pandacss/vite plugin. It runs codegen and injects CSS. transform: true is optional: it inlines
static css() calls at build time. See the framework guides for other setups.
vite.config.ts
import pandacss from '@pandacss/vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [pandacss(), react()]
})If the app runs Panda through PostCSS instead, only the PostCSS config changes. designSystem, the entry CSS, and
the imports stay the same.
postcss.config.mjs
import pandacss from '@pandacss/postcss'
export default {
plugins: [pandacss()]
}The plugin regenerates styled-system on every build, but the folder has to exist the first time the bundler resolves
your imports. Run panda codegen (or panda build) in a predev/prebuild script so a fresh checkout builds — the
example apps (opens in a new tab) are wired this way. To emit CSS without the folder at all,
use panda cssgen.
Add the entry CSS
Panda emits into cascade layers. Declare their order once and import that file at the app root. The bundler plugin injects the generated CSS. This file only sets layer order.
src/index.css
@layer reset, base, tokens, recipes, utilities;src/main.tsx
import { createRoot } from 'react-dom/client'
import { App } from './app'
import './index.css'
createRoot(document.getElementById('root')!).render(<App />)Import components and css
Import components from the design system package. Import css from your local styled-system whenever the app
extends tokens, utilities, conditions, or breakpoints. That keeps css({ p: '6' }) typed against the merged theme.
src/app.tsx
import { Button } from '@acme/ds'
import { css } from '../styled-system/css'
export function App() {
return (
<main className={css({ color: 'brand', p: '6' })}>
<Button>Welcome</Button>
</main>
)
}An app that adds nothing of its own can import css from @acme/ds/css instead. Both paths extract.
What the app generates
Your local styled-system re-exports what the design system already shipped, and only generates what you added.
Authoring a field means you wrote it in this config (or a non-design-system preset), not that you inherited it.
| You author in the app | css(), cva(), cx | Recipes and patterns the design system already owns |
|---|---|---|
| Nothing | Re-export from @acme/ds | Re-export |
| Tokens, utilities, conditions, or breakpoints | Generated locally | Still re-exported |
prefix, hash, separator, jsxFramework, jsxStyleProps, or syntax | Full local tree | Full local tree |
| A nested design system (the package itself extends another) | Full local tree | Full local tree |
Add your own recipes
Author them under theme.extend. Import both your recipes and the design system's from the local styled-system.
panda.config.ts
export default defineConfig({
designSystem: '@acme/ds',
theme: {
extend: {
recipes: {
panel: {
className: 'panel',
base: { display: 'flex', flexDirection: 'column', gap: '3', p: '3' }
}
}
}
}
})src/app.tsx
import { Button } from '@acme/ds'
import { button, panel } from '../styled-system/recipes'
export function App() {
return (
<div className={panel()}>
<button className={button()}>Welcome</button>
</div>
)
}Override a design system value
Write your own definition of the token, recipe, or pattern. It merges over the original and wins.
- A token path both sides define reports
design_system_token_conflict(info). - A recipe or pattern both sides define reports
design_system_artifact_conflict(warning). The design system's copy drops out of the re-export in favor of yours.
Isolate styles with a prefix
Two Panda builds emit the same class names by default, like .button and --colors-brand. Set prefix when those
styles load in more than one independently-built bundle.
panda.config.ts
export default defineConfig({
designSystem: '@acme/ds',
prefix: 'app' // .button → .app-button, --colors-brand → --app-colors-brand
})Pick the prefix by the app's identity, not by design system version. A prefix that differs from the design system's regenerates the runtime locally.
Nested design systems
A design system can extend another. You still write one field, the leaf package:
export default defineConfig({
designSystem: '@acme/marketing-ds'
})
Panda walks the parent chain, merges presets root-first, and hydrates each layer. The app emits a full local
styled-system instead of re-exporting. Output is correct, just not deduped.
You cannot point designSystem at two unrelated packages. Put the second package in include if it only consumes
the first, or give it its own parent link if it is a real child design system.
Troubleshooting
design_system_manifest_not_found/design_system_manifest_not_exportedmeans the installed package has no./panda/*export. The author needs to runpanda liband republish. Then reinstall.design_system_export_missingmeans a styled-system subpath the app's generated files import (./css,./recipes, …) is missing from the package exports. Same fix: rebuild withpanda lib.design_system_peer_range_unsatisfiedmeans this app's Panda major doesn't match the design system's. Upgrade them together.design_system_buildinfo_stalemeans Panda re-extracted the design system's fallbackfilesbecausebuildinfo.jsonwas unusable. Styles stay correct. If the warning persists after a package update, the author needs to republish. If the build errors instead, the manifest has nofilesand recovery is disabled.design_system_token_conflict/design_system_artifact_conflictare expected when you override on purpose.
The example apps (opens in a new tab) show one design system shipped three ways: a monorepo
that builds the system and a Next.js app together, an app that installs the published CSS and components with no Panda,
and an app that runs Panda and extends the system's tokens. Grab any one with degit.