Market Pulse

Live market of several areas side by side ("Market pulse"), over the site's own MLS feeds and refreshed every hour: homes for sale with the change vs last week, the market type (seller's under 4 months of supply,…

Complete widgetClient component@pandaidx/widgets 1.15.0

Anatomy

Every piece of Market Pulse 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
<MarketPulse />complete widget

Live market of several areas side by side ("Market pulse"), over the site's own MLS feeds and refreshed every hour: homes for sale with the…

</PandaProvider>

Installation

  1. 1
    npm install <tarball or @pandaidx/widgets>
    Terminal
    npm install https://github.com/pandaidx/panda-widgets/releases/download/v1.15.0/pandaidx-widgets-1.15.0.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

MarketPulse
// app/page.tsx (a section of any page): the areas of the homepage library, each with a CTA to its CMA
import { MarketPulse } from "@pandaidx/widgets/client"
import { fetchMarketPulse } from "@pandaidx/widgets/server"
const config = { publicKey: process.env.NEXT_PUBLIC_PANDA_PUBLIC_KEY!, apiUrl: process.env.NEXT_PUBLIC_PANDA_API_URL }
export default async function MarketSection() {
const data = await fetchMarketPulse({ ...config, limit: 6 })
// The CTA opens /home-market-analysis?area=<slug>, the page with <MarketAnalysis valuation={await fetchHomeValuation({ searchParams })} />.
return <MarketPulse data={data} title="Market pulse" subtitle="Live numbers from the MLS, updated every hour" />
}

API reference

MarketPulse

Live market of several areas side by side ("Market pulse"), over the site's own MLS feeds and refreshed every hour: homes for sale with the change vs last week, the market type (seller's under 4 months of supply, buyer's over 7), the medians of the last three months of closings (sale price, $/sq ft, days on market, sale-to-list), months of supply, closings of the last 30 days with their change, and a CTA per area to its prefilled CMA (MarketAnalysis with ?area=<slug>), plus a link to its homes for sale. layout: cards (one card per area) or table (one row per area). The areas are an area library (library, default the first homepage library, ranked by sort) or a list of slugs (areas). For any other design (a ticker, one big stat, a chart, a quiz that ends on the CMA) use the data level: fetchMarket({ slug }) / fetchMarketPulse() give the typed numbers, formatMarketFact / formatChange / marketTypeLabel the texts and marketCmaHref the CMA link. Never hard-code market numbers in a page: they go stale; read them from these loaders.

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

PropTypeDefaultDescription
data*object—{ asOf, type, library, areas } (null or empty renders nothing).
titlestring—Section heading (none by default); pass await getText(...).
subtitlestring—Line under the heading; the update date shows beside it.
layout"cards" | "table""cards"cards: a card per area (1 / 2 / 3 columns). table: a row per area (scrolls sideways on phones).
facts"active" | "pending" | "sold30" | "medianSalePrice" | "medianPricePerSqft" | "medianDaysOnMarket" | "saleToListPct" | "monthsOfSupply"["medianSalePrice","medianPricePerSqft","medianDaysOnMarket","monthsOfSupply"]Numbers of each area, in order (homes for sale always shows on top of a card).
cmaHrefstring"/home-market-analysis?area={slug}"Page of the prefilled CMA (the page with MarketAnalysis); {slug} is the area. null hides the CTA.
areaHrefstring"/search?q={area}"Homes for sale of an area; {area} is its search query, {slug} its slug. null hides the link.
sourcestring"market-pulse"source query sent with the CMA link (tells the CMA page or its form which tool sent the visitor).
labelsobject—Copy overrides: fact labels (active, medianSalePrice …), sellers / balanced / buyers, vsLastWeek, vsPrevious30, days, months, cta (with {{area}}), browse, updated (with {{date}}). Pass await getText({ id, label, text }) for each.
classNamesobject—Classes appended to section, container, header, title, subtitle, grid, card, cardBody, cardTitle, chip, factLabel, factValue, button, panel, row.
preset"default" | "template1"—Preset for this widget only (defaults to the provider preset).
variant"default" | "template1"—Deprecated alias of preset.
themeobject—Token overrides for this widget only, merged over the provider theme (same shape as PandaProvider.theme).

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.

fetchMarketPulse()widget loader

GET /v1/market (each area's snapshot, changes and the cma.area of its prefilled CMA, over the site's MLS feeds; cached an hour). Null on failure (never throws). Returns MarketPulseData | null → data

fetchMarket()typed data

One area's market (GET /v1/market/:slug) for a design of your own: { area: { name, slug, query }, asOf, type, snapshot: { active, pending, sold7, sold30, sold90, medianSalePrice, medianPricePerSqft, medianDaysOnMarket, saleToListPct, monthsOfSupply, marketType }, changes: { activeWeekPct, sold30Pct, pricePerSqftYearPct }, history: { month, sales, medianSalePrice, medianPricePerSqft, medianDaysOnMarket, saleToListPct }[] (15 months), inventory: { date, active }[] (30 days), cma: { area } }; params { slug, type?: "all" | "condo" | "house" }; null for an unknown area. Texts: formatMarketFact(fact, snapshot), formatChange(pct), marketTypeLabel(snapshot); the CTA: marketCmaHref("/home-market-analysis", market).