Condo Area
One condo area (Panda /area/<slug>): hero with description, market counters, price stats, neighborhood scores, the area outline map and its buildings with paging.
Anatomy
Every piece of Condo Area 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<CondoArea>One condo area (Panda /area/<slug>): hero with description, market counters, price stats, neighborhood scores, the area outline map and its…<CondoAreaMap />The websites buildings map (price pills in the theme's map pin look, a popup per building with its link, the area…
</CondoArea></PandaProvider>Installation
- 1npm install <tarball or @pandaidx/widgets>Terminalnpm install https://github.com/pandaidx/panda-widgets/releases/download/v1.14.1/pandaidx-widgets-1.14.1.tgz
- 2import "@pandaidx/widgets/styles.css" in app/layout.tsx
- 3Wrap the site in <PandaProvider publicKey apiUrl preset theme> (app/layout.tsx)
- 4In each page (Server Component) call the widget's server loader, then render the widget with its data
- 5Allow the images: next.config images.remotePatterns [{ protocol: "https", hostname: "**" }] (listing and blog photos come from several CDNs)
Usage
// app/condos/areas/[slug]/page.tsximport { notFound } from "next/navigation"import { CondoArea } from "@pandaidx/widgets/client"import { fetchCondoArea } from "@pandaidx/widgets/server"export default async function AreaPage({ params }: { params: Promise<{ slug: string }> }) {const { slug } = await paramsconst data = await fetchCondoArea({ publicKey: process.env.NEXT_PUBLIC_PANDA_PUBLIC_KEY!, apiUrl: process.env.NEXT_PUBLIC_PANDA_API_URL, slug })if (!data) notFound()return <CondoArea data={data} map="top" />}
Examples
An area page in the site's own hero, stats and building cards
// app/area/[slug]/page.tsximport { notFound } from "next/navigation"import { fetchCondoArea } from "@pandaidx/widgets/server"import { AreaView } from "@/components/area-view"export default async function AreaPage({ params }: { params: Promise<{ slug: string }> }) {const { slug } = await paramsconst data = await fetchCondoArea({ publicKey: process.env.NEXT_PUBLIC_PANDA_PUBLIC_KEY!, apiUrl: process.env.NEXT_PUBLIC_PANDA_API_URL, slug })if (!data) notFound()return <AreaView data={data} />}// components/area-view.tsx"use client"import Link from "next/link"import { CondoAreaMap, useCondoArea, type SiteCondoAreaResponse } from "@pandaidx/widgets/client"import { resolveLibraryHref } from "@pandaidx/widgets/search"const BuildingLink = ({ building }: { building: SiteCondoAreaResponse["buildings"][number] }) => {const href = resolveLibraryHref(building, "default", { buildingDetail: "/condos/{slug}" })return href ? <Link href={href}>{building.name}</Link> : <span>{building.name}</span>}export const AreaView = ({ data }: { data: SiteCondoAreaResponse }) => {const area = useCondoArea({ data })return (<main className="mx-auto max-w-6xl px-4 py-16"><h1 className="text-4xl">{area.area.name}</h1>{area.paragraphs.map((text) => <p key={text} className="mt-4">{text}</p>)}<dl className="mt-8 grid grid-cols-2 gap-4 md:grid-cols-4">{area.stats.map((fact) => <div key={fact.label}><dt className="text-sm opacity-70">{fact.label}</dt><dd className="text-xl">{fact.value}</dd></div>)}</dl><CondoAreaMap buildings={area.buildings} boundary={area.area.boundary} routes={{ buildingDetail: "/condos/{slug}" }} className="mt-10 h-96" /><ul className="mt-10 grid gap-6 md:grid-cols-3">{area.buildings.map((building) => <li key={building.id}><BuildingLink building={building} /></li>)}</ul><div className="mt-8 flex gap-4">{area.page > 1 ? <button type="button" onClick={() => area.setPage(area.page - 1)}>Previous</button> : null}{area.page < area.totalPages ? <button type="button" onClick={() => area.setPage(area.page + 1)}>Next</button> : null}</div></main>)}
API reference
CondoArea
One condo area (Panda /area/<slug>): hero with description, market counters, price stats, neighborhood scores, the area outline map and its buildings with paging.
Import from @pandaidx/widgets/client · renders inside PandaProvider
| Prop | Type | Default | Description |
|---|---|---|---|
| data* | object | — | Area page (null → notFound()). |
| sections | "overview" | "market" | "stats" | "neighborhood" | "buildings" | ["overview","market","stats","neighborhood","buildings"] | Blocks and their order. |
| map | "side" | "top" | "off" | "top" | Area map beside the buildings, above them, or none. |
| columns | object | 3 | Building card columns (default 2 with the map beside, else 3). |
| cardVariant | "classic" | "overlay" | "minimal" | "horizontal" | "classic" | Building card design. |
| buildingHref | string | "/condos/{slug}" | Host route of a building. |
| classNames | object | — | Host classes appended to any slot (see slots). |
| preset | "default" | "template1" | — | Preset for this widget only (defaults to the provider preset). |
| variant | "default" | "template1" | "panda" | — | panda: the Panda website condo area page (breadcrumb, title, Share, Request Information, section nav, hero, stats, About with clickable condo details, neighborhood features, the area map and its condos with filters / sort / grid / download, request-info band with the agent card) with the website components (pass fetchPandaCondoArea() data; every other design prop is ignored). default / template1: deprecated alias of preset. |
| theme | object | — | Token overrides for this widget only, merged over the provider theme (same shape as PandaProvider.theme). |
Parts
<CondoAreaMap />The websites buildings map (price pills in the theme's map pin look, a popup per building with its link, the area outline, zoom controls at the kit's small control size) for useCondoArea().buildings, useCondoDirectory().buildings or useCondoDirectory({ pins: true }).pins; boundary takes area.boundary; routes ({ buildingDetail: "/condos/{slug}" }) points the popup links at your building pages. It fills its parent: give it a height with className. hoveredId / onHover link it to your building cards.
Hooks
useCondoArea()inside PandaProvideruseCondoArea({ data }) → { area: { slug, name, image, description, boundary… }, paragraphs (the description), market: { key, label, today, value, change, changeText }[] (For Sale, For Rent, Pending, Sold…; zeros left out), stats: { label, value }[] (formatted averages, price ranges and counts; empty ones left out), scores: { label, score, rating }[] (Walk, Bike, Transit), highlights: { label, text }[] (restaurants, parks…), buildings (current page; link each with its path), total, page, pageSize, totalPages, setPage(page), loading } — data comes from fetchCondoArea (null → notFound()). setPage loads the buildings of another page in place (the previous page stays in buildings while it loads). Draw the hero, counters, stats, scores and building cards in the site's style; CondoArea stays the ready-made page. Client component only.
Server data
Loaders run in a Server Component and come from @pandaidx/widgets/server. Typed data is for a page you design yourself, rendered inside the root.
fetchCondoArea()widget loaderGET /v1/condos/areas/:slug (page from the URL); null for an unknown area. Returns SiteCondoAreaResponse | null → data
fetchPandaCondoArea()loader of variant="panda"Data of variant="panda" (GET /v1/condos/directory/area/:slug): the area, the directory areas, the last market totals, the public application and the request-information form; null for an unknown area (notFound()). Returns PandaCondoAreaData | null → data