Concepts

Style props

Style props lets you quickly build UI components in JSX by passing css properties as "props" to your components. Panda will extract the style props through static analysis and generate the CSS at build time.

While you can get very far by using the className prop and function from Panda, style props provide a more ergonomic way of expressing styles.

If you use Chakra UI, Styled System, or Theme UI, you'll feel right at home right away 😊

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

Configure JSX

Using JSX style props is turned off by default in Panda, but you can opt-in to this feature by using the jsxFramework property in the panda config.

Choose Framework

JSX is a JavaScript syntax extension that allows you to write HTML-like code directly within your JavaScript code and is supported by most popular frameworks. Panda supports JSX style props in React, Preact, Vue 3, Qwik and Solid.js.

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

Generate JSX runtime

Next, you need to run panda codegen to generate the JSX runtime for your framework.

pnpm panda codegen --clean

That's it! You can now use JSX style props in your components.

Using Style Props

JSX Element

Style props are just CSS properties that you can pass to your components as props. With the JSX runtime, you can use styled.<element> syntax to create supercharged JSX elements that support 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>
)

Property Renaming

⚠️
Due to the static nature of Panda, you can't rename properties at runtime.
App.tsx
import { Circle, CircleProps } from '../styled-system/jsx'
 
type Props = {
  circleSize?: CircleProps['size']
}
 
const CustomCircle = (props: Props) => {
  const { circleSize = '3' } = props
  return (
    <Circle
      // ❌ Avoid: Panda can't know that you're mapping `circleSize` to `size`
      size={circleSize}
    />
  )
}
 
// ...
 
const App = () => {
  return (
    // In this case, you should keep the `size` naming
    <CustomCircle circleSize="4" />
  )
}

The same principles apply to all style props, recipe variants, and pattern props.

If you still need to rename properties at runtime, you can use config.staticCss as an escape-hatch to pre-generate the CSS anyway for the properties you need.

Recipe

You can use recipe variants as JSX props to quickly change the styles of your components, as long as you're tracking those components in your recipe config.

import { styled } from '../styled-system/jsx'
import { button, type ButtonVariantProps } from '../styled-system/recipes'
 
const Button = (props: ButtonVariantProps) => (
  <button className={button(props)}>
    Button
  </button>
)
 
const App = () => <Button variant="secondary">Button</Button>

Factory Function

You can also use the styled function to create a styled component from any component or JSX intrinsic element (like "a", "button").

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

You can configure the styled function name using the config.jsxFactory option.

Factory Recipe

You can define a recipe for your component using the styled function. This is useful when you want to create a component that has a specific set of style props.

import { styled } from '../styled-system/jsx'
 
const Button = styled('button', {
  base: {
    py: '2',
    px: '4',
    rounded: 'md'
  },
  variants: {
    variant: {
      primary: {
        bg: 'blue.500',
        color: 'white'
      },
      secondary: {
        bg: 'gray.500',
        color: 'white'
      }
    },
  }
})
 
const App = () => <Button variant="secondary" mt="10px">Button</Button>

Factory Options

There's a few options you can pass to the styled function to customize the behavior of the generated component.

interface FactoryOptions<TProps extends Dict> {
  dataAttr?: boolean
  defaultProps?: TProps
  shouldForwardProp?(prop: string, variantKeys: string[]): boolean
}

dataAttr

Setting dataAttr to true will add a data-recipe="{recipeName}" attribute to the element with the recipe name. This is useful for testing and debugging.

import { styled } from '../styled-system/jsx'
import { button } from '../styled-system/recipes'
 
const Button = styled('button', button, { dataAttr: true })
 
const App = () => <Button variant="secondary" mt="10px">Button</Button>
// Will render something like <button data-recipe="button" class="btn btn--variant_purple mt_10px">Button</button>

defaultProps

allows you to skip writing wrapper components just to set a few props. It also allows you to locall override the default variants or base styles of a recipe.

import { styled } from '../styled-system/jsx'
import { button } from '../styled-system/recipes'
 
const Button = styled('button', button, {
  defaultProps: {
    variant: 'secondary',
    px: '10px'
  }
})
 
const App = () => <Button>Button</Button>
// Will render something like <button class="btn btn--variant_secondary px_10px">Button</button>

shouldForwardProp

allows you to customize which props are forwarded to the underlying element. By default, all props except recipe variants and style props are forwarded.

For example you could use it to integrate with Framer Motion (opens in a new tab).

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

Reducing the allowed style props

You can reduce the allowed JSX properties on the factory using config.jsxStyleProps:

  • When set to 'all', all style props are allowed.
  • When set to 'minimal', only the css prop is allowed.
  • When set to 'none', no style props are allowed and therefore the jsxFactory will not be usable as a component:
    • <styled.div /> and styled("div") aren't valid
    • but the recipe usage is still valid styled("div", { base: { color: "red.300" }, variants: { ...} })
💡

Removing style props (from all to either minimal or none) will reduce the size of the generated code due to not having to check which props are style props at runtime.

JSX Patterns

Patterns are common layout patterns like stack, grid, circle that can be used to speed up your css. Think of them as a way to avoid repetitive layout styles.

All the patterns provided by Panda are available as JSX components.

💡

Learn more about the patterns we provide.

import { Stack, Circle } from '../styled-system/jsx'
 
const App = () => (
  <Stack gap="4" align="flex-start">
    <button>Button</button>
    <Circle size="4" bg="red.300">4</Circle>
  </Stack>
)

Making your own styled components

You can use the styled function to create your own styled components, using the splitCssProps function to split the style props from the rest of the props.

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

TypeScript

Panda provides type definitions for all the style props that are supported by the JSX runtime.

You can use these types to get type safety in your components.

Style Object

Use the JsxStyleProps to get the types of the style object that is compatible with JSX elements.

import { styled } from '../styled-system/jsx'
import type { JsxStyleProps } from '../styled-system/types'
 
type ButtonProps = {
  color?: JsxStyleProps['color']
}
 
const Button = (props: ButtonProps) => {
  return <styled.button {...props}>
}

Style Props

Use the HTMLStyledProps type to get the types of an element in addition to the style props.

import { styled } from '../styled-system/jsx'
import type { HTMLStyledProps } from '../styled-system/jsx'
 
type ButtonProps = HTMLStyledProps<'button'>
 
const Button = (props: ButtonProps) => {
  return <styled.button {...props}>
}

Variant Props

Use the StyledVariantProps type to extract the variants from 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

Every pattern provided by Panda has a corresponding type that you can use to get type safety in your components.

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