Hotel Reservation
The stay card for a booked hotel, for use during the trip, with check-in times, the room, digital key, Wi-Fi and what's due.
Experience · Shares patterns with Hotel Booking, Flight Ticket
≈ 1,628 tokens per use (tool 1,156 · guidance 332 · typical payload 140)How token costs are estimated
Example: Hotel Reservation
My hotel for tonight
Casa Azulejo Lisboa
Tonight · Checked in online
- Check-in
- 15:00 hotel time
- Thu, Oct 8
- Length of stay
- 3 nights
- Check-out
- 11:00 hotel time
- Sun, Oct 11
Room type: Deluxe King
- Room
- 512
- Floor
- 5
- Wi-Fi
- CasaAzulejo-Guest
- Password
- Hidden
When to use
- The user has a hotel booked and asks about it before or during the trip: "my hotel for tonight", "what time is check-in?", "how do I get into my room?", "what's the Wi-Fi?".
- They want to check in online, open their digital key, call the hotel or get directions.
When not to use
- The user is still choosing or booking a hotel. Use Hotel Booking, which also has a confirmation mode right after booking (price paid, cancel).
- The agent doesn't have the reservation from a tool result. Never invent confirmation numbers, rooms, Wi-Fi details or balances.
How the agent should use it
- Pass stay dates as calendar dates and check-in/out times as the hotel prints them, with the hotel's IANA
timeZone. Never convert them to the user's timezone. - Pass
room.numberonly when the hotel has assigned one. Without it the card says "Room assigned at check-in". - Pass the Wi-Fi password only from the hotel's own data. It's masked until the user taps Show, and it never comes back in a result. Don't repeat it in the reply unless the user asks.
- Every action is a handoff. On
check-inoropen-digital-key, open the hotel's flow; onrequest-late-checkout, ask the hotel and report its answer. Never say late check-out is granted until the hotel confirms. - Lifecycle: set
expiresAton the call to the check-out moment (the check-out date atcheckOutUntil, hotel time;checkOutInstant(stay, hotel)in the utils computes it), so the Renderer turns the card inert once the stay is over. Use supersede when the hotel updates the booking (a room is assigned, check-in is done) and the agent shows the card again.fetchedAtisn't needed.
Stay state and times
The header reads the stay against today's date in the hotel's timezone, so "Tonight" is true where the hotel is:
| Today, hotel time | State line |
|---|---|
| Before check-in day | "In 3 days", "Tomorrow" |
| Check-in day | "Tonight" |
| During the stay | "Night 2 of 3" |
Check-out day, before checkOutUntil | "Checking out today" |
| After that | "Stay ended" (check-in, key and late check-out are hidden) |
Before the stay starts, "Checked in online" follows the state once online check-in is done.
The state is computed after mount, so server and client render the same markup, and it refreshes every minute. Pass now to measure from a fixed moment instead (tests, demos). Stay dates never shift with the viewer's timezone or across DST; times show on a 24-hour clock, as hotels print them.
Layout
The card reads like a pass: the time that matters and the room number, large.
- Header: the hotel, the confirmation number with a copy button, and the state line.
- Times: check-in and check-out times, large, with their dates beneath and the number of nights between them.
- Room: the room type, then the room number and floor in large type once assigned ("Room assigned at check-in" before), and what's due at the hotel.
- Wi-Fi: one row with the network and a masked password with Show (a toggle,
aria-pressed) and Copy. Copying works while masked. - One quiet line for what changes a decision: free cancellation (only before the stay starts), or "No digital key · collect at the front desk".
The address powers Directions and the calendar file; the hotel's website goes into the calendar file only.
Actions
| Button | What happens |
|---|---|
| Check in online | { action: "check-in" }. With checkIn.url, also opens the hotel's page in a new tab |
| Open digital key | { action: "open-digital-key" }. With digitalKey.url, also opens the hotel's app link |
| Request late check-out | { action: "request-late-checkout" }. Primary on check-out day |
| Directions | An OpenStreetMap search for the address, in a new tab. Nothing is sent |
| Call hotel | A tel: link. Nothing is sent |
| Add to calendar | Downloads an .ics file for the stay (check-in to check-out, hotel time). Nothing is sent |
The footer shows the first two that apply (late check-out first on check-out day, then check-in, key, directions, call, calendar) and puts the rest in a "More actions" menu. Each handoff is reported once and the footer confirms it ("Opening your digital key…"); the others stay available. Without a link a handoff button locks after use; with a link it stays usable, since a key is opened more than once.
Installation
npx shadcn@latest add https://your-site.com/r/hotel-reservation.json
Usage
import { tool } from "ai"
import { hotelReservationTool } from "@/components/genui/hotel-reservation.schema"
const tools = {
// No execute: the user's first action is the tool result.
[hotelReservationTool.name]: tool({
description: hotelReservationTool.description,
inputSchema: hotelReservationTool.inputSchema,
}),
}Render the tool call and hand each action to your own flow:
import { HotelReservation } from "@/components/genui/hotel-reservation"
function ToolPart({ part, addToolResult }) {
if (part.type !== "tool-show_hotel_reservation") return null
return (
<HotelReservation
{...part.input}
status={part.state === "input-streaming" ? "streaming" : undefined}
result={part.state === "output-available" ? part.output : undefined}
onResult={(result) => {
if (part.state !== "output-available") {
addToolResult({ tool: "show_hotel_reservation", toolCallId: part.toolCallId, output: result })
} else {
sendMessage({ text: `Hotel reservation action: ${JSON.stringify(result)}` })
}
}}
/>
)
}The agent receives one of:
{ action: "check-in" }
{ action: "open-digital-key" }
{ action: "request-late-checkout" }With LangGraph
Push it from a graph node with ui.push({ name: "show_hotel_reservation", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.
Restoring from history
When a conversation is reloaded, pass the stored result back as result, or an array of results when several actions were taken. Those actions show as done; they never call onResult again.
<HotelReservation
{...part.input}
result={part.state === "output-available" ? part.output : undefined}
/>Schema
| Prop | Type | Notes |
|---|---|---|
hotel | { name, address, phone?, timeZone, checkInFrom, checkOutUntil, url? } | Times as 24-hour "15:00"; IANA timezone; url http(s) only. Data-bound |
confirmation | string | 3–24 letters, digits or dashes. Copy button included. Data-bound |
stay | { checkIn, checkOut } | Calendar dates, check-out after check-in. Data-bound |
room | { type, number?, floor? }? | number only once assigned. Data-bound |
digitalKey | { available, url? }? | url opens the hotel's app; http(s) only. Data-bound |
checkIn | { online, done?, url? }? | Online check-in. Data-bound |
wifi | { network, password? }? | Password masked by default; never in a result. Data-bound |
balance | { due, currency }? | Due at the hotel, e.g. city tax. Data-bound |
cancellation | { freeUntil? }? | ISO 8601 with offset. Shown before the stay starts. Data-bound |
Host props: status (streaming, ready, submitting, done), now, onResult, result and className.