Home Search

Homepage hero search of the Panda websites: For Sale / For Rent / Sold tabs over the pill autocomplete (cities, buildings, zip codes, addresses, MLS#) with the black round search button on desktop, and tabs + a pill…

Headless hookClient component@pandaidx/widgets 1.14.1

Anatomy

Every piece of Home Search as it nests in code. A root holds the data and state; its parts and hooks only work inside it.

<PandaProvider>Mounted once in app/layout.tsx
<HomeSearch />complete widget

Homepage hero search of the Panda websites: For Sale / For Rent / Sold tabs over the pill autocomplete (cities, buildings, zip codes,…

</PandaProvider>

Installation

  1. 1
    npm install <tarball or @pandaidx/widgets>
    Terminal
    npm install https://github.com/pandaidx/panda-widgets/releases/download/v1.14.1/pandaidx-widgets-1.14.1.tgz
  2. 2
    import "@pandaidx/widgets/styles.css" in app/layout.tsx
  3. 3
    Wrap the site in <PandaProvider publicKey apiUrl preset theme> (app/layout.tsx)
  4. 4
    In each page (Server Component) call the widget's server loader, then render the widget with its data
  5. 5
    Allow the images: next.config images.remotePatterns [{ protocol: "https", hostname: "**" }] (listing and blog photos come from several CDNs)

Usage

HomeSearch
// app/page.tsx: websites cover (full-height photo, search at the bottom)
import Image from "next/image"
import { HomeSearch } from "@pandaidx/widgets/client"
export default function HomePage() {
return (
<section className="relative flex h-122.5 items-end pb-10 sm:h-screen lg:pb-25">
<Image src={site.homepage.cover} alt="" fill priority sizes="100vw" className="object-cover" />
<div className="relative flex w-full justify-center">
<HomeSearch />
{/* Studio homepage look: <HomeSearch variant="minimal" buttonStyle="text" /> */}
</div>
</section>
)
}

Examples

The hero search in the site's own markup (tabs, field, suggestions, button)

HomeSearch
"use client"
import { useHomeSearch } from "@pandaidx/widgets/client"
// Draw it in the site's style; Panda owns the suggestions, the keyboard and the navigation.
export const HeroSearch = () => {
const search = useHomeSearch()
return (
<div className="relative mx-auto w-full max-w-2xl" onPointerEnter={search.warm}>
<div role="tablist" className="mb-3 flex gap-6">
{search.markets.map((tab) => (
<button key={tab.id} type="button" role="tab" aria-selected={tab.active} onClick={() => search.setMarket(tab.id)} className={tab.active ? "border-b-2 border-foreground pb-1" : "pb-1 opacity-60"}>
{tab.label}
</button>
))}
</div>
<div className="flex items-center border-b border-foreground/30">
<input {...search.inputProps} aria-label={search.placeholder} className="h-14 flex-1 bg-transparent outline-none" />
<button type="button" disabled={search.pending} onClick={() => void search.submit()} className="px-4 text-sm uppercase tracking-wide">
Search
</button>
</div>
{search.error ? <p role="alert" className="mt-2 text-sm">We could not load suggestions. Try again.</p> : null}
{search.open ? (
<ul {...search.listProps} aria-busy={search.loading} className="absolute inset-x-0 top-full z-10 bg-background shadow-lg">
{search.options.map((option, index) => (
<li key={option.id} {...search.optionProps(option, index)} className={index === search.activeIndex ? "bg-muted px-4 py-3" : "px-4 py-3"}>
<span className="block">{option.label}</span>
{option.detail ? <span className="block text-sm opacity-60">{option.detail}</span> : null}
</li>
))}
</ul>
) : null}
</div>
)
}

API reference

HomeSearch

Homepage hero search of the Panda websites: For Sale / For Rent / Sold tabs over the pill autocomplete (cities, buildings, zip codes, addresses, MLS#) with the black round search button on desktop, and tabs + a pill that opens the fullscreen search dialog on phones. Selecting a place navigates to the search page (searchPath, default the PandaProvider searchPath) with the market's status/type, an MLS# to /mls/<id>. Place it over the cover image; every default matches websites.

Import from @pandaidx/widgets/client · renders inside PandaProvider

PropTypeDefaultDescription
searchPathstring—Search page on the host; default the <PandaProvider searchPath> (/search).
tabsarray["sale","rent","sold"]Market tabs in order (sale, rent, sold); false hides them.
tabLabelsobject—Tab labels of this instance, keyed by market ({ sale: "Buy", rent: "Rent", sold: "Sold" }); each wins over the dictionary (filters.sale / rent / sold) on desktop and phones. Omitted markets keep the dictionary label.
initialMarket"sale" | "rent" | "sold""sale"Tab selected on load.
placeholderstring"Enter city, condo, zip code, MLS #, or address"Placeholder of the desktop bar and the mobile pill.
variant"pill" | "rounded" | "classic" | "minimal""pill"pill: the websites cover bar (translucent pill tabs, white pill bar, black round button). rounded / classic / minimal: the Studio search block designs used by Studio homepages (floating translucent tabs over a pill bar; 8px corners with tinted tabs; square tabs attached to an underlined square bar). Studio designs use background, color and spacing.
backgroundstring"var(--panda-surface, #ffffff)"Studio designs: bar and inactive-tab background (any CSS color).
colorstring"var(--panda-ink, #171717)"Studio designs: text, active tab and button color (any CSS color).
spacing"default" | "compact""default"Studio designs: inner padding of the bar (10px / 4px).
tone"light" | "dark""light"pill tabs over a dark photo/video (light, websites: white on translucent white) or a light background (dark: ink text, ink active pill).
size"md" | "lg""md"Bar size: websites (md) or taller with larger text (lg).
buttonStyle"icon" | "text""icon"Round search icon (websites) or a labeled button (buttonLabel).
buttonLabelstring"Search"Label of the text button.
buttonIcon"search" | "arrow" | "none""search"Icon of the text button: the search magnifier before the label (search), an arrow after it (arrow), or none. The icon button always shows the magnifier.
buttonTone"black" | "primary""black"pill button color: websites black, or the theme colors.primary / primaryForeground (Studio designs use color).
buttonBackgroundstring—Search button fill (CSS color or a role like var(--ds-color-accent)), desktop bar and phone pill. Wins over buttonTone. Use it instead of classNames.button color hacks.
buttonColorstring—Search button text and icon color (CSS color), desktop bar and phone pill. Default the theme button text.
classNamesobject—Host classes appended to parts: root, tabsList, tab, bar, input, button.
widthstring"670px"Max width of the bar (CSS length). Layout param: never resize the widget markup with CSS selectors.
align"start" | "center" | "end""center"Horizontal alignment of the tabs and the bar.
tabsPlacement"above" | "below""above"Market tabs above the bar (websites) or below it.
tabsAlign"start" | "center" | "end"—Horizontal alignment of the tabs alone (default: align).
shape"pill" | "rounded" | "square"—Corners of the bar, the tabs and the search button (desktop bar and phone pill): pill, rounded (the site's field radius), square. Default: the site's radius.button (DESIGN.md), the websites pill when unchanged.
surfacestring—Bar fill, desktop bar and phone pill (CSS color, a role like var(--ds-color-surface), or transparent). The text and placeholder switch to black or white automatically so they stay readable. Default the theme surface (white).
borderstring—Bar border color, or none. Default the theme line color.
tabStyle"glass" | "solid" | "underline""glass"Market tabs look: glass (websites translucent pill over the photo), solid (tabs on the bar fill, active = primary button), underline (text tabs with an underline).
tabsAttachedbooleanfalseTabs sit on the bar edge with no gap (the bar corner under the tabs becomes square), like folder tabs.
suggestionsobject—Look of the results dropdown (desktop); every field optional, every default the websites look: images (boolean, photo/icon per result, default true), imageShape circle | rounded | square, size sm | md | lg (row height, text and photo size), weight normal | medium | semibold | bold (main text), itemShape square | rounded | pill (highlighted row corners), highlight soft | primary | none (hovered / arrow-selected row), panelShape match (the bar) | rounded | square, shadow (boolean, default true). Example: suggestions={{ images: false, size: "lg", weight: "semibold", itemShape: "square", highlight: "primary" }}.
preset"default" | "template1"—Preset for this widget only (defaults to the provider preset).
themeobject—Token overrides for this widget only, merged over the provider theme (same shape as PandaProvider.theme).

Hooks

useHomeSearch()inside PandaProvider

useHomeSearch({ searchPath?, markets?: ["sale","rent","sold"], initialMarket?, marketLabels?, placeholder?, minLength?: 1 }) → { markets: { id, label, active }[], market, setMarket(id), query, setQuery(value), options: { id, kind: "building" | "area" | "development" | "postalCode" | "address" | "unit" | "listing" | "smart" | "text", label, detail, image, raw }[] (grouped and limited like the Panda bar), open, activeIndex, setActiveIndex(i), loading, error, pending, placeholder, select(option) (navigates to the search page with the market's status/type, an MLS# to its page, and returns the URL), submit() (the highlighted option, else Panda's smart suggestion or first result for the typed text; Enter), close(), warm(), inputProps (spread on your <input>: value, onChange, onKeyDown, onFocus, onBlur, placeholder and the combobox aria), listProps (spread on the list: id, role="listbox"), optionProps(option, index) (spread on each row: id, role="option", aria-selected, hover, click) } — the site's own hero search: tabs, field, suggestion list (with the photo of an area or building when image), button, error line and loading state in the site's style, on desktop and phones. Panda owns the autocomplete service, the catalog warm-up, the keyboard and the navigation; never call /v1/autocomplete or /v1/suggestions yourself and never read the Panda bar's markup with CSS. Client component only.