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²)…

Headless hookClient component@pandaidx/widgets 1.14.1

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 widget

The agent's off-market (exclusive / pocket) listings as the Panda websites /listings page: headline and the websites cards (hover photo…

</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

OffMarketListings
// app/listings/page.tsx
import { 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

OffMarketListings
// app/listings/page.tsx
import { 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

PropTypeDefaultDescription
data*object—Listings + site URL (null → notFound()).
layout"page" | "grid""page"page: section + container + headline (websites); grid: the cards only.
titlestring—Headline (page layout); default "Listings" (translated); false hides it.
columnsnumber—Fixed card columns from lg (1 on phones, 2 from sm); default the websites fluid grid (320–400px cards).
cardobject—Card design shared with ListingGrid: gallery (arrows | none), photo radius (CSS length), actions.items (include share to keep the share button).
listingHrefstring"/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.
themeobject—Token overrides for this widget only, merged over the provider theme (same shape as PandaProvider.theme).

Hooks

useOffMarketListings()inside PandaProvider

useOffMarketListings({ 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