Using Panda in a Component Library
How to build and ship a component library on Panda, from setup through packaging and troubleshooting.
A component library ships finished, styled components: a <Button>, a <Card>, a <Tabs>, ready to drop into any
project that installs it. That's different from shipping a design system as a preset, a pure config package with
no components at all, just the tokens and recipes another team builds their own components from. If you're not sure
which one you're building, the difference is what you're shipping: compiled components mean you're on this page;
pure definePreset config means you want Building a design system. If you
own the apps and want one package plus designSystem, use
Build a design system.
Mature libraries often ship both, a components package built on top of their own preset, so it's not an
either-or choice, just a question of which one you need first.
The rest of this section walks through building a component library on Panda, in the order you'll likely need it:
Quick answers
If you already know what you're looking for:
- Library code shouldn't be published, and the app uses Panda: ship the build info file. See Ship the styled-system vs the CSS.
- App code doesn't use Panda: ship a static CSS file. See Ship the styled-system vs the CSS.
- Library code lives in the same monorepo as the app: include the source files directly. See Ship the styled-system vs the CSS.
- Library only ships tokens, recipes, or patterns, no components: skip all of the above, ship a preset instead.
If you use the include-source-files or build-info approach and your library has custom tokens, recipes, or patterns, you'll likely still need to ship a preset alongside it.
FAQ
Why should my component library use an external styled-system package?
De-coupling the component library from its styled-system means your users share the same runtime code between
your library and their app, instead of bundling two copies of css(), cva(), and the JSX factory.
component-lib/src/button.tsx
import { css } from '@acme-org/styled-system/css'
export function Button({ children, css: cssProp }) {
return (
<button type="button" className={css({ bg: 'red.300', px: '2', py: '3' }, cssProp)}>
{children}
</button>
)
}app/src/App.tsx
import { Button } from '@acme-org/design-system'
import { css } from '@acme-org/styled-system/css'
export function App() {
return <Button css={{ color: 'white' }}>Click me</Button>
}Marking the styled-system package as external in your build tool keeps that runtime code, the css function in
the example above, imported once, not duplicated per package. See
Set up a library package for the full setup.
How do I use the shared styled-system package day to day?
Like any other workspace dependency: install it, then set importMap in panda.config.ts to the same package name
so Panda knows which imports belong to it.
pnpm add @acme-org/styled-system
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
//...
importMap: '@acme-org/styled-system'
})How do I override tokens used by the shared package?
Extend theme in your users' own panda.config.ts, same as overriding any preset's tokens:
panda.config.ts
import { defineConfig } from '@pandacss/dev'
export default defineConfig({
//...
presets: ['@acme-org/preset'],
theme: {
extend: {
tokens: {
colors: { primary: { value: 'blue.500' } }
}
}
}
})See Theme for the full set of things theme.extend can override, not just tokens.
See also
- Build a design system if you own the apps and want one package
plus
designSystem. - Building a design system if you're shipping a pure config package (tokens, recipes, no components) instead of compiled components.
- Consume a design system if you're adopting a package that already
ran
panda lib.