---
title: "react-horizontal-scrolling-menu vs Swiper, Embla, react-slick"
description: "An honest comparison: when a horizontal scrolling menu beats a carousel library, and when it does not. Swiper, Embla, keen-slider and react-slick, side by side."
canonical: "https://react-horizontal-scrolling-menu.dev/compare"
image: "https://react-horizontal-scrolling-menu.dev/og.png"
---

# Carousel or scrolling menu? An honest comparison

Swiper, Embla, keen-slider and react-slick are carousel engines: they re-implement scrolling in JavaScript to get slide semantics, snap physics and effects. react-horizontal-scrolling-menu is not one of them — it rides native browser scrolling and adds per-item visibility tracking. Which one you want depends on what you’re building — the table and the notes below lay it out honestly, both ways.

|  | this library | Swiper | Embla | keen-slider | react-slick |
| --- | --- | --- | --- | --- | --- |
| What it is | Scrolling menu with visibility tracking | Full slider/carousel framework | Headless carousel engine | Framework-agnostic slider engine | React port of the jQuery slick slider |
| Scroll engine | Native browser scrolling | JS transforms + physics | JS transforms + physics | JS transforms + physics | JS transforms (CSS transitions) |
| Bundle (core, min+gzip) | ≈5.7 kB | ≈40 kB | ≈8 kB | ≈7 kB | ≈15 kB + slick CSS |
| Which items are on screen | Built in — per-item useIsVisible | Slide-index based | Slide-index events | Slide-index events | Slide-index based |
| Snap, effects, physics | None — deliberately | Rich (fade, cube, coverflow…) | Plugin-based, tweenable | Yes, incl. free mode | Fade, center mode |
| Loop / autoplay | Recipes on the public API | Built-in props | Plugins | Built-in options | Built-in props |
| Scrollbar, wheel, keyboard focus | Native — free from the browser | Emulated / opt-in modules | DIY (headless) | DIY | Limited |
| Best for | Category rows, tab strips, chip filters | Fullscreen sliders, galleries | Custom carousels (shadcn default) | Minimal custom sliders | Legacy slick migrations |

Bundle sizes are approximate cores — check bundlephobia for current numbers before deciding on size alone.

## First, the real question

A **carousel** presents slides: one thing (or one page of things) at a time, with snapping, effects and a sense of “position 3 of 8”. A **menu** presents a row your user scans and picks from: a category rail, a tab strip, a chip bar. Carousels want slide semantics; menus want native scrolling — momentum, scrollbar, wheel, touch and keyboard focus behaving exactly like the rest of the page — plus one thing the browser doesn’t give you: knowing which items are on screen.

If you’re building a fullscreen image slider, a hero gallery, or anything with snap-to-slide physics, **use a carousel library — Embla or Swiper are excellent**. This page exists for the other case, the one every carousel FAQ quietly ignores: rows of clickable things that were never really slides.

## vs Swiper

Swiper is the most complete slider framework there is: effects (fade, cube, coverflow), virtual slides, zoom, parallax, pagination, and a mature ecosystem. It earns its ~40 kB when you use what it ships. It re-implements scrolling with transforms, so the native scrollbar, wheel behavior and scroll accessibility are emulations you configure rather than defaults you inherit.

-   **Choose Swiper** for image-first sliders, effects, or anything that must feel like slides.
-   **Choose this library** when the “carousel” is a YouTube-style chip bar or a Netflix-style category row: you get native scroll for ~34 kB less, plus `useIsVisible` per item — which Swiper doesn’t model, because slides aren’t items.

## vs Embla

Embla is a headless carousel engine with beautiful physics and a first-class React adapter — it’s what shadcn/ui builds its carousel on, and the right default when you want full visual control over a real carousel. Headless cuts both ways for menus: scroll-into-view on selection, per-item visibility, arrow disabling and focus management are all yours to hand-build.

-   **Choose Embla** for custom-designed carousels and snap physics with small size.
-   **Choose this library** when those hand-built parts are the whole point: `scrollToItem`, `useIsVisible`, first/last arrow state and `apiRef` ship working.

## vs keen-slider

keen-slider is a lean, framework-agnostic slider engine — a good pick for minimal custom sliders when you want one dependency across frameworks. Like the others it owns the gesture layer with transforms, and its API is slide-index-shaped: fine for slides, awkward for “scroll the selected chip into view and tell me what’s visible”.

## vs react-slick

react-slick ports the jQuery-era slick carousel to React. It still works, but it drags in a separate CSS file, its architecture predates hooks, and maintenance is sparse. Teams leaving it usually fall into two camps: real carousels (go to Embla or Swiper) — and navigation rows that were bent into `centerMode` because slick was already installed. That second camp is this library’s exact shape: [centered selection](https://react-horizontal-scrolling-menu.dev/examples/center-on-click.md), [one-item stepping](https://react-horizontal-scrolling-menu.dev/examples/one-item-scroll.md) and [drag to scroll](https://react-horizontal-scrolling-menu.dev/examples/mouse-drag.md) without a slider engine.

## What the menu side looks like

Every pattern on this site is live and server-rendered, each with its complete source: [scrollable tabs](https://react-horizontal-scrolling-menu.dev/examples/center-on-click.md), [filter chips](https://react-horizontal-scrolling-menu.dev/examples/add-item-and-scroll-to-it.md), [load-more rows](https://react-horizontal-scrolling-menu.dev/examples/add-items.md), and — the two features people assume need a carousel engine — [infinite loop](https://react-horizontal-scrolling-menu.dev/examples/infinite-loop.md) and [autoplay](https://react-horizontal-scrolling-menu.dev/examples/autoplay.md), each about sixty lines on the public API.

-   5.7 kB min+gzip, TypeScript-first, MIT, ~347k downloads/month, maintained since 2018 with one stable API across React 16.8–19.
-   SSR-friendly: the row scrolls before your JavaScript hydrates — this page and every demo on this site prove it.

## More comparisons

Deeper pages on the specific choices people actually weigh.

-   [Embla vs SwiperHeadless engine or batteries included — and the case where you need neither.](https://react-horizontal-scrolling-menu.dev/compare/embla-vs-swiper.md)
-   [react-slick alternativesWhere to migrate real carousels — and where the centerMode rows should go.](https://react-horizontal-scrolling-menu.dev/compare/react-slick-alternatives.md)
-   [Swiper alternativesWhen ≈40 kB is the complaint: lighter engines, and the menu-shaped escape hatch.](https://react-horizontal-scrolling-menu.dev/compare/swiper-alternatives.md)

[Browse all examples](https://react-horizontal-scrolling-menu.dev/examples.md) · [Try it in Storybook](https://asmyshlyaev177.github.io/react-horizontal-scrolling-menu/?path=/story/examples-simple--simple) [GitHub](https://github.com/asmyshlyaev177/react-horizontal-scrolling-menu)

---

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