Want to skip the docs? Check out pandamastery.com - the best way to learn Panda CSS

design systems
setup

Set up a library package

Give your component library its own styled-system package so users share one runtime with your components.

Most component libraries built on Panda split into two packages: the components themselves, and a styled-system package that both the library and its users import. This page walks through creating that styled-system package. It's the setup step behind Ship the styled-system vs the CSS, Wrap headless UI, and forwardProps & the styled factory.

Why a dedicated package

If your library generates its own styled-system and an app using it generates a separate one, you end up with two copies of the same runtime code (css(), cva(), the JSX factory) in the final bundle, and no shared token contract between them. A dedicated workspace package fixes both: the library and the app import the exact same generated code, and the package's panda.config.ts becomes the one place your design tokens live.

Create the package

  1. Create a new directory, for example packages/styled-system.
  2. Install @pandacss/dev as a dev dependency.
  3. Run panda init to generate a panda.config.ts. Set jsxFramework if your components use JSX style props.
  4. If your library has custom tokens, recipes, or patterns, install and import your preset here too.
  5. Run panda emit-pkg to wire up the package's exports map.

The generated package.json looks roughly like this:

{
  "name": "@acme-org/styled-system",
  "version": "1.0.0",
  "exports": {
    "./css": {
      "types": "./css/index.d.ts",
      "require": "./css/index.mjs",
      "import": "./css/index.mjs"
    },
    "./tokens": {
      "types": "./tokens/index.d.ts",
      "require": "./tokens/index.mjs",
      "import": "./tokens/index.mjs"
    },
    "./types": {
      "types": "./types/index.d.ts",
      "require": "./types/index.mjs",
      "import": "./types/index.mjs"
    },
    "./patterns": {
      "types": "./patterns/index.d.ts",
      "require": "./patterns/index.mjs",
      "import": "./patterns/index.mjs"
    },
    "./recipes": {
      "types": "./recipes/index.d.ts",
      "require": "./recipes/index.mjs",
      "import": "./recipes/index.mjs"
    },
    "./jsx": {
      "types": "./jsx/index.d.ts",
      "require": "./jsx/index.mjs",
      "import": "./jsx/index.mjs"
    },
    "./styles.css": "./styles.css"
  },
  "devDependencies": {
    "@pandacss/dev": "^1.4.2",
    "@types/react": "19.2.2",
    "react": "^19.2.0"
  },
  "peerDependencies": {
    "react": ">=19"
  }
}

Keep react and @types/react in both devDependencies and peerDependencies so the package's own types resolve correctly while leaving the actual React instance to the user.

Point your library at it

In the component library's own panda.config.ts, set importMap to the package name and outdir to match:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  //...
  importMap: '@acme-org/styled-system',
  outdir: 'styled-system'
})

Now your library code imports from the shared package instead of a local styled-system:

import { css } from '@acme-org/styled-system/css'
 
export function Button({ children }) {
  return (
    <button type="button" className={css({ bg: 'red.300', px: '2', py: '3' })}>
      {children}
    </button>
  )
}

Mark the package as external in your build tool so its runtime code is bundled once, not duplicated into every library build:

tsup src/index.tsx --external @acme-org/styled-system

Point your app at it

Install the same package in the app using your library:

pnpm add @acme-org/styled-system

Set the same importMap there too, so Panda recognizes which imports belong to the shared package:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  //...
  importMap: '@acme-org/styled-system'
})

From here, the app and the library both call the same css, cva, and JSX factory functions under the hood, and share one token contract:

import { css } from '@acme-org/styled-system/css'
 
export function App() {
  return <div className={css({ padding: '4' })} />
}

Overriding tokens

An app using your library can still extend or override the shared tokens locally, without touching the package:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  //...
  presets: ['@acme-org/preset'],
  theme: {
    extend: {
      tokens: {
        colors: { primary: { value: 'blue.500' } }
      }
    }
  }
})

What's next

Once the package exists, you still need to tell Panda how to extract styles from your library's own source. See Ship the styled-system vs the CSS for the three ways to do that.

If this package is for internal use across several apps in the same monorepo rather than an externally published library, see Shared styled-system in a monorepo for wiring multiple consumers to it.