# Booking time screen

> Step three of a booking flow: a week strip, the day's times grouped by morning, afternoon and evening for the chosen professional, a time zone note, and a summary that springs up from the bottom with the time rolling in on every pick.

- Type: Component (screens)
- Access: Pro
- Page: https://goodui.dev/native/components/booking-time-screen
- Markdown: https://goodui.dev/native/components/booking-time-screen.md
- Registry item: https://goodui.dev/native/r/booking-time-screen.json
- Dependencies: react-native-reanimated
- Keywords: react native appointment time picker, booking calendar screen, time slot picker, week strip availability, salon availability, schedule appointment expo, time zone booking

## When to use

- Picking a start time for a service of known length with one professional or anyone available.
- Availability that changes per day and comes from an API (return a promise and placeholders show while it loads).
- Businesses whose clients may be in another time zone: times are shown in the business's zone, and the note names that zone and how far it is from the phone.

## When not to use

- Picking a date range (hotel nights): use date-picker in range mode.
- Events with fixed sessions and seats: use time-slots with remaining counts on your own screen.
- Recurring schedules: use a form with wheel-picker.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add @goodui-native-pro/booking-time-screen
npx expo install --fix
```

Pro: add `"@goodui-native-pro": { "url": "https://goodui.dev/native/r/pro/{name}.json", "headers": { "Authorization": "Bearer ${GOODUI_LICENSE_KEY}" } }` to the `registries` of components.json and put your license key in .env.local.

The component lands in `components/goodui-native/booking-time-screen/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs avatar, button, locale, service-picker-screen, time-slots, week-strip.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { router, Stack, useIsFocused } from "expo-router";
import { BookingTimeScreen } from "@/components/goodui-native/booking-time-screen/booking-time-screen";
import type { BookingTimeScreenProps } from "@/components/goodui-native/booking-time-screen/booking-time-screen";
import { api } from "@/lib/api";
import { useBookingDraft } from "@/lib/booking"; // your draft store

