Week Calendar

A week strip that morphs into a full month grid, with a spring-driven pill and a draggable grabber handle.

Installation

npx shadcn@latest add @iconiq/week-calendar

File Structure

Usage

"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} />;
}

Props

Props
Description

WeekCalendar

selected

Controlled selected day. Pass null to clear the highlight.

Type Date | null

defaultSelected

Initial selected day for uncontrolled usage when selected is not provided.

Type Date | null·Default today

onSelect

Called when the user taps a day, including days outside the active month while expanded.

Type (date: Date) => void

expanded

Controlled morph state. false renders the week strip, true renders the full month grid.

Type boolean

defaultExpanded

Initial expanded state for uncontrolled usage when expanded is not provided.

Type boolean·Default false

onExpandedChange

Called whenever the week/month morph toggles, from a grabber-handle drag or Enter/Space when it's focused.

Type (expanded: boolean) => void

weekStartsOn

Overrides 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

className

Optional class names applied to the root surface.

Type string

Gestures and morph behavior

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.

Motion and accessibility

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.

Registry bundle

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.

Selected Sunday, July 19, 2026

Jul 19 – Jul 25

SMTWTFS