Parallax Section
A multi-layer parallax section where each layer shifts at a different speed with scroll progress, stacking up depth and a sense of space.
npx shadcn@latest add https://webberui.com/r/parallax-section.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<ParallaxSection />
Installation
npx shadcn@latest add https://webberui.com/r/parallax-section.jsonOr, once registries are configured in components.json, install it as @webberui/parallax-section.
Usage
Use ParallaxSection as the container and put several ParallaxLayer elements inside it. Most parallax layers are stacked with absolute inset-0, and speed decides each layer's displacement multiplier relative to the scroll:
import {
ParallaxLayer,
ParallaxSection,
} from "@/components/ui/parallax-section";
<ParallaxSection className="h-[420px] bg-neutral-950">
{/* Background: sinks slowly */}
<ParallaxLayer decorative speed={-0.6} className="absolute inset-0">
<div className="absolute inset-0 bg-gradient-to-b from-indigo-500/30 to-transparent" />
</ParallaxLayer>
{/* Foreground: floats up quickly and fades out */}
<ParallaxLayer
speed={1.2}
fade
className="absolute inset-0 flex items-center justify-center"
>
<h2 className="text-4xl font-bold text-white">Multi-layer parallax</h2>
</ParallaxLayer>
</ParallaxSection>;Nested scroll container
If the parallax happens inside a fixed-height container that scrolls internally (rather than the whole window), pass that container's ref to container:
const scrollerRef = React.useRef<HTMLDivElement>(null);
<div ref={scrollerRef} className="h-[300px] overflow-y-auto">
<ParallaxSection container={scrollerRef}>{/* … */}</ParallaxSection>
</div>;Props
ParallaxSection
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | One or more ParallaxLayer elements |
range | number | 120 | Maximum parallax displacement per unit of speed (px) |
container | RefObject<HTMLElement | null> | — | Ref of the nested scroll container; when omitted, the window is the scroll container |
className | string | — | Appended to the class list (which defaults to relative overflow-hidden) |
The remaining React.HTMLAttributes<HTMLElement> (such as aria-label, style, id) are forwarded to the root <section>.
ParallaxLayer
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | The contents of this layer |
speed | number | 0.5 | Displacement multiplier; positive values sit toward the foreground (faster, floating up against the scroll), negative values toward the background (slower, sinking with it), 0 means no parallax |
axis | "x" | "y" | "y" | Axis of displacement |
fade | boolean | false | Fade in and out at both ends as the section enters and leaves the viewport |
decorative | boolean | false | Mark the layer as purely decorative (adds aria-hidden, disables pointer events) |
className | string | — | Appended to the class list |
How it works
- Progress is tracked with
useScrollacross the full scroll from "the section enters the viewport at the bottom" to "the section leaves the viewport at the top" (offset: ["start end", "end start"]); the midpoint0.5is where the section is centered and every layer returns to its natural position. - Each layer's displacement is a linear mapping of
progressacross[+speed·range, -speed·range], so displacement is 0 when the section is centered and grows toward both ends. - Parallax layers are usually stacked with
absolute inset-0; giveParallaxSectionan explicit height and keep the defaultoverflow-hiddenso overflowing backgrounds get clipped. - The sign of
speedsets the depth direction and its magnitude sets the amount of displacement; small negative values work well for background layers, larger positive values for foreground accents.
Accessibility
- When the user has "reduce motion" enabled at the system level, parallax displacement and fading are disabled automatically, every layer stays at its natural position, and the content remains fully readable.
- Add
decorativeto purely decorative layers; it appliesaria-hiddenand disables pointer events, so assistive technology does not read them out and they cannot be clicked by accident. - Parallax only applies
transform/opacity, so it does not affect document flow or reading order.