Cross-platform native Pull-to-Refresh for React Native — with New Architecture (Fabric) support
This package is a fork and secondary development of the PullToRefresh module from react-native-troika by listenzz.
We kept the proven API and multi-platform native implementation (Android SmartRefreshLayout, iOS MJRefresh, HarmonyOS native layer), and extended it with React Native New Architecture (Fabric) support on Android, iOS, and HarmonyOS — addressing the biggest gap in the upstream 0.x line.
Upstream reference: listenzz/react-native-troika · Original PullToRefresh docs: packages/pull-to-refresh
Pull-to-refresh is everywhere in mobile apps, but choosing a library in the React Native ecosystem often means painful trade-offs:
| Library | Platforms | Pull + Load more | New Architecture (Fabric) | Last release |
|---|---|---|---|---|
| react-native-troika/pull-to-refresh (upstream 0.x) | Android · iOS · Harmony | ✅ Rich (SmartRefresh on Android) | ❌ No Fabric | 2025 |
| react-native-MJRefresh | iOS · Harmony only | Limited | ❌ | 2019 |
| react-native-pull | All (pure JS) | Partial, known issues | ✅ | 2017 |
| react-native-SmartRefreshLayout | Android · Harmony only | Partial | ❌ | 2019 |
| Gesture / Reanimated–based solutions | All | Varies | ✅ | Requires RN ≥ 0.74 |
-
No New Architecture on the best cross-platform option
Upstream react-native-troika 0.x works well on Android, iOS, and Harmony with customizable headers/footers and load-more — but does not support Fabric. Teams enabling the New Architecture cannot use it without forking. -
Fabric-ready alternatives force a heavy RN upgrade
Gesture-based pull-to-refresh stacks typically require React Native 0.74+ and related dependency bumps (react-native-gesture-handler,react-native-reanimated, etc.). Many brownfield apps stay on older RN versions and cannot adopt them quickly. -
Fragmented platform coverage
MJRefresh wrappers often skip Android; SmartRefresh wrappers skip iOS. Maintaining two libraries doubles integration and QA cost. -
Stale or unmaintained packages
Several community libraries have not been updated since 2017–2019, with poor compatibility on modern RN, Gradle, and Xcode toolchains. -
Pure JS refresh controls fight native scroll
JS-only solutions are easier to ship but tend to conflict with nested scrolling,FlatList/ScrollViewgestures, and native refresh UX expectations.
- ✅ Same mental model as troika —
PullToRefresh, customPullToRefreshHeader/PullToRefreshFooter, load-more - ✅ Android · iOS · HarmonyOS in one package
- ✅ Old Architecture + New Architecture (Fabric) — runtime detection and Fabric-native components where enabled
- Native pull-to-refresh and load-more (footer)
- Fully customizable refresh header and footer in React
- Refresh state and offset events for custom animations
- Android: SmartRefreshLayout
- iOS: MJRefresh
- HarmonyOS: native C++ Fabric integration
- Optional
RefreshControl-style wrapper for Android
| Version | RN version | Architecture |
|---|---|---|
| 0.x | >= 0.67 | Legacy (Paper) |
| 1.x (WIP) | >= 0.76 | New Architecture (Fabric) |
| RN version | Notes |
|---|---|
| 0.72+ | Baseline for legacy arch usage |
| 0.76+ | Recommended when enabling Fabric / 1.x line |
yarn add rn-pull-to-refresh
# or
npm install rn-pull-to-refreshcd ios && pod installMJRefresh is declared in RNPullToRefresh.podspec.
Auto-linked via React Native autolinking. Uses SmartRefreshLayout on the native side.
Include the harmony/library module in your OpenHarmony / RNOH project per your monorepo conventions.
import React, {useState, useCallback} from 'react';
import {FlatList, Text} from 'react-native';
import {PullToRefresh} from 'rn-pull-to-refresh';
export function ListScreen() {
const [refreshing, setRefreshing] = useState(false);
const [data, setData] = useState<string[]>([]);
const onRefresh = useCallback(async () => {
setRefreshing(true);
await fetchData();
setRefreshing(false);
}, []);
return (
<PullToRefresh style={{flex: 1}} onRefresh={onRefresh} refreshing={refreshing}>
<FlatList
data={data}
renderItem={({item}) => <Text>{item}</Text>}
keyExtractor={(_, i) => String(i)}
/>
</PullToRefresh>
);
}import {PullToRefresh, PullToRefreshHeader} from 'rn-pull-to-refresh';
function MyHeader(props) {
return (
<PullToRefreshHeader {...props}>
{/* your UI */}
</PullToRefreshHeader>
);
}
<PullToRefresh header={<MyHeader onRefresh={onRefresh} refreshing={refreshing} />} ... /><PullToRefresh
onLoadMore={loadMore}
loadingMore={loadingMore}
noMoreData={noMoreData}
...
/>import {PullToRefresh} from 'rn-pull-to-refresh';
PullToRefresh.setDefaultHeader(MyHeader);
PullToRefresh.setDefaultFooter(MyFooter);| Export | Description |
|---|---|
PullToRefresh |
Container wrapping scrollable children |
PullToRefreshHeader |
Native header slot for custom UI |
PullToRefreshFooter |
Native footer slot (load more) |
RefreshControl |
Android-friendly helper aligned with RN RefreshControl props |
See src/types.ts for PullToRefreshProps, state constants (PullToRefreshStateIdle, etc.), and event types.
MIT — see LICENSE.
Derived from react-native-troika (MIT). Please retain attribution when redistributing.
