Slot Picker

Let the user pick a date and time from real availability, shown in a stated timezone.

Pattern

≈ 2,062 tokens per use (tool 575 · guidance 268 · typical payload 1,219)How token costs are estimated

Example: Slot Picker

Set up a follow-up call with Dana from Bluepeak. Any time in the coming months works.

Follow-up call with Dana Reyes

30 minNew York time (GMT-4)

Day

October 2026

Times on Monday, October 12

When to use

  • The user needs to pick a time for something with real availability: a call, a table, a service appointment.
  • The agent fetched open slots from a calendar or booking system.

When not to use

  • The agent hasn't checked availability. Fetch it first; never offer invented times.
  • There's exactly one possible time. Ask for a yes or no in text.
  • The user already named a time that's free. Book it, or confirm it with a Change Preview.

How the agent should use it

  • Every slot comes from a tool result, with its real id.
  • Always set timezone to the zone the times are in, and use the user's zone when possible.
  • Include taken slots as available: false when it helps the user see the shape of the day; drop days with nothing at all.
  • Book only the slotId returned with select, and re-check it's still free before booking.

Interaction

Days are toggle buttons (aria-pressed) that scroll sideways on narrow screens; a day with no open slots is disabled, its date struck through and announced as "full" (each day also announces how many times are open). The first day with an open slot is shown first. Times for the chosen day are toggle buttons in a labelled group; taken slots are disabled, struck through and announced as "taken". Confirm stays disabled until a time is picked; the footer then shows the full day and time. After confirming, the chosen time is the card's headline.

start can be wall-clock HH:mm in timezone, or a full ISO date-time with an offset, which is converted into timezone. Dates and times are formatted with Intl in the viewer's locale.

Installation

npx shadcn@latest add https://your-site.com/r/slot-picker.json

Usage

app/api/chat/route.ts
import { tool } from "ai"
import { slotPickerTool } from "@/components/genui/slot-picker.schema"

const tools = {
  // No execute(): the call is completed by the user's choice in the UI.
  [slotPickerTool.name]: tool({
    description: slotPickerTool.description,
    inputSchema: slotPickerTool.inputSchema,
  }),
}
components/message.tsx
import { SlotPicker } from "@/components/genui/slot-picker"

function ToolPart({ part, addToolResult }) {
  if (part.type !== "tool-pick_slot") return null

  return (
    <SlotPicker
      {...part.input}
      status={part.state === "input-streaming" ? "streaming" : undefined}
      result={part.state === "output-available" ? part.output : undefined}
      onResult={(result) =>
        addToolResult({
          tool: "pick_slot",
          toolCallId: part.toolCallId,
          output: result,
        })
      }
    />
  )
}

The agent receives one of:

{ action: "select", slotId: "2026-10-14T12:00", date: "2026-10-14", start: "12:00", label: "Wednesday, October 14 at 12:00 PM" }
{ action: "cancel" }

start is returned exactly as it was given.

With LangGraph

Push it from a graph node with ui.push({ name: "pick_slot", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.

Restoring from history

Pass the stored result back as result. The picker shows the chosen day and time (or the cancelled state) and never calls onResult again.

<SlotPicker
  {...part.input}
  result={part.state === "output-available" ? part.output : undefined}
/>

Schema

PropTypeNotes
kind"call" | "table" | "service" | "meeting" | "other"?What's being booked. Shown as a coloured icon beside the title
titlestringWhat's being booked: "Book a follow-up call"
descriptionstring?Short; joins durationLabel and the timezone on one line under the title
timezonestringIANA zone, e.g. America/New_York. Shown as "New York time (GMT-4)"
days{ date, slots }[]Data-bound. 1–366 days with times, date as YYYY-MM-DD. The picker shows a 12-month calendar strip from the first day; days without times are faded, with a "No open times" tooltip. The arrows scroll it, and the month label follows the days in view
durationLabelstring?"30 min", "2 hours"
submitLabelstring?Verb for the confirm button. Default "Confirm"

Slot

FieldTypeNotes
idstringData-bound. Returned in the result
startstringData-bound. HH:mm, or ISO date-time with offset
endstring?Same format as start
labelstring?Overrides the formatted time
availablebooleanData-bound. Default true; false shows the slot as taken

Host props: status (streaming, ready, submitting, selected, cancelled), onResult, result and className.

Source

On this page