Container Scroll 3d
A container that rotates in 3D as you scroll to reveal its content, flattening out from a tilted-back angle to push the view right up to the viewer.
As the page scrolls, the container flattens out from a tilted-back perspective to face the viewer — a good fit for revealing a product screen, dashboard, or chart in a hero or feature section. useScroll binds to the element's travel through the viewport, so the flattening, scaling, and shadow are all driven by scroll progress.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add container-scroll-3d.enContainer Scroll 3d is a React scroll 3D animation component built with Tailwind CSS and Motion. It tracks the element's scroll progress with Motion's useScroll and rotates a card with a window bar from a tilted-back angle to face the viewer, which is useful for showing product screenshots or dashboards.
Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<ContainerScroll3d/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
When to use
- Showing the product below a SaaS hero, flattening a dashboard screenshot toward the viewer as they scroll.
- Highlighting a single key feature on a features page, with a headline before the screen is revealed.
- Launch pages for apps or desktop tools, with a screen recording or
videoinside the card. - Large diagrams or charts in docs sites and blog posts.
Installation
npx webberui@latest add https://webberui.com/r/container-scroll-3d.en.jsonOr, once registries are configured in components.json, install it as @webberui/container-scroll-3d.
Usage
import { ContainerScroll3d } from "@/components/ui/container-scroll-3d";
<ContainerScroll3d title={<h2 className="text-3xl font-bold">Scroll to reveal</h2>}>
<img src="/dashboard.png" alt="Dashboard" className="w-full" />
</ContainerScroll3d>;children is the content revealed once the card flattens out, and it can be any JSX: a screenshot, a video, a chart, or a live component. title is optional and rises slightly while fading in as you scroll.
Examples
Product screenshot with Next.js Image
children goes into the rounded screen area inside the card, so next/image works as is; title is the heading above the card that fades in.
import Image from "next/image";
import { ContainerScroll3d } from "@/components/ui/container-scroll-3d";
export function ProductShowcase() {
return (
<section className="py-24">
<ContainerScroll3d
title={
<>
<h2 className="text-4xl font-semibold tracking-tight">Every project in one table</h2>
<p className="mt-3 text-neutral-500">Progress, owners, and due dates on a single screen.</p>
</>
}
>
<Image src="/screens/dashboard.png" alt="Project overview screen" width={1600} height={1000} className="h-auto w-full" />
</ContainerScroll3d>
</section>
);
}Gentler angle with a recording
Lower rotate for a smaller starting tilt, raise perspective for flatter depth, and use cardClassName to change the card frame.
<ContainerScroll3d
rotate={12}
perspective={1600}
scaleFrom={1}
cardClassName="max-w-5xl"
>
<video src="/videos/editor.mp4" autoPlay loop muted playsInline className="w-full" />
</ContainerScroll3d>Inside a nested scroll container
If scrolling happens inside an overflow-y-auto container rather than the window, pass that container's ref to container.
"use client";
import * as React from "react";
import { ContainerScroll3d } from "@/components/ui/container-scroll-3d";
export function ScrollPanel() {
const scrollRef = React.useRef<HTMLDivElement>(null);
return (
<div ref={scrollRef} className="h-[600px] overflow-y-auto">
<div className="h-[400px]" />
<ContainerScroll3d container={scrollRef}>
<img src="/screens/report.png" alt="Monthly report screen" className="w-full" />
</ContainerScroll3d>
<div className="h-[400px]" />
</div>
);
}Auto-scrolling screen with depth of field
When the content is taller than the screen (a full-page screenshot, a list, a table), autoScroll turns the screen area into a 16:10 viewport that scrolls slowly up and down at about 24px per second while the user is idle, dwelling about 1.2 seconds at the top or bottom before reversing. It pauses on hover, on keyboard focus, or after the user's own wheel/touch input, and resumes after 2 seconds of idle time. dof adds a gradient mask and a light blur to the top and bottom edges of the screen, like a lens depth of field, so the scrolling content gets a sense of depth. The two can be used separately, and both are off by default.
<ContainerScroll3d autoScroll dof>
<img src="/screens/landing-full.png" alt="Full landing page screenshot" className="w-full" />
</ContainerScroll3d>Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | React.ReactNode | — | Title above the card; rises and fades in with the scroll |
children | React.ReactNode | — | The content revealed inside the card |
rotate | number | 22 | Initial backward tilt angle (deg); reaches zero and flattens mid-way |
scaleFrom | number | 1.05 | Initial scale, converging to 1 once flattened |
titleTranslate | number | 40 | Distance the title rises (px) |
perspective | number | 1000 | 3D perspective depth (px); smaller values exaggerate the perspective |
container | React.RefObject | — | Ref of the nested scroll container; the window is the scroll container by default |
className | string | — | Styling for the outer container |
cardClassName | string | — | Styling for the card frame |
autoScroll | boolean | false | Turns the screen into a 16:10 viewport that slowly scrolls up and down while idle; pauses on hover/focus and is disabled under reduced motion |
dof | boolean | false | Adds a gradient mask and blur to the top and bottom edges of the screen to simulate depth of field |
Accessibility
useScrollbinds to the element's travel through the viewport, so computation stops once it leaves the viewport and no extra resources are consumed- With
autoScrollon, the screen is a focusableregion(with anaria-label) that can be focused with Tab and scrolled manually with the arrow keys; auto-scroll pauses on focus or hover, and also while the screen is off-screen or the tab is hidden - The
dofblur layers arearia-hiddenand do not capture pointer events, so clicks and screen readers are unaffected - When the user has "reduce motion" enabled at the system level, the card is presented flat and static with no scroll transforms applied;
autoScrollis disabled too, and the screen can still be scrolled manually
FAQ
Does it work with the Next.js App Router?
Yes. The source already has "use client", so import it from a Server Component page directly; children can also be content rendered by Server Components. The first render matches the server output and only switches to the static version after mount when reduced motion is on.
Why doesn't the card ever flatten?
Progress starts when the top of the component enters the bottom of the viewport and ends when its bottom leaves the top, and the card flattens between progress 0.1 and 0.55. If there isn't enough scroll distance after the component, progress never gets there, so leave more content or space below it. If scrolling happens in a nested container, pass container.
How do I adjust the strength of the 3D effect?
rotate is the starting tilt (default 22 degrees), perspective is the perspective depth (default 1000px; smaller is more dramatic), scaleFrom is the starting scale (default 1.05), and titleTranslate is how far the title moves up.
Can I remove the three dots at the top of the card?
The window bar is part of ScrollCard in the source and has no prop; delete that JSX in your copied file.
What happens when the user prefers reduced motion?
The card is shown flat and static, and the title does not move or fade in.
Section Snap Pin
Section pinning plus snap-scroll choreography — full-height sections snap one by one to fill the viewport, content reveals and exits in order, with side navigation dots.
Hero Parallax Grid
A product/portfolio wall with alternating row parallax — each row sweeps in a different direction as you scroll, with a 3D reveal.