npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@goodmanlabs/react-swipe-row

v1.0.0

Published

A native-feeling horizontal scroll-snap card rail for React

Readme

npm CI license

react-swipe-row

A native-feeling horizontal “card rail” for React — momentum scrolling + scroll-snap alignment, with optional desktop paging controls.

This was built to solve a common UI problem: you want a horizontally scrollable row of arbitrary content (cards, tiles, chips, etc.) where the number and size of items is unknown, and you don’t want a “slide-based carousel” that feels artificial or forces layout math.

react-swipe-row is intentionally unopinionated:

  • You provide the content (any React nodes)
  • The row scrolls naturally (trackpad, mousewheel, touch)
  • Scroll-snap keeps items aligned
  • Optional left/right controls appear automatically on “desktop-like” pointers
  • Control styling (buttons) and item spacing are exposed via props (no need to target internal DOM or CSS)

Written in TypeScript and ships with types; works in both TypeScript and plain JavaScript projects.


Live Demo

👉 https://react-swipe-row.vercel.app/

An interactive demo showing all supported props, behaviors, and styling hooks. Used in production on campvue.com.


Install

npm install @goodmanlabs/react-swipe-row
# or
pnpm add @goodmanlabs/react-swipe-row
# or
yarn add @goodmanlabs/react-swipe-row

Usage

import SwipeRow from '@goodmanlabs/react-swipe-row';
import '@goodmanlabs/react-swipe-row/style.css';

export default function Example() {
  return (
    <SwipeRow>
      <div className="card">One</div>
      <div className="card">Two</div>
      <div className="card">Three</div>
    </SwipeRow>
  );
}

Note: You must import the provided CSS for base layout, snapping behavior, and default control styling. For Next.js users, (App Router): import this CSS in app/layout.(js|tsx) (or your global stylesheet) per Next’s global CSS rules.

Framework notes

This component uses React hooks. In Next.js App Router, it’s treated as a client component automatically—no extra "use client" wrapper is needed to consume it. In non-Next React apps (Vite, CRA, Remix, etc.), this has no special impact; it behaves like a normal React component.

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | items | React.ReactNode[] | — | Optional array of React nodes to render. If provided, it takes precedence over children. | | children | React.ReactNode | — | Content to render if items is not provided. | | ariaLabel | string | "Scrollable content" | Accessible label for the scroll region (role="region"). | | snap | boolean | true | Enables/disables scroll-snap behavior (applies rsr-snap + rsr-snap-item). | | pageFactor | number | 0.9 | How far the controls and keyboard paging move, as a fraction of the scroller’s clientWidth. | | showControls | "auto" \| "always" \| "never" | "auto" | auto shows controls only on “desktop-like” pointers ((hover: hover) and (pointer: fine)), always forces them on, never hides them. | | id | string | auto-generated | Optional stable id used for aria-controls. If omitted, a React useId() value is used. | | className | string | — | Extra class(es) applied to the outer wrapper (rsr-root). | | gapClassName | string | — | Optional spacing hook applied to the scroller (e.g. Tailwind gap-*, or any consumer-defined class). | | classNames | SwipeRowClassNames | — | Optional granular class hooks for styling without targeting internals. | | scrollerStyle | React.CSSProperties | — | Inline style passthrough for the scroller. Merged after defaults (so it can override). |

SwipeRowClassNames

type SwipeRowClassNames = {
  root?: string;          // outer wrapper
  scroller?: string;      // the horizontal scroller element
  item?: string;          // wrapper around each item (snap target)
  controlButton?: string; // shared class for both buttons
  prevButton?: string;    // prev button
  nextButton?: string;    // next button
};

Styling controls (buttons)

SwipeRow ships with neutral default button styles.

  • If you provide any of classNames.controlButton, classNames.prevButton, or classNames.nextButton, the default button styling is automatically disabled so your classes take full control (no !important needed).
  • Use prevButton / nextButton for positioning (e.g. left-2, right-2) and controlButton for shared button styling.

Accessibility & Behavior

  • The scroll container is keyboard-focusable and supports left/right arrow key paging.
  • Paging controls use semantic <button> elements with accessible labels.
  • Scroll behavior relies on native browser momentum scrolling and scroll-snap, not JavaScript-driven carousels.
  • On touch-first devices, paging controls are hidden by default to avoid interfering with native gestures.

License

MIT © Glenn Goodman