FINDING YOUR DIRECTION

0%
← Scrollhaus Journal

Code Tutorials · Published · 5 min read

Next.js Animated Background: CSS, Canvas & WebGL Examples

An animated background runs on every page, so in Next.js it must be cheap and must never block server rendering. Here are three copy-ready versions, from a zero-JavaScript gradient to a WebGL shader loaded after hydration.

Published by Amanjot Singh, founder of Scrollhaus. AI-assisted editorial work; product context, sources and corrections are documented in our editorial methodology.

Night meadow animated background from a Scrollhaus website
Scrollhaus Journal · Code Tutorials

Disclosure: Scrollhaus publishes this guide. Code samples are original and tested against Next.js App Router conventions; check the versions in your own project.

Choosing an animated background for a Next.js site

An animated background sits behind everything else, so it has to be cheap, quiet and safe to render on every page. In Next.js the choice also depends on where the code runs: anything that touches window, a canvas or WebGL must be a client component, and heavy libraries should load only in the browser so they never slow down server rendering.

TechniqueWeightLookUse when
CSS gradient animation0 KB JavaScriptSoft colour drift, auroraEvery page, marketing sites, dashboards
Canvas 2D particles or noise~2–5 KBDots, stars, flow fieldsHero sections, landing pages
WebGL shader (raw or three.js)5 KB raw, ~150 KB with three.jsFluid, liquid metal, 3D fieldsOne signature hero, not every route
Looping videoMedia onlyAnything filmed or renderedComplex scenes you cannot draw in code

1. Zero-JavaScript animated gradient

Start here. It works in a Server Component, costs nothing and respects reduced motion with one media query.

// app/components/GradientBackground.tsx (server component)
export default function GradientBackground() {
  return <div aria-hidden className="fixed inset-0 -z-10 bg-[length:300%_300%] animate-[drift_18s_ease-in-out_infinite]
    bg-[linear-gradient(120deg,#0b0a1a,#2a1f5c,#0f3b4a,#0b0a1a)] motion-reduce:animate-none" />
}
// tailwind: @keyframes drift { 0%,100% { background-position: 0% 50% } 50% { background-position: 100% 50% } }

Our animated gradient background guide covers mesh and aurora variants.

2. Canvas particle background as a client component

'use client'
import { useEffect, useRef } from 'react'

export default function Particles({ count = 90 }) {
  const ref = useRef<HTMLCanvasElement>(null)
  useEffect(() => {
    const c = ref.current!, ctx = c.getContext('2d')!
    const dpr = Math.min(devicePixelRatio, 2)
    const dots = Array.from({ length: count }, () => ({ x: Math.random(), y: Math.random(), v: 0.0002 + Math.random() * 0.0006 }))
    const size = () => { c.width = innerWidth * dpr; c.height = innerHeight * dpr }
    size(); addEventListener('resize', size)
    const still = matchMedia('(prefers-reduced-motion: reduce)').matches
    let raf = 0
    const tick = () => {
      ctx.clearRect(0, 0, c.width, c.height); ctx.fillStyle = 'rgba(195,161,255,.55)'
      for (const d of dots) { if (!still) d.y = (d.y - d.v + 1) % 1; ctx.fillRect(d.x * c.width, d.y * c.height, 2 * dpr, 2 * dpr) }
      if (!document.hidden) raf = requestAnimationFrame(tick)
    }
    tick()
    const vis = () => { if (!document.hidden) { cancelAnimationFrame(raf); tick() } }
    document.addEventListener('visibilitychange', vis)
    return () => { cancelAnimationFrame(raf); removeEventListener('resize', size); document.removeEventListener('visibilitychange', vis) }
  }, [count])
  return <canvas ref={ref} aria-hidden className="fixed inset-0 -z-10 h-full w-full" />
}

3. A WebGL shader background without blocking the page

For a richer hero, load the WebGL component dynamically so the server renders your content first and the shader arrives after hydration:

// app/page.tsx
import dynamic from 'next/dynamic'
const ShaderBackground = dynamic(() => import('./components/ShaderBackground'), { ssr: false })

export default function Page() {
  return (<><ShaderBackground /><main><h1>Content renders first</h1></main></>)
}

ssr: false is only allowed inside a client component in the App Router, so put the dynamic() call in a small client wrapper if your page is a server component. Inside the shader component, cap the pixel ratio, pause when the tab is hidden and show a CSS gradient until the first frame draws. The same rules from our browser-based 3D guide apply.

Performance checklist for animated backgrounds in Next.js

Ready-made animated backgrounds

Scrollhaus has a library of animated backgrounds with live previews and copy-ready code, plus full websites built on them. KOI LAB is a free example of a three.js colour-current background. For more options, see animated backgrounds for websites and 3D animated backgrounds.

Frequently asked questions

How do I add an animated background in Next.js?

Use a CSS gradient in a server component, or a client component with a canvas or WebGL that you load with next/dynamic so content renders first.

Why does my canvas background break server rendering?

Canvas and WebGL need window and document. Mark the component with use client and load heavy versions with dynamic import and ssr false from a client component.

Do animated backgrounds hurt performance?

A CSS gradient costs nothing. Canvas and WebGL backgrounds need a capped pixel ratio, a pause when hidden and reduced-motion support.

Explore animated website templates at Scrollhaus →

Need help with a template, account, or project? Email admin@scrollhaus.ai.