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.
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.
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.
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">
Button
</Button>
)
// => <button data-recipe="button" class="btn btn--variant_purple">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>
// => <button class="btn btn--variant_secondary px_10px">Button</button>
shouldForwardProp
Used 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 />
andstyled("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
To make a custom JSX component that accepts style props, Use the splitCssProps
function to split style props from other component props.
For this to work correctly, set the jsxFramework
to the framework you're using in your panda config.
import { splitCssProps, styled } 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} />
}
// Usage
function App() {
return <Component w="2">Click me</Component>
}
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'
interface 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'