suiss/uim · Interaction

Reorder List

A list whose rows are dragged by a handle to change their order, with haptics and move up and down actions.

Reorder List 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
Reorder List on iOS in light mode

ReorderList renders its ReorderListItems in the order given by value. Dragging a row's ReorderListHandle lifts the row: a raised surface with a shadow fades in behind it and, outside an inset group, the row grows slightly. It follows the finger while the other rows spring out of the way.

On release the row springs into its new slot and onValueChange receives the new order. Rows can have different heights. Use it for ordering favorites, playlists or priorities.

Installation

$ pnpm dlx @esuiss/uim@latest add reorder-list

The CLI also adds what it builds on: Icon, Item.

Manual

  1. Install the dependencies:
npx expo install cn phosphor-react-native react-native-gesture-handler react-native-reanimated react-native-svg react-native-worklets
  1. Add the pieces it builds on: Icon, Item, and the primitives components/primitives/haptics.tsx.

  2. Copy the source into components/interactions/reorder-list.tsx:

uniwind/components/interactions/reorder-list.tsx
import { cn } from "cn"
import { DotsSixVerticalIcon } from "phosphor-react-native"
import * as React from "react"
import { View, type ViewProps } from "react-native"
import { Gesture, GestureDetector } from "react-native-gesture-handler"
import {
  ReduceMotion,
  runOnJS,
  type SharedValue,
  useAnimatedStyle,
  useReducedMotion,
  useSharedValue,
  withSpring,
} from "react-native-reanimated"
import { useHaptics } from "@/components/primitives/haptics"
import { AnimatedView } from "@/components/ui/animated-view"
  1. Update the import paths to match your project.

Usage

import { ReorderList, ReorderListItem, ReorderListHandle } from "@/components/interactions/reorder-list"
<ReorderDemo />

ReorderList is controlled: pass value as an array of item keys and update it in onValueChange. Give each ReorderListItem a value matching its key, and place a ReorderListHandle anywhere inside it, at the start or end. Place the list inside an ItemGroup with the inset variant for a rounded group; the lifted row then keeps the group's shape.

Only the handle starts a drag, so the rest of the row can still be pressed or scrolled.

Examples

Inset

Rows inside an inset ItemGroup.

iOS
Reorder List (inset) on iOS in light mode

Leading handle

Places the handle at the start of each row.

iOS
Reorder List (leading handle) on iOS in light mode

API Reference

ReorderList

The root that renders items in value order.

ExtendsViewProps
PropType
About
Item keys in their current order.
Type
string[]
Default
required
About
Called with the new order after a move.
Type
(value: string[]) => void
Default
required
About
Label of the move up accessibility action. Defaults to Move up.
Type
string
Default
"Move up"
About
Label of the move down accessibility action. Defaults to Move down.
Type
string
Default
"Move down"

ReorderListItem

One row, keyed by its value.

ExtendsViewProps
PropType
About
The key of this item.
Type
string
Default
required

ReorderListHandle

The grip that starts a drag.

ExtendsViewProps

Accessibility

  • The handle has the button role and offers moveUp and moveDown accessibility actions, so screen reader users can reorder without dragging.
  • Move up is omitted on the first row and move down on the last.

Notes

  • Haptics play when the drag starts (medium impact), each time the target slot changes (selection) and on release (light impact), if the app provides an adapter through HapticsProvider.
  • The dragged row shows a bg-surface-raised layer with a shadow, rounded inside an inset group.
  • The lift scale is skipped inside an inset group and when reduce motion is on; springs respect the system reduce motion setting.