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: aproserecipe. 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 publishApps 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.