React Integration

NightfallProvider, useNightfall hook, and NightfallToggle component — under 3KB total.

Installation

The React integration is included in the main package. No additional install needed:

npm install nightfall-css
# React components are at nightfall-css/react

NightfallProvider

Wrap your app with the provider. It manages theme state, localStorage persistence, and system preference detection:

app/layout.tsx
import { NightfallProvider } from 'nightfall-css/react'
import './nightfall-generated.css'

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <NightfallProvider
          defaultTheme="system"
          storageKey="nightfall-theme"
          attribute="data-theme"
        >
          {children}
        </NightfallProvider>
      </body>
    </html>
  )
}

Provider props:

  • defaultTheme — Initial theme: "light", "dark", or "system" (default: "system")
  • storageKey — localStorage key for persistence (default: "nightfall-theme")
  • attribute — HTML attribute set on the root element (default: "data-theme")
  • enableSystem — Whether to respect prefers-color-scheme (default: true)
  • disableTransition — Disable CSS transitions during theme switch to prevent flicker (default: false)

useNightfall Hook

Access theme state and controls from any component:

ThemeStatus.tsx
import { useNightfall } from 'nightfall-css/react'

export function ThemeStatus() {
  const {
    theme,          // 'light' | 'dark' | 'system'
    resolvedTheme,  // 'light' | 'dark' (actual resolved value)
    setTheme,       // (theme: string) => void
    toggleTheme,    // () => void — switches between light and dark
    systemTheme,    // 'light' | 'dark' — current OS preference
  } = useNightfall()

  return (
    <div>
      <p>Setting: {theme}</p>
      <p>Resolved: {resolvedTheme}</p>
      <p>System prefers: {systemTheme}</p>

      <button onClick={() => setTheme('light')}>Light</button>
      <button onClick={() => setTheme('dark')}>Dark</button>
      <button onClick={() => setTheme('system')}>System</button>
      <button onClick={toggleTheme}>Toggle</button>
    </div>
  )
}

NightfallToggle Component

A ready-to-use toggle button with sun/moon icons and smooth transitions:

Header.tsx
import { NightfallToggle } from 'nightfall-css/react'

export function Header() {
  return (
    <header className="flex items-center justify-between p-4">
      <h1>My App</h1>
      <NightfallToggle
        size="md"           // 'sm' | 'md' | 'lg'
        iconStyle="system"  // 'system' | 'emoji' | 'text'
        className="custom-toggle-class"
      />
    </header>
  )
}

// Renders a button that:
// - Shows a sun icon in dark mode (click to switch to light)
// - Shows a moon icon in light mode (click to switch to dark)
// - Animates the icon transition
// - Includes aria-label for accessibility

Server Components (Next.js)

The provider works with React Server Components. Only the provider and toggle are client components — your page content remains a server component:

app/page.tsx
// This is a Server Component — no 'use client' needed
export default function Page() {
  return (
    <main>
      <h1>Hello from a Server Component</h1>
      <p>Theme switching works because the provider is in the layout.</p>
    </main>
  )
}

Avoiding Hydration Mismatch

The provider delays rendering theme-dependent content until after hydration to prevent React hydration mismatches. Add suppressHydrationWarning to your html element and use the FOUC prevention script for seamless loading:

app/layout.tsx
import { nightfallScript } from 'nightfall-css/script'

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <script dangerouslySetInnerHTML={{ __html: nightfallScript }} />
      </head>
      <body>
        <NightfallProvider>{children}</NightfallProvider>
      </body>
    </html>
  )
}