1. Blocks
Loading preview...
  1. Blocks
  2. Scroll Gallery

component

Scroll Gallery

Full-viewport sticky scroll slider with GSAP-driven strip-mask transitions and animated title swaps.

Released November 14, 2026
Made by Axyl

Installation

npx shadcn@latest add @soralabs/scroll-gallery

File Structure

scroll-gallery.tsx
scroll-trigger-utils.tsx

Usage

Basic

import { ScrollGallery } from "@/components/sora-ui/blocks/scroll-gallery";

const slides = [
  { title: "Room 14B", image: "/images/slide-1.jpg", url: "/project/1" },
  {
    title: "Subject Identified",
    image: "/images/slide-2.jpg",
    url: "/project/2",
  },
];

export default function Page() {
  return <ScrollGallery slides={slides} />;
}

Editorial preset (variant="studio")

Full-bleed hero with a three-column info band (prefix, title, link) and large white type:

<ScrollGallery slides={slides} variant="studio" />

variant="minimal" (default) ships structural layout only — add your own typography via the classNames prop.

Custom class map (advanced)

Import SCROLL_GALLERY_STUDIO_CLASSES to extend or partially override the studio preset:

import {
  SCROLL_GALLERY_STUDIO_CLASSES as studio,
  ScrollGallery,
} from "@/components/sora-ui/blocks/scroll-gallery";

<ScrollGallery
  variant="studio"
  classNames={{ titleText: cn(studio.titleText, "text-amber-200") }}
  slides={slides}
/>;

Custom labels & timing

<ScrollGallery
  slides={slides}
  prefixLabel="Projects"
  linkLabel="View case study"
  showPrefix={false}
  scrollPerTransition={800}
  timing={{
    titleChangeThreshold: 0.25,
    scaleFrom: 1.15,
    stripRevealSpeed: 2.5,
  }}
/>

Place ScrollGallery at the root scroll level of the page. GSAP pin requires the section to scroll within window — it will not work correctly inside a custom scroll container unless you pass scroller.

Use high-quality full-bleed images (at least 1920 × 1080). The component crops to object-fit: cover so landscape images work best.

Props

PropTypeDefault
slides?
ScrollGallerySlide[]
-
prefixLabel?
string
Featured
linkLabel?
string
Explore
showPrefix?
boolean
true
showLink?
boolean
true
showInfoBand?
boolean
true
stripsCount?
number
20
scrollPerTransition?
number
1000
scrub?
number
1
pinStart?
string
top top
refreshPriority?
number
-1
scroller?
Element | Window
-
timing?
ScrollGalleryTiming
-
className?
string
-
variant?
"minimal" | "studio"
minimal
classNames?
ScrollGalleryClassNames
-

ScrollGallerySlide

PropTypeDefault
title?
string
-
image?
string
-
url?
string
-
linkLabel?
string
-

ScrollGalleryTiming

PropTypeDefault
initialDelay?
number
300
finalDelay?
number
300
titleChangeThreshold?
number
0.3
titleDuration?
number
0.3
titleEase?
string
power2.out
titleOffset?
string
120%
scaleFrom?
number
1.25
scaleTo?
number
1
stripRevealSpeed?
number
2

When to Use

Use ScrollGallery for creative agency home pages, portfolio showcases, or editorial sections where each project or item deserves a dedicated full-screen moment. The pinned scroll pattern forces a deliberate, focused presentation — the user can only advance by scrolling, which creates a chapter-by-chapter rhythm.

Provide at least 3 slides for the scroll distance to feel earned. With fewer than 3, the section feels short and the pin may disorient users.

Accessibility

The title element stays in the DOM for screen readers while GSAP animates it. Images use accessible alt text from the slide title, and the CTA link remains keyboard-focusable and updates href and linkLabel on each slide transition.

Sticky Scroll Cards

Scroll-pinned hero where a front card flips to reveal a stack of back cards, dismissed one at a time as you keep scrolling.

Scroll Chapters

A draggable, chaptered progress-bar nav — two-way cursor/scroll sync, ghost-cursor hover preview, snap-to-step seeking, and a per-chapter panel that can opt in or out of showing.

Built by Axyl. A motion-first component registry for React.

Loading preview...