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>
)
}