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, and for a real slice of carousel usage the honest answer is: you were building a menu all along.

this librarySwiperEmblakeen-sliderreact-slick
What it isScrolling menu with visibility trackingFull slider/carousel frameworkHeadless carousel engineFramework-agnostic slider engineReact port of the jQuery slick slider
Scroll engineNative browser scrollingJS transforms + physicsJS transforms + physicsJS transforms + physicsJS transforms (CSS transitions)
Bundle (core, min+gzip)≈5.7 kB≈40 kB≈8 kB≈7 kB≈15 kB + slick CSS
Which items are on screenBuilt in — per-item useIsVisibleSlide-index basedSlide-index eventsSlide-index eventsSlide-index based
Snap, effects, physicsNone — deliberatelyRich (fade, cube, coverflow…)Plugin-based, tweenableYes, incl. free modeFade, center mode
Loop / autoplayRecipes on the public APIBuilt-in propsPluginsBuilt-in optionsBuilt-in props
Scrollbar, wheel, keyboard focusNative — free from the browserEmulated / opt-in modulesDIY (headless)DIYLimited
Best forCategory rows, tab strips, chip filtersFullscreen sliders, galleriesCustom carousels (shadcn default)Minimal custom slidersLegacy 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.

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.

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, one-item stepping and drag to scroll 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, filter chips, load-more rows, and — the two features people assume need a carousel engine — infinite loop and autoplay, each about sixty lines on the public API.