Browse the docs
MotionNo. 79 of 109
Parallax
Drifts content at a different speed from the page as it scrolls past, with CSS scroll-driven animations and no JavaScript.
Installation
pnpm dlx shadcn@latest add @corsair-ui/parallaxNo setup needed: the CLI finds @corsair-ui in the shadcn registry directory. The files land in your project as source: edit them like your own code. More in Installation.
- Also adds
- utils
- npm
@radix-ui/react-slotclsxtailwind-merge
Examples
Depth layers
Scroll inside the box. Each ring drifts at its own speed; the scroll container here is the box, so its timeline is too. The rings are clipped with overflow-clip: overflow-hidden would make a scroll container of its own, and that one never scrolls.
API
| Prop | Type | Default |
|---|---|---|
offsetDrift each way in px; negative values run ahead of the scroll. | number | 40 |
asChildMove the child instead of a wrapper. | boolean | false |
Accessibility
- Purely visual: nothing about the content or its order changes.
- Browsers without scroll-driven animations keep it still.
- With prefers-reduced-motion it holds still and shows the finished state.
Compatibility
- Tailwind CSS
- 3.4 and 4, both checked in CI
- React
- 19
- Rendering
- Works in server components
- Motion
- Respects prefers-reduced-motion
Source
1 file, installed as source you own
parallax.tsxView on GitHubimport { Slot } from "@radix-ui/react-slot";
import type { ComponentProps, CSSProperties } from "react";
import { cn } from "@/registry/default/lib/utils";
interface ParallaxProps extends ComponentProps<"div"> {
/** Move the single child element instead of wrapping it in a div. */
asChild?: boolean;
/**
* How far, in px, the element drifts each way while it crosses the
* viewport. Positive values lag behind the scroll, negative values run ahead.
*/
offset?: number;
}
/**
* Drifts content at a different speed from the page while it scrolls past.
* It runs on CSS scroll-driven animations, so there is no JavaScript and no
* scroll listener; browsers without them, and `prefers-reduced-motion`, keep
* the content still. Works in server components.
*
* It follows the nearest scroll container, and `overflow: hidden` makes one
* that never scrolls, so the content stays put. Clip a wrapper with
* `overflow: clip` (`overflow-clip`) instead.
*/
function Parallax({ asChild = false, offset = 40, className, style, ...props }: ParallaxProps) {
const Comp = asChild ? Slot : "div";
return (
<Comp
data-slot="parallax"
className={cn(
"supports-[animation-timeline:view()]:animate-parallax motion-reduce:!animate-none",
className
)}
style={
{
"--parallax-offset": `${offset}px`,
animationTimeline: "view()",
animationRange: "cover 0% cover 100%",
...style,
} as CSSProperties
}
{...props}
/>
);
}
export { Parallax, type ParallaxProps };