# V date picker 7

> Appointment Booking

A shadcn/ui block from the [@cnippet](https://ui.cnippet.dev/) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/cnippet/v-date-picker-7).

- Page, with a live preview: https://shadcn.exchange/r/cnippet/v-date-picker-7
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/cnippet/v-date-picker-7-light-fdb818660029115b.png
- Registry item: https://ui.cnippet.dev/r/v-date-picker-7.json
- Categories: date picker
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://ui.cnippet.dev/r/v-date-picker-7.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `@cnippet/date-picker`

## Files

### `registry/default/variants/v-date-picker-7.tsx`

```tsx
"use client";

import { format } from "date-fns";
import { CalendarIcon } from "lucide-react";
import { useState } from "react";
import { cn } from "@/lib/utils";
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/registry/default/ui/avatar";
import { Badge } from "@/registry/default/ui/badge";
import { Button } from "@/registry/default/ui/button";
import { Calendar } from "@/registry/default/ui/calendar";
import {
  Popover,
  PopoverPopup,
  PopoverTrigger,
} from "@/registry/default/ui/popover";

const TIME_SLOTS = [
  "9:00 AM",
  "9:30 AM",
  "10:00 AM",
  "10:30 AM",
  "11:00 AM",
  "2:00 PM",
  "2:30 PM",
  "3:00 PM",
  "4:00 PM",
];

const BOOKED_BY_DOW: Record<number, string[]> = {
  1: ["9:00 AM", "10:30 AM", "3:00 PM"],
  2: ["9:30 AM", "2:00 PM"],
  3: ["11:00 AM", "4:00 PM"],
  4: ["9:00 AM", "2:30 PM", "3:00 PM"],
  5: ["10:00 AM"],
};

export default function Particle() {
  const [date, setDate] = useState<Date | undefined>();
  const [selectedSlot, setSelectedSlot] = useState<string | undefined>();

  const handleDateSelect = (d: Date | undefined) => {
    setDate(d);
    setSelectedSlot(undefined);
  };

  const booked = date ? (BOOKED_BY_DOW[date.getDay()] ?? []) : [];

  return (
    <div className="w-full max-w-sm space-y-4 rounded-xl border bg-background p-4 shadow-sm">
      <div className="flex items-center gap-3 border-b pb-4">
        <Avatar className="size-11">
          <AvatarImage src="https://github.com/shadcn.png" />
          <AvatarFallback>SC</AvatarFallback>
        </Avatar>
        <div className="min-w-0 flex-1">
          <p className="font-semibold text-sm">Dr. Sarah Chen</p>
          <p className="text-muted-foreground text-xs">
            Cardiologist · NYC Health
          </p>
        </div>
        <Badge variant="success">Available</Badge>
      </div>

      <Popover>
        <PopoverTrigger
          render={<Button className="w-full justify-start" variant="outline" />}
        >
          <CalendarIcon aria-hidden="true" />
          {date ? format(date, "EEE, MMM dd, yyyy") : "Select a date"}
        </PopoverTrigger>
        <PopoverPopup>
          <Calendar
            disabled={[{ dayOfWeek: [0, 6] }, { before: new Date() }]}
            mode="single"
            onSelect={handleDateSelect}
            selected={date}
          />
        </PopoverPopup>
      </Popover>

      {date && (
        <div>
          <p className="mb-2 font-medium text-muted-foreground text-xs">
            Available slots for {format(date, "MMM dd")}
          </p>
          <div className="flex flex-wrap gap-2">
            {TIME_SLOTS.map((slot) => {
              const isBooked = booked.includes(slot);
              return (
                <button
                  className={cn(
                    "rounded-md border px-3 py-1 font-medium text-xs transition-colors",
                    isBooked
                      ? "cursor-not-allowed border-transparent bg-muted text-muted-foreground/40 line-through"
                      : selectedSlot === slot
                        ? "border-primary bg-primary text-primary-foreground"
                        : "border-input bg-background hover:bg-accent",
                  )}
                  disabled={isBooked}
                  key={slot}
                  onClick={() => setSelectedSlot(slot)}
                  type="button"
                >
                  {slot}
                </button>
              );
            })}
          </div>
        </div>
      )}

      <Button className="w-full" disabled={!date || !selectedSlot}>
        {selectedSlot && date
          ? `Book · ${format(date, "MMM dd")} at ${selectedSlot}`
          : "Book Appointment"}
      </Button>
    </div>
  );
}
```
