# Vacation Rental Minimum Stay

> Typed check-in and check-out dates where the minimum number of nights depends on the check-in date, such as a weekend, a holiday week or the high season. Use this when a rental host sets a longer minimum stay for some dates than for others.

A shadcn/ui block from the [@tealmac](https://ui.tealmac.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/tealmac/vacation-rental-minimum-stay).

- Page, with a live preview: https://shadcn.exchange/r/tealmac/vacation-rental-minimum-stay
- Screenshot: https://shadcn.exchange/shot/tealmac/vacation-rental-minimum-stay/light.png
- Registry item: https://ui.tealmac.com/r/vacation-rental-minimum-stay.json
- Listed: 2026-11-01, source last changed 2026-11-01

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/tealmac/vacation-rental-minimum-stay.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: `@tealmac/date-range-field`, `@tealmac/date-time-core`

## Files

### `registry/patterns/vacation-rental-minimum-stay/vacation-rental-minimum-stay.tsx`

```tsx
"use client"

import * as React from "react"

import { DateRangeField } from "@/components/ui/date-range-field"
import {
  nightsBetween,
  weekday,
  type DateRange,
} from "@/components/ui/date-time-core"

export interface StayRule {
  from: string
  to: string
  nights: number
  reason: string
}

export interface VacationRentalMinimumStayProps {
  value?: DateRange | null
  defaultValue?: DateRange | null
  onValueChange?: (stay: DateRange | null) => void
  rules?: StayRule[]
  weekendNights?: number
  defaultNights?: number
  locale?: string
}

export function minimumStay(
  checkIn: string,
  rules: StayRule[],
  { weekendNights = 2, defaultNights = 1 } = {}
): { nights: number; reason: string } {
  // The longest matching rule wins.
  const hit = rules
    .filter((r) => checkIn >= r.from && checkIn <= r.to)
    .sort((a, b) => b.nights - a.nights)[0]
  if (hit) return { nights: hit.nights, reason: hit.reason }
  if ([5, 6].includes(weekday(checkIn)) && weekendNights > defaultNights)
    return { nights: weekendNights, reason: "weekend check-in" }
  return { nights: defaultNights, reason: "standard stay" }
}

export function VacationRentalMinimumStay({
  value,
  defaultValue = null,
  onValueChange,
  rules = [],
  weekendNights = 2,
  defaultNights = 1,
  locale,
}: VacationRentalMinimumStayProps) {
  const [inner, setInner] = React.useState<DateRange | null>(defaultValue)
  const stay = value === undefined ? inner : value
  const [checkIn, setCheckIn] = React.useState<string | null>(
    defaultValue?.from ?? null
  )
  const rule = checkIn
    ? minimumStay(checkIn, rules, { weekendNights, defaultNights })
    : null

  return (
    <div className="flex flex-col gap-2">
      <DateRangeField
        label="Stay"
        startLabel="Check-in"
        endLabel="Check-out"
        locale={locale}
        value={stay}
        minDays={rule ? rule.nights + 1 : undefined}
        onValueChange={(next) => {
          if (value === undefined) setInner(next)
          if (next) setCheckIn(next.from)
          onValueChange?.(next)
        }}
      />
      {rule ? (
        <p role="status" className="text-muted-foreground text-sm">
          Minimum stay for this check-in: {rule.nights} night
          {rule.nights === 1 ? "" : "s"} ({rule.reason}).
          {stay ? ` You chose ${nightsBetween(stay.from, stay.to)}.` : ""}
        </p>
      ) : (
        <p role="status" className="text-muted-foreground text-sm">
          Enter a check-in date to see the minimum stay.
        </p>
      )}
    </div>
  )
}
```
