Skip to content

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

Customization

Presets

A shareable config of tokens, recipes, and utilities that other projects can install.

A preset is a Panda config packaged for reuse. List one in presets, and Panda merges it into your config before your own settings apply.

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  presets: ['@acme/preset']
})

Using a preset

Use a preset when tokens, recipes, or utilities need to exist in more than one place, and you are not shipping components.

  • A design language. Tokens, recipes, conditions. No components.
  • Multiple apps, one look. The same colors and spacing, defined once.

Shipping components plus that language is a design system. Apps that do not run Panda need a stylesheet, not a preset.

Importing a preset

A package name is enough. You can also import the object:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
import { acmePreset } from '@acme/preset'
 
export default defineConfig({
  presets: [acmePreset]
})

If the preset is a function, call it. A bare reference is not a preset:

panda.config.ts

import myPreset from './my-preset'
 
export default defineConfig({
  presets: [myPreset()]
})

Official presets

None are added for you. Panda includes exactly what's in your presets array. These are the ones we publish:

  • @pandacss/preset-base: default utilities, conditions, and patterns
  • @pandacss/preset-panda: default tokens, breakpoints, and text styles. See Default Theme
  • @pandacss/preset-typography: a prose recipe. See Typography

panda init lists the first two for you. Add your own after:

panda.config.ts

import basePreset from '@pandacss/preset-base'
import pandaPreset from '@pandacss/preset-panda'
import { acmePreset } from '@acme/preset'
import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  presets: [basePreset, pandaPreset, acmePreset]
})

For nothing at all, presets: []. See Minimal setup.

Extending a preset

Presets apply in the order you list them. Your config applies last, so it wins a conflict.

Without extend, what you write replaces the entry it names. Its siblings are untouched:

panda.config.ts

export default defineConfig({
  presets: ['@pandacss/preset-base', '@pandacss/preset-panda'],
  theme: {
    tokens: {
      colors: { brand: { value: '#EA8433' } }
    }
  }
})
// colors: brand. Every preset color is gone, other scales like spacing stay.

With extend, your values deep-merge on top:

panda.config.ts

export default defineConfig({
  presets: ['@pandacss/preset-base', '@pandacss/preset-panda'],
  theme: {
    extend: {
      tokens: {
        colors: { brand: { value: '#EA8433' } }
      }
    }
  }
})
// tokens: brand plus every preset color.

Same pattern on any section: theme.extend, utilities.extend, conditions.extend. Recipes live under theme.

Adding to a preset

Anything you put under extend is added. A new utility keeps every built-in one:

panda.config.ts

export default defineConfig({
  utilities: {
    extend: {
      br: {
        className: 'rounded',
        values: 'radii',
        transform: value => ({ borderRadius: value })
      }
    }
  }
})

Overriding part of a preset

The merge is deep, so you can change one token in a scale and keep the rest. blue.500 is yours. blue.50 through blue.950 stay:

panda.config.ts

export default defineConfig({
  presets: ['@pandacss/preset-base', '@pandacss/preset-panda'],
  theme: {
    extend: {
      tokens: {
        colors: {
          blue: {
            500: { value: '#2563eb' }
          }
        }
      }
    }
  }
})

Removing something from a preset

extend only adds, so removing takes a plugin. config:resolved runs after every preset has merged:

panda.config.ts

import { defineConfig, definePlugin } from '@pandacss/dev'
 
const removeStack = definePlugin({
  name: 'remove-stack',
  hooks: {
    'config:resolved': ({ config, utils }) => utils.omit(config, ['patterns.stack'])
  }
})
 
export default defineConfig({
  presets: ['@pandacss/preset-base', '@pandacss/preset-panda'],
  plugins: [removeStack]
})

For a whole section, drop the preset and spread back only the parts you want:

panda.config.ts

import base from '@pandacss/preset-base'
 
const { stack, ...patterns } = base.patterns
 
export default defineConfig({
  presets: ['@pandacss/preset-panda'],
  patterns: { extend: patterns }
})

To start with nothing at all, see Minimal setup.

Common mistakes

Forgetting extend. The symptom is every preset token or utility disappearing at once.

// ❌ replaces every color
theme: {
  tokens: {
    colors: {
      brand: {
        value: '#EA8433'
      }
    }
  }
}
 
// ✅ adds brand to the palette
theme: {
  extend: {
    tokens: {
      colors: {
        brand: {
          value: '#EA8433'
        }
      }
    }
  }
}

Skipping the category. Tokens live under tokens or semanticTokens, not directly under extend.

// ❌ ignored
theme: {
  extend: {
    colors: {
      brand: {
        value: '#EA8433'
      }
    }
  }
}
 
// ✅
theme: {
  extend: {
    tokens: {
      colors: {
        brand: {
          value: '#EA8433'
        }
      }
    }
  }
}

Putting extend at the root. It belongs inside each section.

// ❌ not a config key
extend: {
  theme: {
    tokens: {
    }
  }
}
 
// ✅
theme: {
  extend: {
    tokens: {
    }
  }
}

Expecting a later preset to merge on its own. Presets apply in order and your config applies last, but a preset without extend replaces the sections it defines, exactly like your config would. Author presets with extend when they build on another preset.

Creating a preset

A preset is a valid Panda config object with a unique name. Wrap it in definePreset for types.

my-preset.ts

import { definePreset } from '@pandacss/dev'
 
export default definePreset({
  name: 'my-preset',
  theme: {
    tokens: {
      colors: {
        brand: { value: '#2563eb' }
      }
    }
  }
})

List it like any other preset:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
import myPreset from './my-preset'
 
export default defineConfig({
  presets: [myPreset]
})

A preset can define any of these keys:

If this preset sits on top of another one, put shared values under extend. Without it, you replace that section for every app that lists you:

export const acmePreset = definePreset({
  name: '@acme/preset',
  theme: {
    extend: {
      tokens: {
        colors: {
          brand: { value: '#2563eb' }
        }
      }
    }
  }
})

Publishing a package

Export the preset from the package entry, then point package.json at the build:

@acme/preset/package.json

{
  "name": "@acme/preset",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  }
}
pnpm tsup src/index.ts --format esm,cjs --dts
pnpm publish

Apps install it and list it the same way as using a preset.

Using an async preset

Return a promise when you need to await something, like a design-tool API. Panda resolves it before merging:

my-preset.ts

export default async function myPreset() {
  const roseColors = await getRoseColors()
 
  return definePreset({
    name: 'my-preset',
    theme: {
      tokens: {
        colors: {
          rose: roseColors
        }
      }
    }
  })
}

The app must call it:

panda.config.ts

export default defineConfig({
  presets: [myPreset()]
})

A preset is config only. No components, no styled-system. If you ship both, that's a design system.

Edit this page on GitHubView as markdown
Last updated on