Time Off Request Panel React component
A time-off request screen: vacation / sick / personal balance cards that preview the request, a month calendar for picking a range that skips weekends and holidays, existing requests and teammates who are off marked on the days, half-day options, overlap and balance warnings, a note, and a requests list with pending items you can cancel.
About the Time Off Request Panel component
Time Off Request Panel lets employees request leave without guessing. Balances sit on top, the calendar shows what's already booked and who on the team is away, and the request counts only real working days, skipping weekends and holidays.
Before sending, people see the balance after the request, get a heads-up when teammates are off at the same time, and are stopped from going over their balance or double-booking. Pending requests can be cancelled from the list.
When to use it
Use it in HR, payroll or team tools wherever employees book vacation, sick or personal days.
For a team-wide view of who's away use the Team Availability block; for general date picking use the Date Range Picker.
Installation
Add the Time Off Request Panel component to your project with the shadcn CLI. It lands in components/time-off-request-panel.tsx as plain React + Tailwind CSS source you own and can edit.
npx shadcn@latest add @reactframe/time-off-request-panelOr copy the source by hand and install its dependencies:
npm install motion clsx tailwind-mergeProps
<TimeOffRequestPanel /> accepts 9 props, all optional.
| Prop | Type | Default |
|---|---|---|
| balances | Record<Kind, { used: number; total: number }> | BALANCES |
| requests | { id: string; kind: Kind; start: string; end: string; days: number; status: "approved" | "pending" | "declined"; note?: string }[] | REQUESTS |
| approver | string | "Lena Hartmann" |
| month First month shown, as YYYY-MM. | string | "2026-11" |
| holidays | Record<string, string> | HOLIDAYS |
| teamOff | Record<string, string[]> | TEAM_OFF |
| onSubmit | (r: Omit<TimeOffRequest, "id" | "status">) => void | Promise<void> | — |
| theme | "dark" | "light" | "dark" |
| className | string | — |
- balancesRecord<Kind, { used: number; total: number }>Default: BALANCES
- requests{ id: string; kind: Kind; start: string; end: string; days: number; status: "approved" | "pending" | "declined"; note?: string }[]Default: REQUESTS
- approverstringDefault: "Lena Hartmann"
- month
First month shown, as YYYY-MM.
stringDefault: "2026-11" - holidaysRecord<string, string>Default: HOLIDAYS
- teamOffRecord<string, string[]>Default: TEAM_OFF
- onSubmit(r: Omit<TimeOffRequest, "id" | "status">) => void | Promise<void>
- theme"dark" | "light"Default: "dark"
- classNamestring
npm dependencies
Installed for you by the shadcn CLI.
Time Off Request Panel FAQ
- How are work days counted?
- Every day in the range that isn't Saturday, Sunday or listed in holidays counts as one, minus half a day for each half-day chip. Change the dow check if your work week differs.
- Will dates shift in other time zones?
- No. Everything is computed from YYYY-MM-DD strings with UTC dates, so the server, the browser and every time zone agree.
- How do I show teammates who are away?
- Pass teamOff as { 'YYYY-MM-DD': ['Name', …] }. Those days get amber dots and the form warns when the request overlaps.
- What happens on submit?
- onSubmit receives { kind, start, end, days, note }. When it resolves, the request appears as Pending at the top of the list and the balances account for it.
- Is it accessible?
- Balance cards are toggle buttons, the calendar is a labelled grid of buttons whose names include holidays, existing requests and who's off, the form summary is announced, and warnings use role=alert.