Rates and dates
Every rate below is for the Sea Suite with breakfast, and it is the same one you would find anywhere else, only lower. Choose an arrival, then a departure.
Lowest rate this month
A nightly rate calendar for direct bookings built on the shadcn calendar: two months side by side, every night shows its own price, the lowest rate of each month carries a dot and full nights are struck through. Picking an arrival then a departure skips stays that cross a full night, sums the total, enforces a minimum and hands the dates to your booking engine.
An open rate calendar where every night shows its own price under the date, the cheapest nights of each month carry a small dot and full nights are struck through, with a footer that reads back the stay, sums the total and hands the dates to your booking engine.
Upgrade to Pro
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://beste.dev/r/hotel5?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://beste.dev/r-base/hotel5?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/hotel5.tsx plus the button22 sliding-marker pill used for the submit button, the text1 word stagger behind the heading, and the shadcn/ui calendar primitive whose day buttons are replaced to print each night's rate.
The installed file exports hotel5Demo alongside the block: the exact props behind the preview above. Spread it to get a working section in one line.
import { Hotel5, hotel5Demo } from "@/components/beste/block/hotel5";
export default function Page() {
return <Hotel5 {...hotel5Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Hotel5 } from "@/components/beste/block/hotel5";
export default function Page() {
return (
<Hotel5
eyebrow="Rates and dates"
heading="Pick your nights. The price is the price."
description="Every rate below is for the Sea Suite with breakfast. Choose an arrival, then a departure."
months={[
{
month: "2027-06",
rates: [440, 420, 400, 480, 480, null, null, 440, 460, 420, 520, 480, 420, 420, 400, 400, 420, 480, null, null, 400, 420, 460, 420, 480, 520, 420, 460, 460, 400],
},
]}
currency="€"
minNights={2}
booking={{
action: "https://booking.example.com/ostria",
params: { checkIn: "checkin", checkOut: "checkout" },
submitLabel: "Continue to booking",
}}
labels={{
arrive: "Arrive",
leave: "Leave",
pick: "Choose a date",
pickLeave: "Now choose when you leave",
nights: { one: "night", other: "nights" },
total: "total with breakfast",
lowest: "Lowest rate this month",
soldOut: "Full",
minStay: "Two nights at least",
}}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | – | Uppercase line above the heading |
heading | string | – | Serif <h2>, staggered word by word through Text1 |
description | string | – | Paragraph to the right of the heading |
months | RateMonth[] | [] | One entry per bookable month with a rate for each night |
currency | string | "" | Prefix for the summed total |
minNights | number | 1 | Shortest stay that can be submitted |
booking | Booking | – | Footer form with the stay readout and submit pill; hidden when omitted |
labels | Labels | {} | Text for the footer, the legend and full nights |
className | string | – | Extra classes for the outer <section> |
type RateMonth = {
month: string; // "YYYY-MM"
rates: (number | null)[]; // index 0 is the 1st; null is a full night
};
type Booking = {
action: string;
params?: { checkIn?: string; checkOut?: string };
submitLabel: string;
};
type Labels = {
arrive?: string;
leave?: string;
pick?: string;
pickLeave?: string;
nights?: { one: string; other: string };
total?: string;
lowest?: string;
soldOut?: string;
minStay?: string;
};months. Days without an entry are disabled, a null rate marks a full night (struck through, showing labels.soldOut), and every night whose rate equals the lowest open rate of its month gets a dot, explained by labels.lowest above the grid.labels.minStay while the stay is shorter than minNights.booking.action. Hidden inputs named by params.checkIn and params.checkOut (default checkin and checkout) carry local YYYY-MM-DD dates, and the submit button stays disabled until a departure is picked and the minimum is met.Wiring the form up
The form posts to the action you pass. To validate on the client and handle submission in React instead, see React Hook Form, TanStack Form, and Formisch.