suiss/uim · Interaction

Pull To Refresh

Adds pull to refresh to a scroll view or list, with a minimum spinner time and a light haptic.

Pull To Refresh is an open-source mobile interaction from suiss/uim for React Native and Expo, with SwiftUI and Jetpack Compose versions built from the same design tokens. The @esuiss/uim CLI copies its source into your project, so you own the code.

iOS
Pull To Refresh on iOS in light mode

PullToRefresh is a ScrollView with a native RefreshControl. Pulling down past the top starts a refresh: a light impact haptic plays, the spinner shows, and onRefresh runs.

The spinner stays for at least 600 ms, even when onRefresh resolves sooner, so it does not flicker. It also hides if onRefresh throws or rejects. Use PullToRefreshControl to add the same behavior to a FlatList or SectionList.

Installation

$ pnpm dlx @esuiss/uim@latest add pull-to-refresh

Manual

  1. Install the dependencies:
npx expo install cn uniwind
  1. Add the pieces it builds on: the primitives components/primitives/haptics.tsx.

  2. Copy the source into components/interactions/pull-to-refresh.tsx:

uniwind/components/interactions/pull-to-refresh.tsx
import { cn } from "cn"
import * as React from "react"
import {
  type ColorValue,
  RefreshControl,
  type RefreshControlProps,
  ScrollView,
} from "react-native"
import { withUniwind } from "uniwind"
import { useHaptics } from "@/components/primitives/haptics"

const MIN_REFRESH = 600

type RefreshHandler = () => Promise<unknown> | unknown

function useRefresh(onRefresh: RefreshHandler) {
  1. Update the import paths to match your project.

Usage

import { PullToRefreshControl, PullToRefresh } from "@/components/interactions/pull-to-refresh"
<View className="flex-1">
  <Feed />
</View>

Pass an onRefresh that returns a promise, or nothing for synchronous work. All other ScrollView props pass through to PullToRefresh.

For lists, pass <PullToRefreshControl onRefresh={...} /> as the list's refreshControl. It manages its own refreshing state; pass refreshing to override it. Other RefreshControl props pass through.

Examples

With flat list

Uses PullToRefreshControl as the refreshControl of a FlatList.

iOS
Pull To Refresh (with flat list) on iOS in light mode

API Reference

PullToRefreshControl

A RefreshControl for any list or scroll view.

ExtendsRefreshControlProps
PropType
About
Called when the user pulls to refresh; the spinner waits for the returned promise.
Type
RefreshHandler
Default
required
About
Overrides the internal refreshing state.
Type
boolean
Default
—

PullToRefresh

A ScrollView with built-in pull to refresh.

ExtendsReact.ComponentProps<typeof ScrollView>
PropType
About
Called when the user pulls to refresh; the spinner waits for the returned promise.
Type
RefreshHandler
Default
required

Accessibility

  • PullToRefresh exposes a busy state while refreshing.
  • PullToRefreshControl is labelled Refreshing while active.

Notes

  • The haptic plays only if the app provides an adapter through HapticsProvider.
  • The spinner uses the label-secondary color, and on Android its background uses surface-raised, so both follow light and dark mode.