Skip to content

Fix: Panda CSS Not Working — Styles Not Applying, Tokens Not Resolving, or Build Errors

FixDevs · (Updated: )

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 HTML

Or 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-system directory must be generated first — running npx panda codegen creates the styled-system/ directory with css, cva, sva, and other utilities. Without this step, imports fail. The codegen must re-run whenever you change panda.config.ts.
  • Panda scans specific file paths — the include array in panda.config.ts tells 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 themecolor: 'blue.500' works because blue.500 is a default token. color: 'primary' only works if you define primary in the theme.tokens or theme.semanticTokens config. 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.css

Then 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.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles