Recipe context
styled() plus a provider. Build a compound component around a single-element recipe.
createRecipeContext is styled() plus a provider. A parent sets variant props
once. Every withContext child picks them up. That is how you build a compound component around a single-element
recipe.
You need jsxFramework in your config, or styled-system/jsx is not
generated. This helper is a Client Component ("use client").
It works with cva, a config recipe, or an inline recipe definition.
import { cva } from '../styled-system/css'
import { createRecipeContext } from '../styled-system/jsx'
const buttonRecipe = cva({
base: { display: 'inline-flex' },
variants: {
size: {
sm: { px: '3' },
lg: { px: '5' }
}
}
})
const { withContext, PropsProvider } = createRecipeContext(buttonRecipe)
export const Button = withContext('button')<Button size="lg" type="submit" onClick={onSave}>
Save
</Button>size is the variant. type and onClick go to the <button>.
A config recipe is the same call with a different import:
import { button } from '../styled-system/recipes'
import { createRecipeContext } from '../styled-system/jsx'
const { withContext, PropsProvider } = createRecipeContext(button)withContext
withContext(
component, // 'button' or Link
options? // same as styled()
)options is the same object as styled().
Wrapping an element
One recipe can back more than one component. Button is a button. LinkButton is an a. Same variants.
export const Button = withContext('button')
export const LinkButton = withContext('a')<Button size="lg">Save</Button>
<LinkButton size="lg" href="/docs">
Docs
</LinkButton>For a config recipe, list both tags. See Config recipe.
Wrapping an external component
Pass the component you don't own.
import { ark } from '@ark-ui/react/factory'
import Link from 'next/link'
export const Button = withContext(ark.button, {
defaultProps: { type: 'button' }
})
export const LinkButton = withContext(Link)Default props
export const Button = withContext('button', {
defaultProps: {
type: 'button',
size: 'sm'
}
})Incoming props win over defaults, so <Button size="lg" /> still works.
unstyled
Skip the recipe classes. Style props, css, and className still apply. Same idea as
styled().
<Button unstyled css={{ bg: 'red.500' }}>
Click me
</Button>Sharing variant props
PropsProvider passes recipe props to every withContext child below it. It takes a value, so wrap it in your own
group component, like ButtonGroup, TagGroup, or AvatarGroup. Split the variant props off for the provider and
pass the rest to the element the group renders.
export const Button = withContext('button')
export function ButtonGroup(props) {
const [variantProps, restProps] = buttonRecipe.splitVariantProps(props)
return (
<PropsProvider value={variantProps}>
<div role="group" {...restProps} />
</PropsProvider>
)
}<ButtonGroup size="lg" visual="outline">
<Button>Save</Button>
<Button visual="solid">Cancel</Button>
</ButtonGroup>Save inherits size="lg" and visual="outline". Cancel keeps the size and overrides visual. Incoming props
win over the group.
Avatar group
The group can render a layout component instead of a div.
import { HStack } from '../styled-system/jsx'
const { withContext, PropsProvider } = createRecipeContext(avatar)
export const Avatar = withContext('div')
export function AvatarGroup(props) {
const [variantProps, restProps] = avatar.splitVariantProps(props)
return (
<PropsProvider value={variantProps}>
<HStack spaceX="-2" {...restProps} />
</PropsProvider>
)
}<AvatarGroup size="lg" px="4">
<Avatar />
<Avatar />
</AvatarGroup>size styles the avatars. px styles the stack.
Config recipe
Panda sees size on ButtonGroup, not on Button. A renamed export like LinkButton is the same miss. List every
tag that carries variant props, or the CSS is missing:
export const buttonRecipe = defineRecipe({
className: 'button',
jsx: ['Button', 'ButtonGroup', 'LinkButton']
})
export const LinkButton = withContext('a')cva already emits every variant, so the extra names do not matter there. The full tag rule is on
Tracking JSX. To skip listing tags on a config recipe,
pregenerate its variants with staticCss.