react-simple-pull-to-refresh

A zero-dependency React component that adds pull-to-refresh and fetch-more gestures to any scrollable list.

Library
npm
v1.3.4
191stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
37/100Needs Attention
Development Activity8
Maintenance0
Community60
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
48/100Fair
Architecture58
Code Quality35
Innovation55
Learning Curve45

react-simple-pull-to-refresh is a lightweight React component that adds a native-style pull-to-refresh gesture to any scrollable list, along with an optional infinite-scroll “fetch more” trigger for paginated content. It ships with zero runtime dependencies, works with touch and mouse input alike, and exposes simple props for tuning pull distance, resistance, and the loading/pulling indicators shown during a refresh.

Originally published in 2019, the library has stayed maintained through incremental peer-dependency bumps — most recently adding React 19 support — while keeping its API surface small: wrap a list’s children in the component and provide an onRefresh handler that returns a promise.

What You Get

  • Drag-to-refresh gesture handling for touch and mouse input, with configurable pull distance and resistance
  • Built-in infinite-scroll “fetch more” trigger when the user nears the bottom of the list
  • Swappable loading/pulling indicator components via refreshingContent/pullingContent props
  • Zero runtime dependencies — only peer-depends on React/ReactDOM
  • Typed props and a bundled .d.ts declaration file for TypeScript projects

Common Use Cases

  • Refreshing a news or social feed list when the user pulls down on mobile
  • Paginating a long list (infinite scroll) by fetching the next page near the bottom
  • Adding native-app-like pull gestures to a PWA or mobile web app
  • Wrapping a dashboard or table view so it can be manually refreshed on mobile

Under The Hood

Architecture The library is a single, flat React component (src/components/pull-to-refresh.tsx) re-exported as the package’s default export from src/index.ts. All drag/pull/fetch-more logic lives inside one useEffect that wires native touchstart/mousedown/touchmove/mousemove/scroll/touchend/mouseup/mouseleave listeners directly onto the children container ref, with a second useEffect handling the edge case where canFetchMore becomes true while already scrolled to the bottom. State (isDragging, startY, currentY, threshold-breached flags) is tracked as plain mutable closure variables rather than React state, and visual updates are applied imperatively via direct style/className writes on refs instead of re-renders — a deliberate performance tradeoff for a gesture that fires on every touchmove. Scroll-ancestor detection is cleanly isolated into its own pure module (isScrollable.ts), which recursively walks up the DOM tree to decide whether an ancestor should consume the scroll instead of the pull gesture. There’s no DI, plugin system, or internal service boundary — it’s one cohesive gesture-handling component, so changing its core imperative-ref approach would mean rewriting the whole interaction model.

Tech Stack Written in TypeScript (tsc 3.7.5) and bundled with Rollup 1.x (rollup-plugin-typescript2 for compilation, rollup-plugin-postcss for the bundled Sass styles, rollup-plugin-delete to clean prior output), producing dual CJS and ESM builds plus a .d.ts declaration file. React and ReactDOM are peer dependencies only (versions ^16.10.2 through ^19.0.0), and the package declares zero runtime dependencies, matching its “0 dependency” README claim. A nested Create React App project under playground/ serves as both the local dev harness (build-watch plus start-playground run in parallel) and the source for the GitHub Pages demo, generated by copying the playground build output into docs/.

Code Quality There are no test files anywhere in the repository (no tests, .test., or .spec. files) and no CI configuration (no .github/workflows), so correctness is verified only by manual use of the playground demo rather than any automated pipeline. The code is typed with tsconfig’s strict mode enabled, uses an interface for component props and an enum (DIRECTION) in place of magic strings, and file/variable naming is consistently kebab-case/camelCase. Error handling is minimal: promises returned from onRefresh/onFetchMore are chained with .then(initContainer).catch(initContainer), resetting the UI identically on success or failure and silently discarding any rejection reason. There is no ESLint configuration, only a bare Prettier config.

API Design The public surface is intentionally tiny — wrap children in the component and pass an onRefresh handler — with everything else (thresholds, resistance, spinner/pulling content) defaulted and overridable via props. Combining pull-to-refresh with an on-scroll “fetch more” trigger in a single component is the one distinctive design choice; most comparable npm packages handle only the pull gesture. Zero runtime dependencies keep the bundle-size and supply-chain cost at zero, and the README documents every prop with its type, default, and behavior in a single table, which is unusually thorough for a package this small.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search