Off-Market Listings
The agent's off-market (exclusive / pocket) listings as the Panda websites /listings page: headline and the websites cards (hover photo gallery with arrows and dots, price with its qualifier, share, beds / baths / ft²)…
Anatomy
Every piece of Off-Market Listings 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<OffMarketListings />complete widgetThe agent's off-market (exclusive / pocket) listings as the Panda websites /listings page: headline and the websites cards (hover photo…
</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/listings/page.tsximport { notFound } from "next/navigation"import { OffMarketListings } from "@pandaidx/widgets/client"import { fetchOffMarketListings } from "@pandaidx/widgets/server"export default async function ListingsPage() {const data = await fetchOffMarketListings({ publicKey: process.env.NEXT_PUBLIC_PANDA_PUBLIC_KEY!, apiUrl: process.env.NEXT_PUBLIC_PANDA_API_URL })if (!data) notFound()return <OffMarketListings data={data} />}
Examples
Exclusive listings in the site's own cards
// app/listings/page.tsximport { fetchOffMarketListings } from "@pandaidx/widgets/server"import { ExclusivesView } from "@/components/exclusives-view"export default async function ListingsPage() {const data = await fetchOffMarketListings({ publicKey: process.env.NEXT_PUBLIC_PANDA_PUBLIC_KEY!, apiUrl: process.env.NEXT_PUBLIC_PANDA_API_URL })return data ? <ExclusivesView data={data} /> : null}// components/exclusives-view.tsx"use client"import Image from "next/image"import Link from "next/link"import { useOffMarketListings, type OffMarketListingsData } from "@pandaidx/widgets/client"export const ExclusivesView = ({ data }: { data: OffMarketListingsData }) => {const { listings } = useOffMarketListings({ data })return (<section className="mx-auto max-w-6xl px-4 py-16"><div className="grid gap-8 md:grid-cols-3">{listings.map((card) => (<Link key={card.id} href={card.href}>{card.image ? <Image src={card.image} alt="" width={640} height={480} sizes="(min-width: 768px) 33vw, 100vw" className="aspect-[4/3] w-full object-cover" /> : null}<p className="mt-3 text-xl">{card.priceLabel}</p><p>{card.address}</p></Link>))}</div></section>)}
API reference
OffMarketListings
The agent's off-market (exclusive / pocket) listings as the Panda websites /listings page: headline and the websites cards (hover photo gallery with arrows and dots, price with its qualifier, share, beds / baths / ft²) in a fluid grid, each linking to /listing/<slug> (render that page with PropertyDetail variant="panda" + fetchOffMarketListing). layout="grid" renders the cards alone inside your own section.
Import from @pandaidx/widgets/client · renders inside PandaProvider
| Prop | Type | Default | Description |
|---|---|---|---|
| data* | object | — | Listings + site URL (null → notFound()). |
| layout | "page" | "grid" | "page" | page: section + container + headline (websites); grid: the cards only. |
| title | string | — | Headline (page layout); default "Listings" (translated); false hides it. |
| columns | number | — | Fixed card columns from lg (1 on phones, 2 from sm); default the websites fluid grid (320–400px cards). |
| card | object | — | Card design shared with ListingGrid: gallery (arrows | none), photo radius (CSS length), actions.items (include share to keep the share button). |
| listingHref | string | "/listing/{slug}" | Detail route ({slug}). |
| preset | "default" | "template1" | — | Preset for this widget only (defaults to the provider preset). |
| variant | "default" | "template1" | — | Deprecated alias of preset. |
| theme | object | — | Token overrides for this widget only, merged over the provider theme (same shape as PandaProvider.theme). |
Hooks
useOffMarketListings()inside PandaProvideruseOffMarketListings({ data, listingHref?: "/listing/{slug}" }) → { listings: { id, slug, title, address, href, shareUrl, image, images, priceLabel ("$2,500/mo", "$1,250,000" with the agent's qualifier), price, listingStatus, forLease, type, group, commercial, bedrooms, bathrooms (half = 0.5), squareFeet, squareFeetLabel }[], forSale, forLease, total, siteUrl, empty } — data comes from fetchOffMarketListings (null → render nothing). Link each card with its href (/listing/<slug>), share with <ShareButton url={card.shareUrl} name={card.title} image={card.image}>, show photos with next/image and sizes. OffMarketListings 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.
fetchOffMarketListings()widget loader/v1/offmarket (every photo of each card) + the site URL for share links; null when the agent has none. Detail pages: fetchOffMarketListing({ slug }) → PropertyDetail props + offMarket (title, remarks, photos for metadata). Returns OffMarketListingsData | null → data