A week strip that morphs into a full month grid, with a spring-driven pill and a draggable grabber handle.
npx shadcn@latest add @iconiq/week-calendar"use client";
import { useState } from "react";
import { WeekCalendar } from "@/components/ui/week-calendar";
export function WeekCalendarPreview() {
const [selected, setSelected] = useState<Date | null>(new Date());
return <WeekCalendar onSelect={setSelected} selected={selected} />;
}selectedControlled selected day. Pass null to clear the highlight.
Type Date | null
defaultSelectedInitial selected day for uncontrolled usage when selected is not provided.
Type Date | null·Default today
onSelectCalled when the user taps a day, including days outside the active month while expanded.
Type (date: Date) => void
expandedControlled morph state. false renders the week strip, true renders the full month grid.
Type boolean
defaultExpandedInitial expanded state for uncontrolled usage when expanded is not provided.
Type boolean·Default false
onExpandedChangeCalled whenever the week/month morph toggles, from a grabber-handle drag or Enter/Space when it's focused.
Type (expanded: boolean) => void
weekStartsOnOverrides the first day of the week for both the weekday header and the rendered grid.
Type 0 | 1 | 2 | 3 | 4 | 5 | 6·Default 0
classNameOptional class names applied to the root surface.
Type string
Swipe the week strip to change weeks, drag the grabber handle to morph between week and month, and use the chevrons for single-step navigation.
Collapsed: drag horizontally or use the chevrons to move by one week; the row cross-dissolves with a soft blur.
Expanded: the chevrons move by one month; the previously visible week stays anchored in place while the remaining weeks of the month spring in above and below it.
The grabber handle beneath the grid toggles on a vertical drag past a small threshold, or Enter/Space when it's focused — a plain tap does nothing.
The selected-day pill and the today ring share a layoutId per instance, so Motion animates them between cells instead of cutting between positions.
Spring transitions respect prefers-reduced-motion, and selection state is announced for screen readers.
Day buttons expose aria-pressed for the selected day and a full spoken date via aria-label.
The expand toggle exposes aria-expanded, and a polite live region announces the currently selected date.
All layoutId and enter/exit springs are skipped when useReducedMotion() reports a reduced-motion preference, falling back to instant state changes.
Theme colors come from CSS variables, so the surface follows light/dark mode without client-side palette hydration.
Install the exact registry entry shown on the right when you want the component file and its declared runtime dependencies together.
Dependencies: motion, lucide-react, date-fns.
Contact
Additionally, if you find any bug or issue, feel free to raise an issue.