Fix: Panda CSS Not Working — Styles Not Applying, Tokens Not Resolving, or Build Errors
Part of: React & Frontend Errors
Quick Answer
How to fix Panda CSS issues — PostCSS setup, panda.config.ts token system, recipe and pattern definitions, conditional styles, responsive design, and integration with Next.js and Vite.
The Problem
You write Panda CSS styles but nothing appears:
import { css } from '../styled-system/css';
function Button() {
return (
<button className={css({ bg: 'blue.500', color: 'white', p: '4' })}>
Click me
</button>
);
}
// Button renders with no styles — plain unstyled HTMLOr a token value doesn’t resolve:
css({ color: 'primary' })
// TypeScript error: Type '"primary"' is not assignable to type...Or the build fails:
Error: Cannot find module '../styled-system/css'Why This Happens
Panda CSS is a build-time CSS-in-JS library. It scans your source files at build time, extracts style function calls, and generates a static CSS file. No runtime JavaScript ships to the browser, which is why Panda is positioned as a successor to runtime CSS-in-JS libraries that struggle inside React Server Components.
The core mental model trips people up because Panda looks like a runtime library at the call site but behaves like a build tool at compile time. Every css({ ... }) call is a pure data extraction target — the bundler reads the literal object, hashes it, and outputs a class. If the object shape can’t be statically read (dynamic keys, computed values, spread from a function), Panda silently skips it and the rule never reaches the stylesheet. That’s why most “styles not applying” cases trace back to either a missing codegen step, a file outside the scan glob, or a value that looks fine at runtime but isn’t statically analyzable.
The platform layer adds a second class of failures. Each bundler treats PostCSS differently — Vite runs PostCSS through its own plugin chain, Next.js App Router runs it inside the SWC pipeline before RSC boundaries, and Astro applies it per island. Misconfigured loaders or duplicated PostCSS configs (one at the root, one inside a monorepo package) lead to partial extraction, ghost CSS, or builds that work locally but ship empty styles to production.
- The
styled-systemdirectory must be generated first — runningnpx panda codegencreates thestyled-system/directory withcss,cva,sva, and other utilities. Without this step, imports fail. The codegen must re-run whenever you changepanda.config.ts. - Panda scans specific file paths — the
includearray inpanda.config.tstells Panda which files to scan for style usage. Files outside this glob pattern are invisible to Panda, and their styles won’t appear in the generated CSS. - Token values must exist in the theme —
color: 'blue.500'works becauseblue.500is a default token.color: 'primary'only works if you defineprimaryin thetheme.tokensortheme.semanticTokensconfig. Unrecognized values cause type errors. - Generated CSS must be imported — Panda generates a CSS file that you must import in your app’s entry point. Without this import, the generated styles exist but aren’t loaded.
Fix 1: Set Up Panda CSS
npm install -D @pandacss/dev
npx panda init// panda.config.ts
import { defineConfig } from '@pandacss/dev';
export default defineConfig({
// Enable CSS reset
preflight: true,
// Files to scan for styles
include: [
'./src/**/*.{ts,tsx,js,jsx}',
'./app/**/*.{ts,tsx,js,jsx}', // Next.js App Router
'./pages/**/*.{ts,tsx,js,jsx}', // Next.js Pages Router
'./components/**/*.{ts,tsx,js,jsx}',
],
// Files to exclude
exclude: [],
// Theme customization
theme: {
extend: {
tokens: {
colors: {
primary: { value: '#3b82f6' },
secondary: { value: '#64748b' },
},
fonts: {
body: { value: 'Inter, sans-serif' },
heading: { value: 'Cal Sans, sans-serif' },
},
},
semanticTokens: {
colors: {
// Tokens that change with color mode
text: {
value: { base: '{colors.gray.900}', _dark: '{colors.gray.100}' },
},
bg: {
value: { base: 'white', _dark: '{colors.gray.900}' },
},
accent: {
value: { base: '{colors.primary}', _dark: '{colors.blue.400}' },
},
},
},
},
},
// Output directory for generated code
outdir: 'styled-system',
// JSX framework
jsxFramework: 'react', // 'react' | 'solid' | 'vue' | 'qwik'
});# Generate the styled-system directory
npx panda codegen
# Add to package.json scripts
# "prepare": "panda codegen"// postcss.config.mjs — required
export default {
plugins: {
'@pandacss/dev/postcss': {},
},
};/* src/index.css or app/globals.css — import generated styles */
@layer reset, base, tokens, recipes, utilities;// Import CSS in your entry point
// app/layout.tsx (Next.js) or src/main.tsx (Vite)
import './globals.css';Fix 2: Use the css() Function and Utilities
import { css } from '../styled-system/css';
import { flex, center, stack, grid } from '../styled-system/patterns';
// Basic styles
function Card() {
return (
<div
className={css({
bg: 'white',
borderRadius: 'lg',
boxShadow: 'md',
p: '6',
// Shorthand properties
mx: 'auto', // marginInline
mt: '4', // marginTop
w: 'full', // width
maxW: 'lg', // maxWidth
})}
>
<h2 className={css({
fontSize: '2xl',
fontWeight: 'bold',
color: 'gray.900',
mb: '2',
})}>
Card Title
</h2>
<p className={css({ color: 'gray.600', lineHeight: 'relaxed' })}>
Card content here.
</p>
</div>
);
}
// Responsive styles — mobile-first breakpoints
function ResponsiveLayout() {
return (
<div
className={css({
display: 'flex',
flexDirection: 'column',
gap: '4',
// Responsive: sm, md, lg, xl, 2xl
md: {
flexDirection: 'row',
gap: '8',
},
lg: {
maxW: '6xl',
mx: 'auto',
},
})}
>
<aside className={css({ w: { base: 'full', md: '1/4' } })}>Sidebar</aside>
<main className={css({ w: { base: 'full', md: '3/4' } })}>Content</main>
</div>
);
}
// Patterns — pre-built layout utilities
function PatternExamples() {
return (
<>
{/* Flex layout */}
<div className={flex({ gap: '4', align: 'center', justify: 'between' })}>
<span>Left</span>
<span>Right</span>
</div>
{/* Center content */}
<div className={center({ h: '64' })}>
<p>Centered vertically and horizontally</p>
</div>
{/* Stack (vertical flex) */}
<div className={stack({ gap: '4', direction: 'column' })}>
<div>Item 1</div>
<div>Item 2</div>
</div>
{/* Grid */}
<div className={grid({ columns: { base: 1, md: 2, lg: 3 }, gap: '6' })}>
<div>Cell 1</div>
<div>Cell 2</div>
<div>Cell 3</div>
</div>
</>
);
}Fix 3: Recipes — Reusable Component Variants
// styled-system recipes (defined in panda.config.ts)
// panda.config.ts
import { defineConfig } from '@pandacss/dev';
export default defineConfig({
theme: {
extend: {
recipes: {
button: {
className: 'button',
description: 'Button component styles',
base: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 'md',
fontWeight: 'semibold',
cursor: 'pointer',
transition: 'all 0.2s',
_disabled: {
opacity: '0.5',
cursor: 'not-allowed',
},
},
variants: {
variant: {
solid: {
bg: 'primary',
color: 'white',
_hover: { bg: 'blue.600' },
},
outline: {
border: '2px solid',
borderColor: 'primary',
color: 'primary',
_hover: { bg: 'blue.50' },
},
ghost: {
color: 'primary',
_hover: { bg: 'blue.50' },
},
},
size: {
sm: { px: '3', py: '1.5', fontSize: 'sm' },
md: { px: '4', py: '2', fontSize: 'md' },
lg: { px: '6', py: '3', fontSize: 'lg' },
},
},
defaultVariants: {
variant: 'solid',
size: 'md',
},
},
},
},
},
});// Usage after running npx panda codegen
import { button } from '../styled-system/recipes';
function Button({ variant, size, children, ...props }) {
return (
<button className={button({ variant, size })} {...props}>
{children}
</button>
);
}
// Or use cva() for inline recipe definitions
import { cva } from '../styled-system/css';
const badge = cva({
base: {
px: '2',
py: '0.5',
borderRadius: 'full',
fontSize: 'xs',
fontWeight: 'bold',
},
variants: {
status: {
success: { bg: 'green.100', color: 'green.800' },
warning: { bg: 'yellow.100', color: 'yellow.800' },
error: { bg: 'red.100', color: 'red.800' },
},
},
});
<span className={badge({ status: 'success' })}>Active</span>Fix 4: Conditional and Dynamic Styles
import { css, cx } from '../styled-system/css';
// Conditional styles — use conditions (pseudo-classes, states)
function InteractiveCard() {
return (
<div
className={css({
bg: 'white',
p: '4',
borderRadius: 'lg',
border: '1px solid',
borderColor: 'gray.200',
transition: 'all 0.2s',
// Hover state
_hover: {
borderColor: 'primary',
boxShadow: 'lg',
transform: 'translateY(-2px)',
},
// Focus-within (when child is focused)
_focusWithin: {
borderColor: 'primary',
ring: '2',
ringColor: 'blue.200',
},
// Dark mode
_dark: {
bg: 'gray.800',
borderColor: 'gray.700',
},
// First/last child
_first: { mt: '0' },
_last: { mb: '0' },
})}
>
Interactive card
</div>
);
}
// Dynamic styles based on props
function Alert({ type }: { type: 'info' | 'warning' | 'error' }) {
return (
<div
className={css({
p: '4',
borderRadius: 'md',
// Dynamic value based on prop
bg: type === 'info' ? 'blue.50' : type === 'warning' ? 'yellow.50' : 'red.50',
color: type === 'info' ? 'blue.800' : type === 'warning' ? 'yellow.800' : 'red.800',
borderLeft: '4px solid',
borderColor: type === 'info' ? 'blue.400' : type === 'warning' ? 'yellow.400' : 'red.400',
})}
>
Alert content
</div>
);
}
// Merging class names — use cx()
function MergedStyles({ className }: { className?: string }) {
return (
<div className={cx(
css({ bg: 'white', p: '4' }),
css({ borderRadius: 'lg' }),
className, // External classes merge correctly
)}>
Merged styles
</div>
);
}Fix 5: JSX Style Props (Type-Safe)
Panda generates typed JSX components for style props:
// panda.config.ts — enable JSX
export default defineConfig({
jsxFramework: 'react',
jsxStyleProps: 'all', // Enable style props on JSX components
});// After codegen — use styled components
import { styled } from '../styled-system/jsx';
import { flex, center } from '../styled-system/patterns';
// styled.div — type-safe style props
function StyledExample() {
return (
<styled.div bg="white" p="6" borderRadius="lg" shadow="md">
<styled.h1 fontSize="2xl" fontWeight="bold" mb="4">
Title
</styled.h1>
<styled.p color="gray.600" lineHeight="relaxed">
Paragraph with style props.
</styled.p>
</styled.div>
);
}
// Pattern JSX components
import { Flex, Center, Stack, Grid } from '../styled-system/jsx';
function LayoutExample() {
return (
<Stack gap="4">
<Flex justify="between" align="center">
<styled.span fontWeight="bold">Title</styled.span>
<styled.button bg="primary" color="white" px="4" py="2" borderRadius="md">
Action
</styled.button>
</Flex>
<Grid columns={{ base: 1, md: 3 }} gap="6">
<styled.div p="4" bg="gray.50" borderRadius="md">Cell 1</styled.div>
<styled.div p="4" bg="gray.50" borderRadius="md">Cell 2</styled.div>
<styled.div p="4" bg="gray.50" borderRadius="md">Cell 3</styled.div>
</Grid>
</Stack>
);
}Fix 6: Integration with Next.js and Vite
Next.js App Router:
// postcss.config.mjs
export default {
plugins: {
'@pandacss/dev/postcss': {},
},
};// app/layout.tsx
import '../styled-system/styles.css';
// Or if using a custom global CSS file:
// import './globals.css'; // which contains @layer reset, base, tokens, recipes, utilities;
export default function RootLayout({ children }) {
return <html><body>{children}</body></html>;
}Vite:
// postcss.config.mjs — same PostCSS config
export default {
plugins: {
'@pandacss/dev/postcss': {},
},
};// src/main.tsx
import '../styled-system/styles.css';
// Or: import './index.css';// package.json — auto-generate on dev/build
{
"scripts": {
"prepare": "panda codegen",
"dev": "panda codegen && vite",
"build": "panda codegen && vite build"
}
}Fix 7: Platform-Specific Bundler Quirks
Panda behaves slightly differently across the major bundlers, and the same panda.config.ts rarely works untouched in every environment.
Next.js App Router (RSC compatibility):
Panda CSS is RSC-safe because it compiles to plain CSS — no runtime context, no React hook usage. This is its biggest advantage over libraries like styled-components or Emotion, which require client-side runtime and force every consumer into a Client Component. You can call css({ ... }) directly inside async Server Components:
// app/page.tsx — Server Component, no "use client"
import { css } from '../styled-system/css';
export default async function Page() {
const data = await fetch('https://api.example.com/items').then(r => r.json());
return (
<ul className={css({ display: 'grid', gap: '4', p: '6' })}>
{data.items.map((item: any) => (
<li key={item.id} className={css({ p: '3', bg: 'gray.50' })}>{item.name}</li>
))}
</ul>
);
}If you see “Module not found: Can’t resolve ‘styled-system/css’” during a Next.js build, the most common cause is the outdir not matching your tsconfig.json paths. Set both consistently and make sure prepare runs before next build in CI.
Vite vs Webpack:
Vite resolves PostCSS via postcss.config.{js,mjs} at the project root. Webpack (used by older Next.js Pages Router and Create React App) discovers PostCSS through postcss-loader configuration that you may or may not own. In a Webpack project where Panda’s PostCSS plugin doesn’t fire, run the standalone CLI as a watcher instead:
panda --watch --outfile styled-system/styles.cssThen import styled-system/styles.css directly. This bypasses PostCSS entirely and works in any bundler.
Astro adapter:
Astro processes CSS per island, and the global stylesheet must be imported from a layout — not from an island. Put import '../styled-system/styles.css' in src/layouts/Layout.astro, not inside a React or Vue island, otherwise the styles only ship on pages that render that island.
Tailwind v3 vs v4 cohabitation:
If you are migrating off Tailwind, both can coexist for a transition window. Run Panda’s PostCSS plugin before Tailwind’s so Panda-extracted classes don’t get purged. With Tailwind v4 (which uses @tailwindcss/postcss), Panda’s layer ordering takes priority if you declare layers explicitly:
@layer reset, base, tokens, recipes, utilities, tailwind;
@import 'tailwindcss';For Tailwind-specific edge cases, see Fix: Tailwind v4 Not Working.
Monorepo TypeScript path resolution:
In a pnpm or Turborepo workspace, the styled-system/ directory typically lives inside packages/ui/. Consumers in apps/web/ need to either import the package barrel (import { css } from '@my/ui') or alias the directory through tsconfig.json:
{
"compilerOptions": {
"paths": {
"@my/ui/styled-system/*": ["../../packages/ui/styled-system/*"]
}
}
}Also add the consumer’s source paths to the package’s panda.config.ts include array — Panda only scans paths listed there, so a UI library that ships css() calls to consumers won’t generate the right classes unless the consumer files are also scanned.
Still Not Working?
Styles appear in dev but not in production — the CSS file might not be included in the production build. Verify the @layer import is in your CSS entry point and that it’s imported in your app. Also run npx panda codegen before building — some CI environments skip the prepare script.
TypeScript can’t find styled-system imports — add the path to your tsconfig.json: "paths": { "styled-system/*": ["./styled-system/*"] }. Also ensure styled-system/ is not in .gitignore (or add it and run codegen in CI).
Dynamic values don’t work — Panda is build-time, not runtime. css({ color: someVariable }) where someVariable is computed at runtime won’t work because Panda can’t scan dynamic values. Use conditional objects or recipes for variant-based styling. For truly dynamic values (e.g., user-selected color), use inline style attribute.
Hot reload doesn’t pick up new tokens — changes to panda.config.ts require running npx panda codegen to regenerate the styled-system/ directory. The PostCSS plugin watches for style changes in components but doesn’t watch config changes.
RSC build fails with “createContext is not a function” — this is not a Panda issue. Something in your imports is pulling in a runtime CSS-in-JS library (often via a transitive dependency). Panda itself doesn’t use React context, but if you also have Emotion or styled-components installed and accidentally imported them in a Server Component, the error surfaces during the Panda build step. Audit the import tree with npx next build --debug and remove or wrap any runtime CSS-in-JS calls in "use client".
Vite reports “Failed to resolve import ‘styled-system/css’” — this usually means the codegen never ran or node_modules was reinstalled without re-running prepare. Run npx panda codegen once, then add "postinstall": "panda codegen" to package.json so fresh installs regenerate the directory automatically. For the underlying Vite resolver behavior, see Fix: Vite Failed to Resolve Import.
Next.js Server Action returns styled-system import error — Server Actions are compiled into a separate bundle that may not inherit the same path aliases as the page bundle. Use relative imports (from '../../styled-system/css') inside Server Action files or expose styled-system through an internal package. See Fix: Next.js Server Action Not Working for related debugging.
For related CSS issues, see Fix: styled-components Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: UnoCSS Not Working — Classes Not Generating, Presets Missing, or Attributify Mode Broken
How to fix UnoCSS issues — Vite plugin setup, preset configuration, attributify mode, icons preset, shortcuts, custom rules, and integration with Next.js, Nuxt, and Astro.
Fix: CSS Container Query Not Working — @container and container-type Issues
How to fix CSS container queries not working — setting container-type correctly, understanding containment scope, fixing @container syntax, and handling browser support and specificity issues.
Fix: CodeMirror Not Working — Editor Not Rendering, Extensions Not Loading, or React State Out of Sync
How to fix CodeMirror 6 issues — basic setup, language and theme extensions, React integration, vim mode, collaborative editing, custom keybindings, and read-only mode.
Fix: GSAP Not Working — Animations Not Playing, ScrollTrigger Not Firing, or React Cleanup Issues
How to fix GSAP animation issues — timeline and tween basics, ScrollTrigger setup, React useGSAP hook, cleanup and context, SplitText, stagger animations, and Next.js integration.