Room by room
The show home in the warehouse, photographed over a single day. Pick a room to see it, or move through them with the arrow keys.
Use the arrow keys to move between rooms

A double-height room with the river wall of arched windows, brick on one side and oak underfoot.
1 of 5The living room in the late morning light
A room picker for a show home: a hairline list of rooms on the left with a thin accent bar sliding to the selected one, and on the right a 4:3 frame in which the matching photograph cross-fades in once it has loaded, with the room's note, a counter and a caption under it; the arrow keys move the selection and the first photograph opens the frame top to bottom.
A room picker for a development's show home: a rule eyebrow, a heading and an intro, then a hairline list of rooms on the left with a thin accent bar that slides to the selected row, and on the right a 4:3 frame in which the matching photograph cross-fades in once its file has loaded. Under the frame, the room's note, a counter and a caption change with it. Clicking a row or pressing the arrow keys moves the selection; the first photograph opens the frame from the top and the frame drifts slowly against the scroll.
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/realestate64?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://beste.dev/r-base/realestate64?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/realestate64.tsx plus the badge24 rule eyebrow; no shadcn/ui primitives are needed.
The installed file exports realestate64Demo alongside the block: the exact props behind the preview above. Spread it to get a working section in one line.
import { Realestate64, realestate64Demo } from "@/components/beste/block/realestate64";
export default function Page() {
return <Realestate64 {...realestate64Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Realestate64 } from "@/components/beste/block/realestate64";
export default function Page() {
return (
<Realestate64
eyebrow="Room by room"
heading="One home, one room at a time."
description="Pick a room to see it, or move through them with the arrow keys."
rooms={[
{
title: "Living",
description: "A double-height room with the river wall of arched windows.",
image: {
src: "https://oud.pics/b/realestate/alder-warehouse-living.webp",
alt: "A double-height living room with a brick wall and a tall oak window",
},
caption: "The living room in the late morning light",
},
{
title: "Kitchen",
description: "Oak cabinets, a stone island and a run of windows onto the birches.",
image: {
src: "https://oud.pics/b/realestate/alder-courtyard-kitchen.webp",
alt: "An open kitchen with oak cabinets and a stone island",
},
caption: "The kitchen island faces the courtyard",
},
{
title: "Bedroom",
description: "The main bedroom with an oak bed and a brick wall kept from the old building.",
image: {
src: "https://oud.pics/b/realestate/alder-warehouse-bedroom.webp",
alt: "A main bedroom with an oak bed, a brick wall and a view of birch trees",
},
caption: "The main bedroom, birches outside",
},
]}
labels={{ of: "of", hint: "Use the arrow keys to move between rooms" }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | – | Short line above the heading, rendered through badge24. |
heading | string | – | Section heading. |
description | string | – | Intro paragraph beside the heading, bottom-aligned on desktop. |
rooms | Room[] | [] | The rooms in list order; the first is selected on load. Every photograph should be 4:3 to fill the frame without cropping. |
labels | Labels | {} | The word between the two counter numbers and the keyboard hint under the list (desktop only). |
className | string | – | Extra classes merged onto the section. |
type Room = {
title: string;
description: string;
image: { src: string; alt: string };
caption: string;
};
type Labels = { of?: string; hint?: string };ArrowDown, ArrowRight, ArrowUp and ArrowLeft on the list move the selection with wrap-around and focus the new row, so a keyboard user steps through the rooms without leaving the list. Each row carries aria-pressed.layoutId span, so it slides from the previous row to the new one over 0.5 s instead of blinking.AnimatePresence mode="wait": the old one fades out over 0.2 s, the new one fades in only once its own load event has fired, so a slow file never shows a half-drawn image. The first photograph to load, together with the frame being in view, opens the frame with a top-to-bottom clip over 1.6 s while the image settles from 1.18 to 1.12 scale.mode="wait" grammar.prefers-reduced-motion the frame renders open, the drift is zero, photographs appear without fades and rows do not rise.