/

Animated Backgrounds in React: What They Cost and Which Ones to Use

CSS gradient, canvas or WebGL shader: what each costs on a laptop and a mid range phone, illustrated with the most-bookmarked background components on 21st, and the source-level audit of all 155 of them.

Serafim Korablev
Serafim Korablev
@korablev

An animated background is the cheapest way to make a page look considered and the easiest way to make it drop frames. The short answer, cheapest first: a CSS gradient costs effectively nothing, an SVG or canvas animation costs a slice of your main thread every frame, and a WebGL shader runs a fragment program once per pixel per frame for as long as the page is open. All three look identical in a screenshot. Only one of them is free.

There are 155 public components on 21st with "background" in the name. We read the source of every one of them for this piece, and the number worth leading with: 67 run a continuous frame loop, and 6 of those stop when the tab is hidden or the element scrolls out of view. Five check prefers-reduced-motion. The families come first, then the part that matters.

Gradients: the family that costs nothing

Aurora Background by Manu Arora (3,648 bookmarks) is about two kilobytes of component with no dependencies and no frame loop: layered gradients moved by a CSS keyframe. Your JavaScript is never involved, so when the main thread is busy the animation keeps going rather than stalling.

Background Gradient Animation (1,222) is the richer version, blurred colour fields under an SVG filter, still with no requestAnimationFrame anywhere in the file. Animated Gradient Background by hammamikhairi (1,708) goes the other way and drives the gradient from JavaScript, which buys cursor reactivity at the price of a frame loop.

If what you want is "this page has a mood", stop here. This family cannot make a phone warm.

Paths and geometry: SVG doing the drawing

The most bookmarked background on 21st is here. Background Paths by kokonutd has 4,749 bookmarks: a field of animated SVG paths, no canvas, no frame loop of its own. Background Boxes (1,158) and Pulse Beams (1,139) are the same technique with different geometry.

SVG is the comfortable middle. It is resolution independent, composites well, and you can open the file and understand what it draws. Its ceiling is element count: a few dozen animated paths is nothing, a few hundred is layout and paint work every frame that a canvas would have done in one pass.

Wave Background by KainXu (1,184) sits exactly on that line: simplex noise recomputing path data inside a requestAnimationFrame loop. It looks better than most shaders, and per visible pixel it is the most main-thread-expensive thing here.

Shaders: the best looking, most expensive

Shader Background by Le Thanh (1,823) is a raw WebGL context with hand-written GLSL and no dependencies, as lean as this family gets. Animated Shader Background (1,925) does the same job through three.js. Background Paper Shaders by reuno-ui (2,365) goes through three plus @react-three/fiber.

Across the 155, 30 use WebGL in some form and 15 of those pull in three. Make that choice deliberately: the shader is a few hundred bytes of text, and the runtime you pick for it is anywhere from nothing to a few hundred kilobytes of JavaScript that must parse before the first pixel appears. Raw WebGL for a single full-screen quad is roughly forty lines. Reach for three when you have a scene, not a rectangle.

If you would rather tune one than adopt one, the Shader Builder does it in the browser and exports a React component.

Particles and fields: canvas 2D

Flow Field Background by Hossain Jahed (1,406) is 600 particles by default, pushed through a noise field and drawn on a 2D canvas. Beams Background by kokonutd (1,753) is the same machinery, softer shapes. For the background and the hero already composed: Ethereal Beams Hero by M.bin Salman (995) and Glowy Waves Hero by Moumen Soliman (851).

This family has the most obvious throttle: particle count is linear in cost and nearly always a prop.

What each one actually costs

Canvas and WebGL cost pixels multiplied by frames, and the arithmetic is worth doing once.

A full-viewport canvas on a 1440 by 900 laptop, backed at devicePixelRatio 2, is 5.2 million pixels per frame and 311 million per second. That is comfortable for clearing the canvas and drawing a few hundred sprites, which is why Flow Field's 600 particles are not a problem, and uncomfortable the moment something touches every pixel in JavaScript.

WebGL moves the per-pixel work to the GPU, so the main thread pays for one draw call, but the fragment shader still runs once per pixel per frame. A mid range phone at 390 by 844 CSS pixels with devicePixelRatio 3 is 2.9 million fragments a frame, 178 million a second. Four octaves of noise per fragment is where those devices give up, and they do not give up by dropping frames: they thermally throttle, so the page is smooth for twenty seconds and sluggish after a minute. It is the hardest performance bug to reproduce at a desk.

