Order tracking screen

ProReact Native · Expo

Live order status: the arrival estimate with minutes that roll as they count down, a timeline whose line fills into each step as the order moves on (finished steps draw a check, the current one breathes), the courier with call and message buttons, and the items. Delivery lands a success check and asks for a rating.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add @goodui-native-pro/order-tracking-screen

Then run npx expo install --fix so react-native-reanimated, react-native-svg match your Expo SDK. Files land in components/goodui-native/order-tracking-screen.

Pro screens install from the license-gated @goodui-native-pro registry. Setup: Pro access.

  • Wrap the app in GestureHandlerRootView and ThemeProvider.

Usage

// app/orders/[id].tsx
import { Linking } from "react-native";
import { Stack, router, useLocalSearchParams } from "expo-router";
import { OrderTrackingScreen } from "@/components/goodui-native/order-tracking-screen/order-tracking-screen";
import { api, useOrder } from "@/lib/orders";

export default function OrderRoute() {
  const { id } = useLocalSearchParams<{ id: string }>();
  const order = useOrder(id); // live: re-renders as your server pushes status updates
  return (
    <>
      <Stack.Screen options={{ headerShown: false }} />
      <OrderTrackingScreen
        orderNumber={order.number}
        steps={order.steps} // [{ key, title, description, time }]
        currentStep={order.stepIndex} // steps.length when delivered
        eta={new Date(order.eta)}
        courier={order.courier} // { name, avatar, vehicle, rating }
        items={order.items}
        total={order.total}
        currency="EUR"
        onCall={() => Linking.openURL(`tel:${order.courier.phone}`)}
        onMessage={() => router.push(`/orders/${id}/chat`)}
        unreadMessages={order.unread}
        onRateCourier={(stars) => api.rateCourier(id, stars)}
        onHelp={() => router.push(`/help?order=${id}`)}
        onBack={() => router.back()}
      />
    </>
  );
}

When to use

Use it for
  • After checkout in a shop, grocery or food delivery app: where the order is and when it arrives.
  • Any process with a few ordered steps people wait on: a repair, a pickup, a ride on its way.
Reach for something else
  • A live map with the courier's position: put your map component above this screen's content; this screen has no map.
  • Past orders: list them with list-row and open a static receipt.

Examples

On its own, on its way

<OrderTrackingScreen />

Delivered, asking for a rating

<OrderTrackingScreen currentStep={4} onRateCourier={(stars) => api.rate(stars)} />

API reference

PropTypeDefault
orderNumber
Order reference shown in the header.
string"PN-48213"
steps
The steps, first to last. The last one is delivery.
OrderStep[]defaultSteps
currentStep
Index of the step in progress (controlled). `steps.length` or more means delivered.
number2
eta
Expected arrival. The minutes left count down and roll.
Date—
courierOrderCourierdefaultCourier
itemsOrderTrackingItem[]defaultItems
total
Order total in `currency`.
number408.8
currency
ISO 4217 code. Default "EUR".
string"EUR"
onCall
Call the courier. Hidden when omitted.
() => unknown—
onMessage
Message the courier. Hidden when omitted.
() => unknown—
unreadMessages
Unread messages from the courier, shown on the message button.
number—
onHelp
"Get help with this order". Hidden when omitted.
() => unknown—
onRateCourier
Rate the courier once delivered. Called with 1 to 5 when a tap or drag ends.
(rating: number) => unknown—
onBack
Back button. Hidden when omitted.
() => void—
locale
BCP 47 locale for times and amounts. Defaults to the device locale.
string—
labels
Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide.
Partial<OrderTrackingScreenLabels>—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • When currentStep moves on, the line under the finished step fills down to the next dot on the smooth spring, the finished dot fills and draws its check, and the new current dot springs in (bouncy) and starts breathing.
  • The minutes left roll down as the estimate counts down (checked every 15 s).
  • Delivered: the success check springs in, draws, and its ring pulses; the rating stars pop as you choose.
  • Reduced motion: lines and dots change at once, no breathing, the check appears complete without drawing, digits change in place.
  • Haptic: tap on call and message (IconButton)
  • Haptic: success when the delivery check lands (SuccessCheck)
  • Haptic: select for each star while rating (Rating)
  • Haptic: tap on Get help (Button)
  • Each step owns two shared values (line fill, dot) and the current one a repeating halo, all on the UI thread.
  • One interval re-renders the screen every 15 s to update the minutes; it stops when the screen unmounts.

Accessibility

  • The header reads 'Order PN-48213' with the item count and total under it.
  • The timeline is a list named 'Order progress'. On native each step is one element read as 'Packed, done, 14:02, At the Chiado store, step 2 of 4'; on the web the visible text is read with the state as visually hidden text.
  • A step change is announced with the new step's title and description; delivery is announced.
  • The minutes left are read as plain text ('12 minutes'); 'Expected by 14:35' is visible text.
  • Call and message are labelled icon buttons ('Call Leo Martins', 'Message Leo Martins, 1 unread'). The delivery rating is adjustable with its labels (Poor to Great).

Notes for AI

  • Drive currentStep from your server: the index of the step in progress; steps.length (or more) means delivered. Steps before it show their time.
  • Pass eta as a Date; the screen counts the minutes itself and never shows less than 1. Without eta it uses 14 minutes from when it opened, which is only for previews.
  • Buttons appear only for the callbacks you pass: onCall, onMessage, onHelp, onBack; onRateCourier shows the rating once delivered.
  • Render it full-bleed with the navigator header hidden (headerShown: false); it handles the safe areas itself.
Checkout screen

The last step before paying: delivery address and payment method pickers, delivery options, the order summary with totals that roll when the delivery changes, and slide to pay in thumb reach. Paying covers the screen with a success check and confetti, then hands over through onDone.

Success check

A success or error moment: the circle springs in, the check draws itself while a ring pulses out and the title rises in; the error variant draws a cross and shakes once. Inline or fullscreen, optionally with confetti.

Avatar

A photo that fades in over initials on a color picked from the name, with a presence dot and a story ring whose accent arc sweeps in when there is something new and away once seen; AvatarGroup stacks people with a +N counter and fans out on tap.

Rating

Stars (or any symbol) you tap or drag across: the fill sweeps from the old rating to the new one and every symbol it crosses pops on its own beat; half steps optional, and a read-only variant shows exact fractions with a value and a count.

Rolling number

A number whose digits roll in the direction of change, with digits that open and close in place and currency signs that glide instead of jumping.

List row

Settings and navigation cells with a token-colored icon tile, title, subtitle and a trailing value, switch, badge, check or chevron, grouped in inset sections with headers and footers. On touch down the highlight floods in, the tile sinks and the chevron leans forward; on release the highlight lingers and fades.