---
title: "Netflix-style horizontal row in React"
description: "Build a Netflix-style category row in React with native scrolling: hover arrows, edge fade, drag to scroll, visibility tracking. Live demo and full source."
canonical: "https://react-horizontal-scrolling-menu.dev/netflix-row"
image: "https://react-horizontal-scrolling-menu.dev/og.png"
---

# Netflix-style horizontal row in React

The row of posters you scrub through on every streaming site is not a carousel — it is a menu. Nothing snaps, nothing autoplays; it rides native momentum scrolling with arrows overlaid on top. That is exactly what `react-horizontal-scrolling-menu` ships: your cards, native scroll, and per-item visibility so the arrows know when to hide.

Sci-Fi

Comedy

Drama

Horror

Docs

Kids

Action

Indie

Anime

Classics

Thriller

Reality

Drag it, or hover the row — the arrows fade in over the edges, and each disappears when its end of the row is reached.

[Edit this example live in Storybook](https://asmyshlyaev177.github.io/react-horizontal-scrolling-menu/?path=/story/examples-mousedrag--mouse-drag)

## Why this is not a carousel job

A Netflix row never shows one slide at a time. Items are partially cut off at the edges on purpose — the cut-off poster is the affordance that says "there's more". Carousel engines fight this: they own the gesture layer with JavaScript transforms, snap to slide boundaries, and re-implement the momentum your users' browsers already have. On a row of clickable cards all of that is overhead.

Native scrolling gives you momentum, touch, trackpad and scrollbar for free. The two things it does not give you are the overlay arrows and knowing which cards are on screen — and those are the two things this library adds, via [`useIsVisible`](https://react-horizontal-scrolling-menu.dev/examples/simple.md) per item and edge-aware arrow state.

## The three details that sell the effect

-   **Arrows overlay the content**, they don't sit beside it. Render them absolutely positioned over the row ends (the demo above passes them through `Header` so they stay inside the menu's context), show them on hover, and hide each one when [`useLeftArrowVisible` / `useRightArrowVisible`](https://react-horizontal-scrolling-menu.dev/examples/simple.md) report that end of the row is reached.
-   **Edges fade.** One line of CSS — a `mask-image` gradient on the scroll container — replaces the "peek" logic carousel plugins ship for this.
-   **Drag must not fire clicks.** A mouse drag that ends on a poster must not open it. The [drag-to-scroll recipe](https://react-horizontal-scrolling-menu.dev/examples/mouse-drag.md) tracks drag state and swallows exactly that click.

## Scaling it: lazy rows and long rails

Streaming UIs stack dozens of rows with hundreds of cards. Because items are plain DOM in a native scroll container, nothing re-renders on scroll — the [performance example](https://react-horizontal-scrolling-menu.dev/examples/performance.md) runs 300 items without virtualization. Per-item visibility also gives you image lazy-loading for free: render a placeholder until `useIsVisible` reports the card on screen.

If your row should wrap around at the end, that is the one place slide semantics genuinely help — see the [infinite loop recipe](https://react-horizontal-scrolling-menu.dev/examples/infinite-loop.md) for the ~60-line userland version before reaching for a carousel engine.

## The pattern, minimal

Overlay arrows over a native-scroll row — the demo above is this structure plus styling. Complete drop-in source, with drag and edge fade, ships as the shadcn component below.

MediaRow.tsx

```
function MediaRow({ children }: { children: React.ReactNode }) {
  return (
    <div className="group relative">
      <ScrollMenu Header={<OverlayArrows />} wrapperClassName="edge-fade">
        {children}
      </ScrollMenu>
    </div>
  );
}

// Rendered as Header, so it lives inside the menu's VisibilityContext
// and can sit absolutely positioned over the row ends.
function OverlayArrows() {
  const api = React.useContext<publicApiType>(VisibilityContext);
  const atStart = api.useLeftArrowVisible();
  const atEnd = api.useRightArrowVisible();
  return (
    <>
      <button
        className="overlay-arrow left-0"
        hidden={atStart}
        onClick={() => api.scrollPrev()}
      >
        ←
      </button>
      <button
        className="overlay-arrow right-0"
        hidden={atEnd}
        onClick={() => api.scrollNext()}
      >
        →
      </button>
    </>
  );
}

/* .edge-fade — the Netflix edge, one CSS line:
   mask-image: linear-gradient(to right,
     transparent, black 40px, black calc(100% - 40px), transparent); */
```

## Or install it as a shadcn component

The [media-row](https://react-horizontal-scrolling-menu.dev/r/media-row.json) registry item is this exact pattern — hover arrows, gradient edge fade, drag to scroll — as a Tailwind-styled component in your `components/ui/`, yours to edit:

shadcn

```
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/media-row.json
```

## Related examples

-   [Drag to scroll with the mouseMouse drag that still lets item clicks work.](https://react-horizontal-scrolling-menu.dev/examples/mouse-drag.md)
-   [Infinite loopSeamless looping from the public API — no library changes.](https://react-horizontal-scrolling-menu.dev/examples/infinite-loop.md)
-   [5,000 items and still fastNative scrolling scales — no virtualization needed here.](https://react-horizontal-scrolling-menu.dev/examples/performance.md)

[All 21 examples](https://react-horizontal-scrolling-menu.dev/examples.md)

---

More examples: <https://react-horizontal-scrolling-menu.dev/examples.md>
Library summary for LLMs: <https://react-horizontal-scrolling-menu.dev/llms.txt>
