Appointments screen

ProReact Native · Expo

A client's bookings in Upcoming and Past tabs: date-tile cards with status, time, professional and place. Swipe a card for Reschedule or Cancel; cancelling asks in an alert that spins while the request runs, then the card slides out of Upcoming and the tab count drops.

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/appointments-screen

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

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

Usage

import { router, Stack } from "expo-router";
import { AppointmentsScreen } from "@/components/goodui-native/appointments-screen/appointments-screen";
import { api } from "@/lib/api";
import { useAppointments } from "@/lib/booking"; // your data, Appointment[]

export default function AppointmentsRoute() {
  const { data, refresh } = useAppointments();
  return (
    <>
      <Stack.Screen options={{ headerShown: false }} />
      <AppointmentsScreen
        appointments={data}
        onOpen={(a) => router.push({ pathname: "/appointments/[id]", params: { id: a.id } })}
        onReschedule={(a) => router.push({ pathname: "/book/time", params: { reschedule: a.id } })}
        onCancel={(a) => api.cancel(a.id).then(refresh)}
        onBookAgain={(a) => router.push({ pathname: "/book", params: { again: a.id } })}
        onBook={() => router.push("/book")}
      />
    </>
  );
}

When to use

Use it for
  • The bookings tab of a client app for a salon, clinic, studio, gym or tutor.
  • Lists where each item has quick actions (reschedule, cancel, book again) and a details screen.
Reach for something else
  • A staff calendar with many bookings per day: use a timeline or calendar view.
  • Notifications or messages: use inbox-screen.
  • One appointment's details: use booking-details-screen.

Examples

Sample appointments, no props

<AppointmentsScreen />

Empty

<AppointmentsScreen appointments={[]} onBook={() => router.push("/book")} />

API reference

PropTypeDefault
appointments
Every appointment, any order. Confirmed and pending ones that have not started are upcoming; the rest are past.
Appointment[]—
onOpen
Tapping a card, for example to push the details screen.
(appointment: Appointment) => void—
onReschedule
The Reschedule swipe action on an upcoming card. Hidden when omitted.
(appointment: Appointment) => void—
onCancel
Cancels after the person confirms in the alert. Return a promise: the alert keeps a spinner until it settles, closes on success (the card leaves Upcoming and shows as cancelled under Past) and shakes on failure.
(appointment: Appointment) => unknown—
onBookAgain
The Book again swipe action on a past card. Hidden when omitted.
(appointment: Appointment) => void—
onBook
New booking: the plus button in the header and the empty state's button. Hidden when omitted.
() => void—
onAppointmentsChange
Called with the whole list after a cancellation changes it, to keep your store in sync.
(appointments: Appointment[]) => void—
defaultTab
Tab shown first. Default "upcoming".
"upcoming" | "past""upcoming"
title
Headline. Default "Appointments". Same as `labels.title`.
string—
locale
BCP 47 locale for dates and times. Defaults to the device locale.
string—
hour12
Force a 12 or 24 hour clock. Defaults to the locale's.
boolean—
now
The current time, for splitting upcoming from past. Default now.
Date—
labels
Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide.
Partial<AppointmentsScreenLabels>—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • Cards follow the finger and unfold Cancel and Reschedule in proportion to the drag (SwipeActions).
  • After a confirmed cancellation the card fades out and the cards below slide up into its place; the Upcoming count badge updates.
  • Empty states draw their line art in.
  • Reduced motion: cards below jump into place instead of sliding; the components inside follow their own reduced-motion rules.
  • Haptic: tap when a swipe action runs (SwipeActions)
  • Haptic: select when the tab changes (TopTabs)
  • Haptic: success or error when the cancellation settles (AlertDialog)
  • Upcoming and Past are derived with useMemo from one list; a page mounts only when its tab is first shown (TopTabs lazy).
  • For hundreds of appointments, page the data from your API; each page is a ScrollView.

Accessibility

  • Tabs are the kit's TopTabs (tablist with selected state and a count on Upcoming).
  • Each card is one button: on native it reads as a sentence (services, day, time, professional, status) and offers Reschedule and Cancel as accessibility actions; the web names it from its text and opens it with Enter or Space.
  • Cancelling always asks first in an AlertDialog that names the visit and the free cancellation window or the late fee.
  • A line under each list says that cards can be swiped, so the actions are discoverable.

Notes for AI

  • appointments is Appointment[] from booking-types. Confirmed and pending ones that have not ended are Upcoming; everything else is Past.
  • onCancel runs only after the alert is confirmed. Return a promise; on success the screen marks the appointment cancelled locally and calls onAppointmentsChange with the new list.
  • Swiping between tabs is off on purpose: horizontal swipes belong to the cards. Tabs change with a tap.
  • Reschedule and Book again appear only when their callbacks are passed; Cancel is always offered on upcoming cards.
  • Hide the navigator header (headerShown: false); the screen draws its own title.