Skip to content

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

Customization

Patterns

Customize a built-in layout pattern, or define your own reusable layout abstraction.

Patterns are layout helpers like stack, flex, and grid, functions that return a style object instead of you writing one by hand. This page covers customizing the built-in ones and writing your own.

A pattern config takes:

  • description: shown in the generated JSDoc.
  • properties: the props the pattern accepts, and their type.
  • defaultValues: what a prop is when the caller omits it. Either a plain object, or a function of the other props, for defaults that depend on each other.
  • transform: takes the resolved props (plus a small set of helpers) and returns the style object to emit.
  • jsxName: the name of the generated JSX component. Defaults to the pascal-case of the pattern name.
  • jsx: which JSX tag names count as a usage of this pattern for extraction, an array of strings or regexes. This is a different thing from jsxName: it's about recognizing usage, not generating a component.
  • jsxElement: the actual DOM element the generated component renders. Defaults to 'div'.
  • blocklist: CSS properties the pattern rejects, enforced at the type level. Marked experimental in the type definitions, expect this one to still move.
  • strict: only type the properties you listed, nothing else. With strict: true, arbitrary CSS properties won't type-check on this pattern.

Creating a pattern

Say you want a scrollable pattern, a container with a fixed scroll direction and an optional hidden scrollbar:

const config = {
  patterns: {
    extend: {
      scrollable: {
        description: 'A container that allows for scrolling',
        defaultValues: {
          direction: 'vertical',
          hideScrollbar: true
        },
        properties: {
          direction: { type: 'enum', value: ['horizontal', 'vertical'] },
          hideScrollbar: { type: 'boolean' }
        },
        blocklist: ['overflow'], // reject `overflow` at the type level, since this pattern sets it
        transform(props) {
          const { direction, hideScrollbar, ...rest } = props
          return {
            overflow: 'auto',
            height: direction === 'horizontal' ? '100%' : 'auto',
            width: direction === 'vertical' ? '100%' : 'auto',
            scrollbarWidth: hideScrollbar ? 'none' : 'auto',
            WebkitOverflowScrolling: 'touch',
            '&::-webkit-scrollbar': {
              display: hideScrollbar ? 'none' : 'auto'
            },
            ...rest
          }
        }
      }
    }
  }
}

transform also receives a second argument with a few helpers (map, isCssUnit, isCssVar, isCssFunction) for patterns whose props need to branch on what kind of value they got:

transform(props, { isCssVar }) {
  // isCssVar(props.gap) is true for something like 'var(--my-gap)'
}

Run codegen so the pattern function and its types exist:

pnpm panda codegen
import { scrollable } from '../styled-system/patterns'
 
const App = () => {
  return (
    <div className={scrollable({ direction: 'vertical', hideScrollbar: true })}>
      <div>Scrollable content</div>
    </div>
  )
}

Customizing a built-in pattern

Extend a default pattern the same way, under patterns.extend. Here's flex narrowed to two directions, with a second component name recognized as a usage of it:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  patterns: {
    extend: {
      flex: {
        properties: {
          direction: { type: 'enum', value: ['row', 'column'] },
          jsx: ['Flex', 'CustomFlex'] // extraction now also matches <CustomFlex />
        }
      }
    }
  }
})

Changing a pattern's defaults

defaultValues in patterns.extend changes what a built-in pattern applies when a prop is omitted. Same shape for every pattern, only the pattern name and keys change.

@pandacss/preset-base sets gap: '8px' on stack. To default to a spacing token instead:

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  patterns: {
    extend: {
      stack: {
        defaultValues: {
          gap: '2' // same as <Stack gap="2" />
        }
      }
    }
  }
})

<Stack /> and stack({}) now default to spacing token 2. A gap passed at the call site still overrides it.

Values can be a token key ('2') or a raw literal ('12px'). stack, hstack, and vstack are separate patterns with their own defaultValues, so override each one if you want the same default gap everywhere.

Re-run codegen after changing defaults, same as any other config change:

pnpm panda codegen

Starting from zero

Everything here assumes you're extending Panda's defaults with patterns.extend. For no built-in patterns at all, see minimal setup.

Edit this page on GitHubView as markdown
Last updated on