Skip to content

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

Style props

Pass CSS properties as props on styled.* elements. Same atomic CSS as css(), different authoring surface.

Style props are CSS properties on a JSX element. bg="blue.500" is the same atomic CSS as css({ bg: 'blue.500' }).

import { css } from '../styled-system/css'
import { styled } from '../styled-system/jsx'
 
// className
<button className={css({ bg: 'blue.500', color: 'white', py: '2', px: '4' })} />
 
// style props
<styled.button bg="blue.500" color="white" py="2" px="4" />

css, as, and className are factory props. Variants on styled() are Styled factory.

You need jsxFramework in your config, or styled-system/jsx is not generated.

Configure JSX

panda.config.ts

import { defineConfig } from '@pandacss/dev'
 
export default defineConfig({
  jsxFramework: 'react'
})

Panda supports React, Preact, Vue 3, Qwik, and Solid. Then run panda codegen.

You can rename the factory with jsxFactory.

Using style props

import { styled } from '../styled-system/jsx'
 
const Button = ({ children }) => (
  <styled.button bg="blue.500" color="white" py="2" px="4" rounded="md">
    {children}
  </styled.button>
)

styled.button is a shorthand for styled('button') when you only need style props.

Factory props

Besides bg and px, the factory reads a few props itself. They are not CSS utilities.

css

An extra style object, merged last among styles. Use it for conditions, selectors, or a style you already have as an object.

<styled.button
  bg="blue.500"
  css={{
    _hover: { bg: 'blue.600' },
    '&[data-active]': { bg: 'blue.700' }
  }}
>
  Click me
</styled.button>

One override on a custom component is also css. Named parts use trackCss, slideCss, and so on. See passing styles to custom components.

className

Appended after the factory classes. Use it for a class you do not control, or a string from outside Panda.

<styled.button bg="blue.500" className="external">
  Click me
</styled.button>

as

Render a different element. Style props still apply.

<styled.button as="a" href="https://panda-css.com" bg="blue.500">
  Docs
</styled.button>

HTML attributes

width, height, translate, and content are style props, so they do not reach the DOM. Use the html* aliases when you need the HTML attribute.

<styled.img htmlWidth="40" htmlHeight="40" src="/logo.png" alt="" />
PropLands on the DOM as
htmlWidthwidth
htmlHeightheight
htmlTranslatetranslate
htmlContentcontent

Property names

Extract reads the prop on the tag. circleSize is not size. elementWidth is not width. See Extraction rules.

styled()

styled() wraps any element or component. The result accepts style props.

import { styled } from '../styled-system/jsx'
import { Button } from 'component-library'
 
const StyledButton = styled(Button)
 
<StyledButton bg="blue.500" color="white" py="2" px="4">
  Button
</StyledButton>

To pass a recipe as the second argument, use Styled factory.

Factory options

The third argument is optional.

interface FactoryOptions<TProps> {
  dataAttr?: boolean
  defaultProps?: TProps
  shouldForwardProp?(prop: string, variantKeys: string[]): boolean
  forwardProps?: string[]
}
OptionWhat it does
dataAttrSets data-recipe={name} when the recipe has a name
defaultPropsSit under incoming props, so a caller can still override them
shouldForwardPropDecides what reaches the DOM. By default, variants and CSS properties are not forwarded
forwardPropsAllowlist on top of shouldForwardProp. A forwarded prop no longer feeds the recipe
const Input = styled('input', {}, { forwardProps: ['size'] })
 
<Input size={4} />

size becomes an HTML attribute. It does not feed a recipe. To keep a key as a variant and pass it through, use withProvider.

shouldForwardProp

Use a function when the list is not fixed, for example Framer Motion:

import { styled } from '../styled-system/jsx'
import { motion, isValidMotionProp } from 'framer-motion'
 
const StyledMotion = styled(
  motion.div,
  {},
  {
    shouldForwardProp: (prop, variantKeys) =>
      isValidMotionProp(prop) || (!variantKeys.includes(prop) && !isCssProperty(prop))
  }
)

Recipe defaults and unstyled are on Styled factory.

Restricting style props

jsxStyleProps controls which props the factory treats as styles. Default is all. Tighten it when you do not want bg on every component, or when you want a smaller JSX runtime.

panda.config.ts

export default defineConfig({
  jsxStyleProps: 'minimal'
})

all

Every style prop. This is the default.

<styled.button bg="blue.500" px="4">
  Click me
</styled.button>

minimal

The css prop and props that end in Css. bg and px no longer work.

<styled.button css={{ bg: 'blue.500', px: '4' }}>Click me</styled.button>
<Carousel trackCss={{ width: '300px' }} />

none

Style props are off. <styled.div /> and styled('div') are invalid. css() and a recipe on styled() still work.

<button className={css({ bg: 'blue.500' })}>Click me</button>
 
const Button = styled('button', {
  base: { bg: 'blue.500' }
})

Making your own styled components

splitCssProps pulls style props off a hand-written component.

import { css } from '../styled-system/css'
import { splitCssProps } from '../styled-system/jsx'
import type { HTMLStyledProps } from '../styled-system/types'
 
export function Component(props: HTMLStyledProps<'div'>) {
  const [cssProps, restProps] = splitCssProps(props)
  const { css: cssProp, ...styleProps } = cssProps
 
  const className = css({ display: 'flex', height: '20', width: '20' }, styleProps, cssProp)
 
  return <div {...restProps} className={className} />
}
 
<Component w="2">Click me</Component>

TypeScript

Style object

JsxStyleProps is the style-object shape.

import type { JsxStyleProps } from '../styled-system/types'
 
interface ButtonProps {
  color?: JsxStyleProps['color']
}

Style props

HTMLStyledProps is the element plus style props.

import type { HTMLStyledProps } from '../styled-system/jsx'
 
type ButtonProps = HTMLStyledProps<'button'>

Variant props

StyledVariantProps reads variants off a styled() component.

import { styled } from '../styled-system/jsx'
import type { StyledVariantProps } from '../styled-system/jsx'
 
const Button = styled('button', {
  base: { color: 'black' },
  variants: {
    state: {
      error: { color: 'red' },
      success: { color: 'green' }
    }
  }
})
 
type ButtonVariantProps = StyledVariantProps<typeof Button>
//   { state?: 'error' | 'success' | undefined }

Patterns

Each pattern has a matching props type.

import type { StackProps } from '../styled-system/jsx'

See also

Edit this page on GitHubView as markdown
Last updated on