The lever almost nobody pulls is the backing store. Of the 41 canvas-based backgrounds in the catalogue, 14 scale for devicePixelRatio and none of them cap it. Clamping with Math.min(window.devicePixelRatio, 1.5) removes half to three quarters of the fragment work on a phone, and a blurred gradient is the one thing on a page where nobody can see the difference.

Three rules, and how rarely they are followed

Pause when it is off screen. requestAnimationFrame already stops when the whole tab is hidden, so a missing visibilitychange handler is the smaller half of this. The expensive half is scrolling: a hero background sitting a thousand pixels above the fold keeps painting every frame, because nothing told it to stop. An IntersectionObserver around the loop is six lines. Five components out of 155 have one.

Respect prefers-reduced-motion. One media query. For a background the correct behaviour is usually to paint a single frame and stop rather than render nothing, so the composition survives and the motion goes. Five components out of 155 check it. This is an accessibility requirement, not a preference: full-screen drifting motion is exactly the pattern that triggers vestibular symptoms.

Start after first paint. Most of these initialise inside an effect, which fires in the same busy tick as hydration, so the frame loop starts competing for the main thread during the one second that decides LCP and INP. Mounting it a tick later, or behind an idle callback, moves the cost out of that window and looks identical, since it fades in anyway.

Battery is the sum of the three. A full-screen shader holds the GPU out of its low-power state for the whole session: a laptop answers with fans, a phone with throttling. None of that argues against shaders. It argues for a shader that stops when nobody is looking at it.

Where else to look

Honestly, us included:

SourceBest forTrade-off
shadcn/uiThe primitives under everything elseShips no background components at all
Paper ShadersHand-tuned shader primitives with a real APIA fixed set of looks; you tune rather than compose
React BitsCopy-paste animated backgroundsCopy-paste means no update path once it is in your repo
ShadertoyEndless GLSL to borrow fromNot components; you write the WebGL wiring, and most of it ignores mobile budgets
21stThe widest set, plus a builder that generates oneQuality varies by author, and most of the animated ones never pause

On the search everyone runs: shadcn/ui has no backgrounds and does not need any. The registry format is the delivery mechanism, so anything published to a shadcn-compatible registry installs with the same command as a button.

Taking one

Every component page has a live preview and the source. Installs go through the shadcn CLI against our registry:

bash

The key comes from your 21st account and installs need a membership, so set API_KEY_21ST once in your shell and the command works for anything in the catalogue. Read the frame loop before you ship it: check for an IntersectionObserver, a prefers-reduced-motion branch and a devicePixelRatio cap, and add the ones that are missing. It is fifteen lines, and it separates a background that costs something while someone looks at it from one that costs something all session.

Browse background components →

Frequently asked

Do animated backgrounds slow down a React app?
They can, and the deciding factor is what runs on every frame rather than which library produced the effect. A gradient animated with CSS keyframes costs almost nothing because no JavaScript is involved, while a canvas or WebGL background runs a loop for as long as the page is open. The damage is usually not the effect itself but where it starts: initialising a frame loop inside an effect puts it in the same tick as hydration, competing for the main thread during the second that decides LCP and INP.
What is the difference between a CSS, canvas and WebGL background?
A CSS background is painted by the browser from gradients and keyframes, so it costs no JavaScript and keeps animating even when the main thread is busy. A canvas background runs drawing code on the main thread every frame, so its cost scales with particle count and canvas size, which is why particle count is nearly always exposed as a prop. A WebGL shader moves the per-pixel work to the GPU but runs a fragment program once per pixel per frame, which is why it looks the best and heats a phone the fastest.
Does shadcn/ui have animated background components?
No. shadcn/ui ships primitives such as buttons, inputs and dialogs, and no background effects at all. What people call shadcn backgrounds is usually the install mechanism rather than the library: any component published to a shadcn-compatible registry installs with the shadcn CLI add command pointed at that registry URL, which is how community backgrounds end up in a project.
How do I stop an animated background draining battery?
Three changes cover most of it. Stop the frame loop when the element scrolls out of view with an IntersectionObserver, because requestAnimationFrame only pauses for a fully hidden tab and keeps painting a hero background that has scrolled off the top. Cap the canvas backing store with Math.min(window.devicePixelRatio, 1.5), which removes most of the fragment work on a phone with no visible difference on a blurred effect, and paint a single static frame when prefers-reduced-motion is set.

Published

Aug 19, 2026

Read time

7 min

Tags

GuideBackgroundsPerformance

Share