Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Property Search Filters Map Block for React Aria

A homes-for-sale map shown with its filters sheet open beside it: a two-thumb price slider, a bedrooms toggle and property types, with price pills and a held-height list that follow every change.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Narrow a map of homes for sale without leaving it, with this property search filters map for React with React Aria. Hearthfind, a property portal, draws fourteen homes for sale as price pills on an SVG street map of the invented harbour town of Wrenmouth, and a list under the map names the same homes cheapest first. The header's Filters button, which counts the filter groups that are set, opens a Sheet with a two-thumb price Slider from $100k to $900k, a bedrooms Toggle Group (Any, 1+ to 4+) and three property types as checkboxes, and its one Shine Button reads Show N homes with the live count. A home the filters leave out stays on the map in place, hidden, and leaves the list; a filter that matches nothing shows an empty state in the same box. For preview the sheet is drawn open in place at the start of the map when the page loads; Filters opens the real Sheet with the same fields, and from a phone the copy docks to the foot of the map. For property portals, car and rental searches, and any map with a filter panel.

FAQ

Why is the sheet already open, and what do I keep?

So you can see it without a click. On load the sheet is a copy drawn in place over the map: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Filters button opens the real Sheet, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same FilterFields component and share the block's state, so a price set in the copy is the price in the real one. In your app keep the Sheet and drop the copy: the "in-place" state, the scrim and the dialog element, and the Escape effect.

How do the filters combine?

A home is shown when its price is inside the slider's two thumbs, it has at least the chosen number of bedrooms and its type is ticked. Prices are integer cents, so the slider works in thousands of dollars and the comparison multiplies back with CENTS_PER_K. The trigger counts the groups that differ from their open state (a narrowed price, a bedroom minimum, a type unticked), not the number of values.

Does the sheet follow the reading direction?

Yes. It opens at the start of the layout, which the catalog's Sheet calls left or right, so the block reads the page's computed direction when you press Filters and passes the matching side. The copy shown on load uses logical classes and sits at the start already.

Can I use it without a mouse?

Yes. Filters is a button; inside the sheet the slider thumbs take the arrow keys and announce "From $200k" and "Up to $750k", the bedroom toggle is one tab stop, and the type boxes are real checkboxes. The list under the map is the way in to each home; the pins are hidden from assistive technology and skipped by Tab. A polite message speaks the count once the controls settle.

What stays local in the demo, and how do I wire it up?

All of it. The homes are typed sample data, the filters run in the tab, and nothing is searched, saved or sent. In your app fetch the homes for the filters (or for the map's bounds), keep the count on the button from the response, and replace the drawing with your map library's markers.

How does it look on a phone?

The map comes first. The copy shown on load docks to the foot of the map as a bottom sheet with its fields scrolling and its buttons pinned; the real Sheet keeps the catalog's own phone layout. The list is a box that scrolls on its own, so filtering never changes the height of the page.