Skip to content
Template Empire
Browse components

Horizontal Scroll

A free horizontal scroll section that glides panels sideways as you scroll.

Rotterdam

Booking PW-40118

Booking

Lock a lane in ninety seconds

Live carrier rates, equipment and cutoffs land on one screen. Your desk books the sailing while the quote is still valid, and the paperwork files itself behind it.

Rotterdam, Maasvlakte II · Booked 12 Aug 09:41 CEST

Mid-Atlantic

Booking PW-40118

In transit

Every box on one moving line

Vessel position, reefer temperature and berth congestion stream into the same timeline, so a six hour delay reaches your customer before it reaches your inbox.

41.2°N 38.7°W · Day 6 of 11 · Northline Atlantic

Newark

Booking PW-40118

Delivered

Signed at the gate, invoiced by lunch

Proof of delivery closes the leg and releases the invoice the same hour. Finance sees the cash days earlier, and the lane's real cost feeds straight into the next quote.

Newark, PNCT berth 4 · Delivered 23 Aug 11:48 EDT

Horizontal Scroll is a free scroll section for Next.js and React that sticks to the viewport and slides between two and six panels sideways as the page scrolls, built with Motion and styled with Tailwind. You pass your panels as plain children, one per direct child, and the component builds the flex row track, the sticky frame, the scrub and the reduced motion fallback for you.

The section is pinned with CSS position: sticky inside a track that reserves the scroll distance, so scrolling stays native: wheel, keyboard and touch all carry you straight through and past the section, and nothing shifts. The pinned layout is part of the server-rendered page, so a reload or the Back button returns you to the same point in the scene. With prefers-reduced-motion enabled, or with JavaScript unavailable, the panels render as a static vertical stack, fully visible 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-horizontal

Usage

import { ScrollHorizontal } from "@/components/scroll-horizontal";

export default function Page() {
  return (
    <ScrollHorizontal>
      <div>
        <h2>Scene one</h2>
        <p>The section pins and scroll turns sideways.</p>
      </div>
      <div>
        <h2>Scene two</h2>
        <p>Each panel glides across, tied to the scrub.</p>
      </div>
      <div>
        <h2>Scene three</h2>
        <p>Scroll on and the pin releases.</p>
      </div>
    </ScrollHorizontal>
  );
}

Props

PropTypeDefaultDescription
childrenReactNoderequiredPanel content. Pass 2 to 6 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

Horizontal Scroll 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 Horizontal Scroll free for commercial use?
Yes. Horizontal Scroll is MIT licensed, so you can use it in personal and commercial projects without attribution.
Does Horizontal Scroll trap scrolling or hurt accessibility?
No. The sideways travel is scrubbed from native vertical 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, with no snap points. The track reserves the full scroll distance up front, so pinning causes no layout shift, and the scrub animates a single transform on the row of panels. Every panel stays visible and Tab reachable in both modes, focusing content in an offscreen panel scrolls the page to that panel so its focus ring is in view, and with reduced motion enabled the panels render as a static vertical stack with nothing listening to scroll.
Why does the section scroll past instead of sticking?
The pin is CSS position: sticky, which needs a clear path to the page's scroller. If any ancestor between the section and the page sets overflow to hidden, auto or scroll, sticky stops working there; use overflow: clip on that ancestor instead, which clips the same way without creating a scroll container. The section also stays stacked where a panel's own content is taller than the viewport, because pinned content below a panel's fold could never be scrolled to.
How do I change the number of panels or their content?
Pass between two and six direct children to the component; each direct child becomes one panel in DOM order, and the scroll distance scales with the panel count automatically. 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