Skip to content
Learni
Voir tous les tutoriels
Frontend

Comment configurer Panda CSS avancé pour Next.js en 2026

Read in English

Introduction

Panda CSS est un moteur de styling zero-runtime révolutionnaire pour React et Next.js, surpassant Tailwind par sa flexibilité et sa sécurité types. En 2026, avec l'essor des apps web performantes, Panda excelle grâce à ses recettes (composables et réutilisables), patterns (UI complexes) et extraction statique CSS. Contrairement aux solutions runtime coûteuses, Panda génère du CSS pur à la build-time, réduisant le JS bundle de 90%+.

Ce tutoriel advanced vous guide pas à pas pour un setup complet dans Next.js App Router : config TS avancée, variants conditionnels, états interactifs, responsive + dark mode, et patterns slot-based. Vous obtiendrez un projet fonctionnel, scalable et bookmarqué par tout dev senior. Prêt à transformer vos styles en code type-safe ? (128 mots)

Prérequis

  • Node.js 20+ et npm/yarn/pnpm
  • Next.js 15+ avec App Router et TypeScript
  • Connaissances avancées en React hooks et Tailwind-like utilities
  • Un éditeur comme VS Code avec extension Panda CSS (optionnel mais recommandé)
  • Git pour versionner le projet

Installation du projet Next.js et Panda CSS

terminal
npx create-next-app@latest mon-projet-panda --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
cd mon-projet-panda
npm uninstall tailwindcss postcss autoprefixer
npx @pandacss/dev@latest init
npm install @pandacss/css

Ce script crée un projet Next.js moderne sans Tailwind (supprimé ensuite), initialise Panda CSS et installe le runtime. L'init génère panda.config.ts et un preset de base. Lancez npm run dev après pour vérifier.

Configuration de base de Panda

La config se fait dans panda.config.ts. Elle définit les presets, utilities, thèmes et exclusions. Pour l'avancé, activez les recettes/patterns, le dark mode et les animations. Ajoutez des tokens custom pour cohérence design.

panda.config.ts complet avec thème et presets

panda.config.ts
import { defineConfig } from '@pandacss/dev'

export default defineConfig({
  preflight: true,
  theme: {
    extend: {
      tokens: {
        colors: {
          primary: { value: '{colors.blue.500}' },
          neutral: {
            50: { value: '#f8fafc' },
            900: { value: '#0f172a' }
          }
        },
        spacing: {
          xs: { value: '0.5rem' },
          xl: { value: '3rem' }
        }
      },
      breakpoints: {
        xs: '30em',
        xl: '80em'
      }
    },
    semanticTokens: {
      colors: {
        bg: { value: '{colors.neutral.50}' },
        bgDark: { value: '{colors.neutral.900}' }
      }
    }
  },
  globalCss: {
    body: {
      bg: '{colors.bg}',
      color: '{colors.neutral.900}',
      _dark: { bg: '{colors.bgDark}' }
    }
  },
  conditions: {
    sm: '@media (min-width: 640px)',
    dark: '@media (prefers-color-scheme: dark)',
    hover: '&:hover',
    focus: '&:focus-visible'
  },
  patterns: {
    code: {
      description: 'Code block',
      properties: [{ name: 'variant', type: 'enum', default: 'default', values: ['default', 'ghost'] }],
      slots: ['container', 'badge'],
      style: {
        container: {
          base: { p: '2', bg: 'neutral.100', rounded: 'md', _dark: { bg: 'neutral.800' } },
          variants: {
            variant: {
              ghost: { bg: 'transparent' }
            }
          }
        },
        badge: {
          base: { px: '1.5', py: '0.5', fontSize: 'sm', fontWeight: 'bold', bg: 'accent', color: 'white', rounded: 'full' }
        }
      }
    }
  },
  recipes: {
    button: {
      description: 'A customizable button',
      className: 'btn',
      jsx: ['Button', 'button'],
      variants: {
        size: {
          sm: { fontSize: 'sm', px: 'xs', py: '1.5', height: '2rem' },
          lg: { fontSize: 'lg', px: 'xl', py: '2', height: '3rem' }
        },
        variant: {
          solid: { bg: 'primary', color: 'white', _hover: { bg: '{colors.primary.600}' } },
          outline: { border: '2px solid currentColor', _hover: { bg: 'primary.50' } },
          ghost: { _hover: { bg: 'neutral.100' } }
        }
      },
      defaultVariants: {
        size: 'sm',
        variant: 'solid'
      }
    }
  },
  src: ['./src/**/*.{ts,tsx}'],
  outdir: 'styled-system',
  include: ['./src/**/*.{js,jsx,ts,tsx}'],
  exclude: [],
})

Cette config avancée étend le thème avec tokens sémantiques, conditions CSS custom (hover, dark), une recette 'button' multi-variants et un pattern 'code' avec slots. Elle cible src/ pour l'extraction. Les _dark et _hover utilisent les conditions pour du CSS natif. Lancez npx panda cssgen pour générer le CSS.

Ajout du CSS Panda dans layout.tsx

src/app/layout.tsx
import type { Metadata } from 'next'
import { css } from '../styled-system/css'
import { darkTheme, theme } from '../styled-system/theme'
import './globals.css'

export const metadata: Metadata = {
  title: 'Panda CSS Advanced',
  description: 'Tutoriel Panda CSS avancé',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="fr" suppressHydrationWarning>
      <body>
        <div className={css(
          {
            theme,
            darkTheme,
            colorMode: { base: 'light', _dark: 'dark' },
          },
          'min-h-screen w-full'
        )}>
          {children}
        </div>
      </body>
    </html>
  )
}

