Browse components
Desert Dune Background
Soft layered waves in parallax drift — cool indigo tones by default, colors via props.
Add to your project
Pick the way you like to build.
Let your AI assistant add it
Copy this prompt and paste it into the AI tool building your site.
# Integrate this UI component
Add this component to my project.
1. Install any missing dependencies.
2. Make sure Tailwind CSS is set up and scans the new file.
3. Prefer passing props over editing the component source — changing the component itself may break it.
4. Adjust colors, size, and layout via props or wrapper classes to match my project. You know my codebase better than this snippet does.
## Component source
```tsx
'use client';
import React, { useEffect, useRef, type ReactNode } from 'react';
// ---------------------------------------------------------------------------
// Props
// ---------------------------------------------------------------------------
//
// children Content layered above the waves (headline, CTA, etc.)
// className Styles for the content wrapper
// containerClassName Styles for the outer shell (size, layout)
// skyColors Vertical sky gradient [top, bottom]
// waveColors Wave fills back → front (light → dark)
// speed "slow" (default) or "fast"
//
type DesertDuneBackgroundProps = {
children?: ReactNode;
className?: string;
containerClassName?: string;
skyColors?: [string, string];
waveColors?: string[];
speed?: 'slow' | 'fast';
};
// ---------------------------------------------------------------------------
// Default palette — soft indigo / lilac
// ---------------------------------------------------------------------------
const DEFAULT_SKY: [string, string] = ['#eef1ff', '#c8d4f5'];
const DEFAULT_WAVES = ['#a8b8f0', '#8a9fe3', '#6d86d4', '#556ec0', '#4258a8'];
// ---------------------------------------------------------------------------
// Build a filled wave silhouette — padded so edges stay covered while animating
// ---------------------------------------------------------------------------
function wavePath(
width: number,
height: number,
baseline: number,
amplitude: number,
phase: number,
frequency: number
) {
const pad = Math.max(32, amplitude * 2);
const waveY = (x: number) =>
baseline +
Math.sin(x * frequency + phase) * amplitude +
Math.sin(x * frequency * 2.1 + phase * 1.3) * amplitude * 0.4;
let d = `M ${-pad} ${height} L ${-pad} ${waveY(-pad)}`;
for (let x = -pad; x <= width + pad; x += 4) {
d += ` L${x} ${waveY(x)}`;
}
d += ` L ${width + pad} ${height} Z`;
return d;
}
export default function DesertDuneBackground({
children,
className = '',
containerClassName = '',
skyColors = DEFAULT_SKY,
waveColors = DEFAULT_WAVES,
speed = 'slow',
}: DesertDuneBackgroundProps) {
// ---------------------------------------------------------------------------
// Refs
// ---------------------------------------------------------------------------
const containerRef = useRef<HTMLDivElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
const rafRef = useRef(0);
const timeRef = useRef(0);
useEffect(() => {
const canvas = canvasRef.current;
const container = containerRef.current;
if (!canvas || !container) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const step = speed === 'fast' ? 0.008 : 0.004;
let width = 0;
let height = 0;
let dpr = 1;
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches;
// -------------------------------------------------------------------------
// Setup helpers
// -------------------------------------------------------------------------
const resize = () => {
const rect = container.getBoundingClientRect();
dpr = Math.min(window.devicePixelRatio || 1, 2);
width = Math.max(1, Math.floor(rect.width));
height = Math.max(1, Math.floor(rect.height));
canvas.width = Math.floor(width * dpr);
canvas.height = Math.floor(height * dpr);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};
// -------------------------------------------------------------------------
// Draw loop — sky + layered waves
// -------------------------------------------------------------------------
const draw = () => {
timeRef.current += prefersReducedMotion ? 0 : step;
const t = timeRef.current;
const sky = ctx.createLinearGradient(0, 0, 0, height);
sky.addColorStop(0, skyColors[0]);
sky.addColorStop(1, skyColors[1]);
ctx.fillStyle = sky;
ctx.fillRect(0, 0, width, height);
const layers = waveColors;
const count = layers.length;
for (let i = 0; i < count; i++) {
const progress = i / Math.max(count - 1, 1);
const baseline = height * (0.48 + progress * 0.28);
const amplitude = height * (0.045 + (1 - progress) * 0.035);
const frequency = 0.006 + i * 0.0012;
// Parallax drift — farther layers move slower
const phase = t * (0.55 + i * 0.35) + i * 1.7;
const path = new Path2D(
wavePath(width, height, baseline, amplitude, phase, frequency)
);
ctx.fillStyle = layers[i];
ctx.fill(path);
}
if (!prefersReducedMotion) {
rafRef.current = requestAnimationFrame(draw);
}
};
// -------------------------------------------------------------------------
// Start + cleanup
// -------------------------------------------------------------------------
resize();
draw();
const observer = new ResizeObserver(resize);
observer.observe(container);
return () => {
cancelAnimationFrame(rafRef.current);
observer.disconnect();
};
}, [waveColors, skyColors, speed]);
// ---------------------------------------------------------------------------
// Render — canvas behind, content on top
// ---------------------------------------------------------------------------
return (
<div
ref={containerRef}
className={[
'relative flex min-h-[420px] w-full flex-col items-center justify-center overflow-hidden',
containerClassName,
]
.filter(Boolean)
.join(' ')}
>
<canvas
ref={canvasRef}
className='absolute inset-0 z-0 h-full w-full'
aria-hidden
/>
<div
className={['relative z-10 px-6 text-center', className]
.filter(Boolean)
.join(' ')}
>
{children}
</div>
</div>
);
}
```
Works with your AI tools
* Compatible with most projects that use the default React/Next.js framework.
Built With
Props
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Content layered above the waves (headline, CTA, etc.) |
className | string | Styles for the content wrapper |
containerClassName | string | Styles for the outer shell (size, layout) |
skyColors | [string, string] | Vertical sky gradient [top, bottom] |
waveColors | string[] | Wave fills back → front (light → dark) |
speed | 'slow' | 'fast' | How quickly the dunes drift |
More Backgrounds Components

Animated canvas waves that fill the container and drift with the pointer — a softer, SaaS-ready take on classic wavy backgrounds.
Subtle canvas meteors drifting across a dark sky — a quiet animated background for heroes and landing sections.
Staggered neon trails that sweep across a dark field — colors are prop-driven for easy theming.

Northern lights as folding canvas curtains: irregular ribbons and faint rays drifting across a night sky.
A static 3D background grid — a wireframe perspective grid that covers the full container, warped and bowed toward a vanishing point for depth. No animation, no layout shift.