Skip to content
Template Empire / components

Search components

10 results

Browse components

Pinned Panels

A free pinned scroll section that crossfades panels in place as you scroll.

  • Automated axe check
  • Reduced-motion tested
  • SSR build verified
  • GSAP
  • 2 runtime packages

Pinned Panels is a free scroll section for Next.js and React that pins to the viewport and crossfades between two and five panels in place as the page scrolls, driven by a single GSAP ScrollTrigger scrub and styled with Tailwind. You pass your panels as plain children, one per direct child, and the component builds the timeline, the pin and the reduced motion fallback for you.

The scrub follows native scrolling and never blocks it, so wheel, keyboard and touch all carry you straight through and past the section, and the pin spacer reserves the scroll distance so pinning causes no layout shift. With prefers-reduced-motion enabled, or with JavaScript unavailable, no ScrollTrigger is created at all: the panels render as a static stack, fully visible in order.

Chapter 01

The story holds still

As the section reaches the top of the viewport it pins in place. The page keeps scrolling underneath, and that scroll distance becomes the timeline for everything that follows.

Chapter 02

Panels crossfade in place

Each panel rises and fades in over the last, tied directly to the scrub. Scroll slowly and the story moves slowly; stop and it stops with you, mid-frame.

Chapter 03

Scroll on when you are ready

Nothing is trapped. Wheel, keyboard or touch carries you straight past the final panel, and with reduced motion the three chapters simply stack in order.

Install

Add the @te registry to your components.json once:

{
  "registries": {
    "@te": "https://templateempire.io/r/{name}.json"
  }
}

Then install the component:

npx shadcn@latest add @te/scroll-pinned-panels

Usage

import { ScrollPinnedPanels } from "@/components/scroll-pinned-panels";

export default function Page() {
  return (
    <ScrollPinnedPanels>
      <div>
        <h2>Chapter one</h2>
        <p>The section pins while the story plays.</p>
      </div>
      <div>
        <h2>Chapter two</h2>
        <p>Each panel crossfades in over the last.</p>
      </div>
      <div>
        <h2>Chapter three</h2>
        <p>Scroll on and the pin releases.</p>
      </div>
    </ScrollPinnedPanels>
  );
}

Props

PropTypeDefaultDescription
childrenReactNoderequiredPanel content. Pass 2 to 5 direct children; each direct child is rendered inside its own [data-panel] wrapper as one panel, in DOM order.
classNamestringundefinedAdditional classes merged onto the section root.

Customisation

Pinned Panels declares no tokens of its own: the section and each panel surface use the semantic background token, and everything inside a panel is your own markup, styled however you like. To retheme the section, restyle your panel content directly or override the semantic tokens it consumes from :root or any ancestor:

:root {
  --background: var(--color-neutral-900);
  --foreground: var(--color-neutral-50);
}

Frequently asked questions

Is Pinned Panels free for commercial use?
Yes. Pinned Panels is MIT licensed, so you can use it in personal and commercial projects without attribution.
Does Pinned Panels trap scrolling or hurt accessibility?
No. The crossfade is scrubbed from native scroll and never intercepts it, so mouse wheel, trackpad, touch and keyboard keys such as arrows, space and PageDown all move through and past the section uninterrupted. The pin spacer reserves the full scroll distance up front, so pinning causes no layout shift, and the timeline animates only transforms and opacity. Panels that are not yet on screen sit at visibility hidden, so keyboard users tabbing through the page never land on invisible content, and with reduced motion enabled the panels render as a static stack with no ScrollTrigger created at all.
How do I change the number of panels or their content?
Pass between two and five direct children to the component; each direct child becomes one panel in DOM order. Panels are plain containers, so headings, text, images and layout inside each one are entirely your own markup and Tailwind classes, and heading levels stay under your control.

Related components

Want the full page this belongs to?Template Empire builds complete Next.js templates with this motion system throughout. Browse the paid templates that pair with these free components.Explore Template Empire templates