Intégrez Panda dans le layout root avec css() pour appliquer thème light/dark. theme et darkTheme sont générés par Panda. suppressHydrationWarning évite les mismatches SSR. Cela active le color mode global.

Création de recettes avancées

Les recettes sont des objets composables avec variants, states (hover, focus) et compound variants. Imaginez-les comme des Lego : base + modificateurs. Pour advanced, combinez responsive, états et slots.

Recette Button avancée avec états et responsive

src/components/ui/Button.tsx
import { styled } from '../styled-system/jsx'

import { button } from '../styled-system/recipes'

const ButtonVariant = button.variantKeys

const ButtonSize = button.sizeKeys

type ButtonProps = {
  variant?: ButtonVariant
  size?: ButtonSize
  children: React.ReactNode
  className?: string
}

export const Button = ({
  variant,
  size,
  children,
  className,
}: ButtonProps) => {
  return (
    <styled.button
      className={button({ size, variant, className })}
      role="button"
    >
      {children}
    </styled.button>
  )
}

Ce composant utilise styled de Panda et applique la recette button via button({ variants }). Types inférés auto via variantKeys. Ajoutez aria-* pour accessibilité. Totalement type-safe : VS Code auto-complete les props.

Pattern Card avec slots et conditions

src/components/ui/Card.tsx
import { styled } from '../styled-system/jsx'
import { code } from '../styled-system/patterns'

type CardProps = {
  children: React.ReactNode
  variant?: 'default' | 'ghost'
  className?: string
}

export const CardContainer = styled('div', {
  base: {
    p: '6',
    bg: 'bg',
    rounded: 'lg',
    shadow: 'md',
    _dark: { bg: 'bgDark' },
    _hover: { shadow: 'lg', transform: 'translateY(-2px)' },
    transition: 'all 0.2s ease'
  }
})

export const Card = ({ children, variant = 'default', className }: CardProps) => (
  <code.codeRoot className={code({ variant, className })}>
    {children}
  </code.codeRoot>
)

Le pattern code (renommé Card ici) utilise slots implicites via code.codeRoot. Ajout d'animations CSS natives (_hover, transition). styled('div') pour un slot custom. Slots permettent une composition granulaire sans prop drilling.

Intégration responsive et dark mode

Responsive : Utilisez sm:, md: etc. dans css() ou recettes. Dark mode : Préfixes _dark auto-générés. Testez avec localStorage.setItem('theme', 'dark').

Page d'exemple utilisant tous les composants

src/app/page.tsx
import { css } from '../styled-system/css'
import { Button } from '@/components/ui/Button'
import { Card } from '@/components/ui/Card'

export default function Home() {
  return (
    <main className={css({ p: '10', maxW: '4xl', mx: 'auto' })}>
      <h1 className={css({ textStyle: '2xl', fontWeight: 'bold', mb: '8', textAlign: 'center' })}>
        Panda CSS Avancé en Action
      </h1>
      <div className={css({ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '6' })}>
        <Card>
          <h2 className={css({ fontSize: 'xl', fontWeight: 'semibold', mb: '4' })}>
            Bouton Solid Responsive
          </h2>
          <Button size="lg" variant="solid" className={css({ w: 'full', sm: { w: 'auto' } })}>
            Cliquez-moi
          </Button>
        </Card>
        <Card variant="ghost">
          <h2 className={css({ fontSize: 'xl', mb: '4' })}>
            Bouton Ghost Dark Mode
          </h2>
          <Button size="sm" variant="ghost">
            Ghost Button
          </Button>
        </Card>
      </div>
    </main>
  )
}

Cette page démontre grille responsive, boutons variants et Card pattern. css({ sm: {} }) applique media queries. Tout est server-rendered sans JS runtime. Lancez npx panda codegen puis npm run build pour optimiser.

Script de build et watch pour développement

package.json scripts
{
  "scripts": {
    "dev": "next dev",
    "build": "pnpm panda generate && next build",
    "panda:watch": "pnpm panda --watch",
    "panda:build": "pnpm panda generate",
    "lint": "next lint"
  }
}

Ajoutez ces scripts à package.json. panda generate extrait et génère CSS/TS types. Utilisez concurrently pour watch en dev : concurrently "pnpm panda:watch" "next dev". Évite les rebuilds lents.

Bonnes pratiques

  • Toujours utiliser recettes/patterns : Évite le css() inline pour la maintenance.
  • Tokens sémantiques first : bg: 'bg' au lieu de couleurs hardcodées pour thèmes.
  • Compound variants : Pour des combos comme size=lg + variant=outline.
  • Plugins custom : Étendez Panda avec plugins: [monPlugin()] pour animations avancées.
  • Génération CI/CD : Intégrez panda generate dans build pipeline.

Erreurs courantes à éviter

  • Oublier panda generate : Pas de styles sans génération post-install.
  • Mix Tailwind/Panda : Conflits PostCSS ; supprimez Tailwind.
  • Props non typées : Utilisez recipeKeys pour inférence auto.
  • États sans transitions : Ajoutez toujours transition: 'all 0.15s' pour UX fluide.

Pour aller plus loin

Plongez dans les docs officielles Panda CSS pour plugins avancés. Maîtrisez les animations avec @keyframes custom. Découvrez nos formations Learni sur React/Next.js avancés pour scaler vos skills frontend.