Documentation

SSR with Next.js / React

Shaders uses WebGPU, which requires a browser environment and cannot run during server-side rendering. This page covers how to safely use Shaders in SSR-enabled React applications.

App Router: 'use client'

In Next.js App Router, create a client component wrapper:

// components/MyShader.jsx
'use client'

import { Shader, LinearGradient, CursorTrail } from 'shaders/react'

export default function MyShader() {
  return (
    <Shader className="w-full h-64">
      <LinearGradient colorA="#0f172a" colorB="#7c3aed" />
      <CursorTrail />
    </Shader>
  )
}

Then use it in a Server Component:

// app/page.jsx (Server Component)
import MyShader from '@/components/MyShader'

export default function Page() {
  return (
    <main>
      <MyShader />
      <h1>Your content</h1>
    </main>
  )
}

The 'use client' directive marks the component and its subtree as client-only, preventing SSR execution.

App Router: next/dynamic with ssr: false

For the most reliable approach — especially if you want to keep the component file itself framework-agnostic — use next/dynamic to disable SSR:

// app/page.jsx
import dynamic from 'next/dynamic'

const MyShader = dynamic(
  () => import('@/components/MyShader'),
  { ssr: false }
)

export default function Page() {
  return (
    <main>
      <MyShader />
    </main>
  )
}

This defers the component entirely to the client and prevents the module from loading on the server.

Pages Router

In the Pages Router, the same next/dynamic approach works:

// pages/index.jsx
import dynamic from 'next/dynamic'

const MyShader = dynamic(
  () => import('../components/MyShader'),
  { ssr: false }
)

export default function Home() {
  return (
    <div>
      <MyShader />
    </div>
  )
}

In a root layout

If you want a shader to appear across all pages (for example, a full-page background), add it to your root layout as a client component:

// app/layout.jsx
import BackgroundShader from '@/components/BackgroundShader'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <BackgroundShader />
        {children}
      </body>
    </html>
  )
}
// components/BackgroundShader.jsx
'use client'

import { Shader, Aurora } from 'shaders/react'

export default function BackgroundShader() {
  return (
    <Shader className="fixed inset-0 -z-10 pointer-events-none">
      <Aurora />
    </Shader>
  )
}

React (without Next.js)

In a React SSR setup without Next.js, use a mounted state guard:

import { useState, useEffect } from 'react'
import { Shader, LinearGradient } from 'shaders/react'

export default function MyShader() {
  const [mounted, setMounted] = useState(false)

  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) return null

  return (
    <Shader className="w-full h-64">
      <LinearGradient />
    </Shader>
  )
}