Build faster with Premium Chakra UI Components 💎
Learn moreNovember 19, 2025
Optimizing styling performance is crucial for maintaining smooth user experiences, especially in complex applications. This guide covers best practices for styling components in Chakra UI with a focus on performance.
Dynamic styling through inline CSS props can impact performance in several ways:
// ❌ Avoid: Dynamic styles recalculated on every render
const Component = ({ isActive, size }) => {
return (
<Box
padding={isActive ? "8" : "4"}
background={isActive ? "blue.500" : "gray.100"}
fontSize={size === "large" ? "xl" : "md"}
>
Content
</Box>
)
}Static styles are fine whether you write them as style props or in the css
prop. The performance concern is primarily about dynamic conditional style
values during render, not about style props in general.
// ✅ Fine: static style props
const Component = () => {
return (
<Box padding="8" background="blue.500" fontSize="lg">
Content
</Box>
)
}// ✅ Also fine: static css object
const Component = () => {
return (
<Box
css={{
padding: "8",
background: "blue.500",
fontSize: "lg",
}}
>
Content
</Box>
)
}Use whichever is more readable for your component. For simple static styles, style props are usually the clearest choice.
Data attributes provide a performant way to handle state-based styling without dynamic conditional style props. The styles are pre-compiled and don't change at runtime.
// ✅ Better: Keep static styles as style props, use css for selector logic
const Component = ({ isActive, isDisabled }) => {
return (
<Box
padding="4"
background="gray.100"
fontSize="md"
data-active={isActive || undefined}
data-disabled={isDisabled || undefined}
css={{
"&[data-active]": {
padding: "8",
background: "blue.500",
},
"&[data-disabled]": {
opacity: 0.5,
cursor: "not-allowed",
},
}}
>
Content
</Box>
)
}For truly dynamic values (like animations or user-controlled properties), use CSS variables instead of inline styles.
// ✅ Better: CSS variables for dynamic values
const ProgressBar = ({ progress }) => {
return (
<Box
style={{ "--progress": `${progress}%` }}
css={{
width: "100%",
height: "8px",
background: "gray.200",
"&::before": {
content: '""',
display: "block",
width: "var(--progress)",
height: "100%",
background: "blue.500",
transition: "width 0.3s ease",
},
}}
></Box>
)
}Recipes provide the most performant way to handle variant-based styling. All styles are pre-compiled and optimized at build time.
// ✅ Best: Use recipes for variants
import { createRecipeContext, defineRecipe } from "@chakra-ui/react"
const cardRecipe = defineRecipe({
className: "card",
base: {
padding: "4",
borderRadius: "md",
transition: "all 0.2s",
},
variants: {
variant: {
elevated: {
boxShadow: "md",
background: "white",
},
outline: {
borderWidth: "1px",
borderColor: "gray.200",
},
},
size: {
sm: { padding: "2" },
md: { padding: "4" },
lg: { padding: "6" },
},
isActive: {
true: {
borderColor: "blue.500",
background: "blue.50",
},
},
},
compoundVariants: [
{
variant: "elevated",
isActive: true,
css: {
boxShadow: "lg",
transform: "translateY(-2px)",
},
},
],
})
// Create component with recipe context
const { withContext } = createRecipeContext({ recipe: cardRecipe })
// Usage - variant props are passed directly
const Card = withContext<HTMLDivElement, CardProps>("div")
// In your app
const App = () => (
<Card variant="elevated" size="lg" isActive>
Content
</Card>
)For single dynamic values that don't fit into variants, use the style attribute sparingly with CSS variables.
// ✅ OK for one-off dynamic values
const CustomComponent = ({ rotation, scale }) => {
return (
<Box
transform="rotate(var(--rotation)) scale(var(--scale))"
transition="transform 0.3s ease"
style={{ "--rotation": `${rotation}deg`, "--scale": scale }}
>
Content
</Box>
)
}Before shipping your component, consider:
If your application requires absolute zero runtime CSS-in-JS overhead, consider using Panda CSS with Ark UI as an alternative stack:
Panda CSS is a build-time CSS-in-JS solution that
generates static CSS files with zero runtime overhead. You can use the
@chakra-ui/panda-preset to get all of Chakra UI's design tokens, recipes, and
patterns in a zero-runtime environment.
# Install dependencies
npm install -D @pandacss/dev @chakra-ui/panda-preset// panda.config.ts
import { defineConfig } from "@pandacss/dev"
export default defineConfig({
presets: ["@chakra-ui/panda-preset"],
include: ["./src/**/*.{js,jsx,ts,tsx}"],
outdir: "styled-system",
})// Use Chakra UI recipes with zero runtime
import { css } from "styled-system/css"
import { hstack, vstack } from "styled-system/patterns"
import { button, card } from "styled-system/recipes"
function App() {
return (
<div className={vstack({ gap: "4" })}>
<button className={button({ variant: "solid", size: "lg" })}>
Click me
</button>
<div className={card({ variant: "outline" })}>Card content</div>
</div>
)
}For unstyled, accessible components with zero runtime, combine Panda CSS with Ark UI:
import { Dialog } from "@ark-ui/react/dialog"
import { button, dialog } from "styled-system/recipes"
const styles = dialog({ size: "md" })
export const Modal = () => {
return (
<Dialog.Root>
<Dialog.Trigger className={button({ variant: "solid", size: "lg" })}>
Open
</Dialog.Trigger>
<Dialog.Positioner className={styles.positioner}>
<Dialog.Content className={styles.content}>
{/* Your content */}
</Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>
)
}By following these patterns, you'll create applications that are not only fast but also maintainable and scalable. The key is to move styling logic from runtime to build time whenever possible.