export default function TimeRoute() {
  const draft = useBookingDraft();
  const focused = useIsFocused();
  const availability: BookingTimeScreenProps["availability"] = (day, staffId) =>
    api.slots({ day: day.toISOString().slice(0, 10), staffId, minutes: draft.duration });
  return (
    <>
      <Stack.Screen options={{ headerShown: false }} />
      <BookingTimeScreen
        business={draft.business}
        services={draft.services}
        duration={draft.duration}
        staff={draft.staff}
        availability={availability}
        focused={focused}
        onBack={() => router.back()}
        onChangeStaff={() => router.back()}
        onConfirm={(start) => {
          draft.setStart(start);
          router.push("/book/confirm");
        }}
      />
    </>
  );
}
```

## Examples

### Sample salon, no props

```tsx
<BookingTimeScreen />
```

### Anyone available, 12 hour clock

```tsx
<BookingTimeScreen staff={null} hour12 locale="en-US" confirmLabel="Book this time" onConfirm={book} />
```

## API reference

### BookingTimeScreen

`import { BookingTimeScreen } from "@/components/goodui-native/booking-time-screen/booking-time-screen";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `business` | `BookingBusiness` | `sampleBusiness` | The business. Times are shown in its `timeZone` (with a note naming that zone and how far it is from the phone), and its closed weekdays dim days in the strip. An empty or unknown zone shows the phone's clock. |
| `services` | `BookingService[]` | `[sampleServices[0]]` | The picked services, for the summary line and the appointment length. |
| `duration` | `number` | – | Appointment length in minutes. Defaults to the sum of `services`; pass it when add-ons add time. |
| `staff` | `BookingStaff \| null` | `sampleStaff[0]` | Who it is with. `null` is "Any available". |
| `availability` | `BookingAvailability` | `sampleAvailability` | Slots for a day and a staff id (`null` = anyone). Return a promise to show placeholders while it loads. Each "HH:mm" is a moment on the device clock of that day (`slotTime(date)` of your API's start); it is shown in the business's zone. `onConfirm` gets `slotStart(day, time)` back. |
| `value` | `Date \| null` | – | Picked start (controlled). `null` for none. Pair it with `onValueChange`. |
| `defaultValue` | `Date \| null` | `null` | Starting pick when uncontrolled. Its day opens first. |
| `onValueChange` | `(start: Date \| null) => void` | – | Called with the picked start, or `null` when the day changes and the pick is cleared. |
| `onConfirm` | `(start: Date) => unknown` | – | The button in the floating summary, with the picked start. Return a promise to show pending, success and error. |
| `confirmLabel` | `string` | – | Label of that button. Default "Continue". Same as `labels.confirm`. |
| `onChangeStaff` | `() => void` | – | Shows a "Change" button beside the professional, for going back to the staff step. |
| `onBack` | `() => void` | – | Shows a back button in the top bar. |
| `minDate` | `Date` | – | First bookable day. Default today. |
| `maxDate` | `Date` | – | Last bookable day. Default 60 days from today. |
| `markedDates` | `(date: Date) => boolean \| number` | – | Dots under days in the week strip, for example days with few times left. |
| `focused` | `boolean` | `true` | Whether this screen is the one on top. The floating summary renders above everything, so pass `false` while another screen covers this one (`useIsFocused()` from expo-router or React Navigation) and it leaves with it. Default `true`. |
| `step` | `BookingStep \| null` | `{ current: 3, total: 4 }` | Step of the booking flow in the top bar. `null` hides it. Default step 3 of 4. |
| `title` | `string` | – | Headline. Default "Pick a time". Same as `labels.title`. |
| `locale` | `string` | – | BCP 47 locale for days and times. Defaults to the device locale. |
| `hour12` | `boolean` | – | Force a 12 or 24 hour clock. Defaults to the locale's. |
| `labels` | `Partial<BookingTimeScreenLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- The week strip and the time grid are the kit's WeekStrip and TimeSlots: days are tabs, times are radios with checked and disabled state; taken times read as unavailable.
- The time grid is named after the day ("Times on Thursday, 6 November").
- The floating summary is a polite live region, so the picked time and its end are read when they change.
- A full day says so in text and offers a button to the next day with free times.

## Motion

- The summary springs up from the bottom with the first pick, and each new time rolls in from below (TimeSlots floating summary).
- The picked slot pops, the week strip pill stretches between days, placeholders shimmer while a day loads.
- Reduced motion: the summary fades instead of sliding, the time crossfades, slots do not pop.

## Haptics

- select when a time or a day is picked
- tap on the summary button, then success or error when onConfirm returns a promise

## Performance

- availability runs once per day and staff change (an inline function is fine, it does not refetch on re-render); stale answers from a slow request are ignored when a newer day is picked.
- The jump to the next free day only searches synchronous availability, so it never fires a request per day.

## Notes for AI

- availability(day, staffId) returns TimeSlot[] ({ time: 'HH:mm', available, remaining }) or a promise of it. staffId is null for Any available.
- Slot "HH:mm" strings are moments on the device clock of the day (build them with slotTime(start) from your API's start times; zonedSlotStart(day, '09:00', zone) turns business hours into moments). The grid, its morning/afternoon/evening groups and the summary show them in business.timeZone through Intl, matching the note; set timeZoneCity for the note's city name.
- An empty or unknown business.timeZone shows the phone's clock and the note says so.
- onConfirm(start) receives slotStart(day, time), the exact moment. Return a promise only if you hold the slot remotely.
- Pass duration when add-ons add minutes; otherwise it is the sum of the services.
- business.closedWeekdays dims closed days in the strip.
- Hide the navigator header (headerShown: false). The floating summary renders in ThemeProvider's overlay layer, above every screen: pass focused={useIsFocused()} so it leaves when the next screen is pushed.

## Related

- [Time slots](https://goodui.dev/native/components/time-slots.md): A booking grid of time slots grouped into morning, afternoon and evening: the picked slot pops into the accent, taken slots are crossed out, low availability shows "2 left", and a summary with the end time and the confirm button rides under the grid or pinned to the bottom of the screen.
- [Week strip](https://goodui.dev/native/components/week-strip.md): A horizontal week of days: one accent pill slides between days and stretches toward the new one, weeks page with the finger 1:1, dots mark busy days, and a header shows the month with a Today button.
- [Staff picker screen](https://goodui.dev/native/components/staff-picker-screen.md): Step two of a booking flow: an Any available card and a card per professional with photo, rating and next free time. One accent ring springs from card to card, its edges on different springs so it stretches toward the pick.
- [Booking confirm screen](https://goodui.dev/native/components/booking-confirm-screen.md): The last step of a booking flow: a summary card, client details, notes and the cancellation policy, booked with a slide (or a button). The slider spins while the request runs, then the screen gives way to a success check with confetti, Add to calendar and Done.
- [Date picker](https://goodui.dev/native/components/date-picker.md): Inline month calendar: months slide with the finger, the selection circle travels from day to day and stretches toward the new one, a range sweeps its band in from the start day, and booked days are crossed out. A compact wheel variant picks day, month and year in the locale's order.

## Guidance for AI tools

Booking time screen: Step three of a booking flow: a week strip, the day's times grouped by morning, afternoon and evening for the chosen professional, a time zone note, and a summary that springs up from the bottom with the time rolling in on every pick. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
