New Development Detail
A pre-construction project page (Panda /new-development/<slug>): gallery, description, brochure and video, specs (asking prices, sizes, units, developer, architect), amenities, floor plans, deposit schedule, resale…
Anatomy
Every piece of New Development Detail 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<NewDevelopmentDetail />complete widgetA pre-construction project page (Panda /new-development/<slug>): gallery, description, brochure and video, specs (asking prices, sizes,…
<NewDevelopmentRoot>Holds the data and state its parts and hooks read<NewDevelopmentBlock>One block per id, in any orderid="hero"id="stats"id="about"id="details"id="gallery"id="highlights"id="listings"id="neighborhood"id="amenities"id="downloads"</NewDevelopmentBlock><NewDevelopmentBlock />One block of the Panda project page by id (hero | stats | about | details | gallery | highlights | listings |…
</NewDevelopmentRoot></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/new-developments/[slug]/page.tsximport { notFound } from "next/navigation"import { NewDevelopmentDetail } from "@pandaidx/widgets/client"import { fetchNewDevelopmentDetail } from "@pandaidx/widgets/server"export default async function ProjectPage({ params }: { params: Promise<{ slug: string }> }) {const { slug } = await paramsconst data = await fetchNewDevelopmentDetail({ publicKey: process.env.NEXT_PUBLIC_PANDA_PUBLIC_KEY!, apiUrl: process.env.NEXT_PUBLIC_PANDA_API_URL, slug })if (!data) notFound()return <NewDevelopmentDetail {...data} galleryVariant="carousel" contactPlacement="sidebar" />// Panda websites look: <NewDevelopmentDetail {...data} variant="panda" />}
Examples
Own hero, the rest of the Panda page
"use client"import Image from "next/image"import { useNewDevelopmentDetail } from "@pandaidx/widgets/client"export const ProjectHero = () => {const { displayName, photos, photoAlt, openRequest } = useNewDevelopmentDetail()return (<section className="relative h-[80vh]">{photos[0] ? <Image src={photos[0]} alt={photoAlt(0)} fill sizes="100vw" className="object-cover" priority /> : null}<div className="absolute inset-x-0 bottom-0 p-10 text-background"><h1 className="text-5xl">{displayName}</h1><button type="button" onClick={openRequest}>Request information</button></div></section>)}// <NewDevelopmentDetail {...data} variant="panda" parts={{ hero: <ProjectHero /> }} />
Root + blocks in your own order, with your own key facts
<NewDevelopmentRoot {...data}><NewDevelopmentBlock id="hero" /><ProjectFacts />{/* "use client": useNewDevelopmentDetail().keyDetails + deposits as cards */}<NewDevelopmentBlock id="gallery" /><NewDevelopmentBlock id="listings" /><NewDevelopmentBlock id="amenities" /><NewDevelopmentBlock id="neighborhood" /></NewDevelopmentRoot>
The websites new development page composed block by block (same blocks and order as NewDevelopmentDetail variant="panda")
<NewDevelopmentRoot {...data}>{/* The websites project page in its order (amenities and downloads live inside details / about there). */}<NewDevelopmentBlock id="hero" /><NewDevelopmentBlock id="stats" /><NewDevelopmentBlock id="about" /><NewDevelopmentBlock id="details" /><NewDevelopmentBlock id="gallery" /><NewDevelopmentBlock id="highlights" /><NewDevelopmentBlock id="listings" /><NewDevelopmentBlock id="neighborhood" /></NewDevelopmentRoot>
API reference
NewDevelopmentDetail
A pre-construction project page (Panda /new-development/<slug>): gallery, description, brochure and video, specs (asking prices, sizes, units, developer, architect), amenities, floor plans, deposit schedule, resale listings inside, map and a lead form (new-development spot). Inquiries go to the site owner, never to the developer's lead agent.
Import from @pandaidx/widgets/client · renders inside PandaProvider
| Prop | Type | Default | Description |
|---|---|---|---|
| newDevelopment* | object | — | Project detail. |
| listings* | object | — | First for-sale / for-rent pages of resale units. |
| application* | object | — | Public application (listing cards, detail dialog). |
| leadForm | object | — | Lead form (null hides the contact block). |
| sections | "gallery" | "overview" | "specs" | "amenities" | "floorplans" | "deposits" | "listings" | "map" | "contact" | ["gallery","overview","specs","amenities","floorplans","deposits","listings","map","contact"] | Blocks and their order (empty blocks are skipped). |
| galleryVariant | "carousel" | "grid" | "masonry" | "carousel" | Photo presentation: slider strip, one large photo + four tiles, or a masonry wall (all open a fullscreen viewer). |
| contactPlacement | "sidebar" | "bottom" | "sidebar" | Lead form as a sticky sidebar or after the content. |
| listingStatuses | "sale" | "rent" | ["sale","rent"] | Resale listing tabs (tabs without units are hidden). |
| listingColumns | object | 2 | Listing card columns (default 2 with the sidebar, else 3). |
| listingCardVariant | "horizontal" | "classic" | "overlay" | "minimal" | "classic" | Listing card design. |
| searchPath | string | "/properties" | Host page of <PropertyDetail> (<searchPath>/<ListingId>). |
| leadFormTitle | string | "Get More Details" | Heading of the lead form. |
| 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 websites project page (fullscreen Ken Burns hero with logo, Request Information / Share Development CTAs, Developer · Architect · Interior stats, About split with sign-up-gated brochure / floor plan and the video, key details + deposit schedule beside a sticky image, gallery wall + legal disclaimer, highlights, one table of units for sale and rent, neighborhood map; lead form in a dialog; default preset). default / template1: deprecated alias of preset (template design). |
| theme | object | — | Token overrides for this widget only, merged over the provider theme (same shape as PandaProvider.theme). |
| pandaSections | "hero" | "stats" | "about" | "details" | "gallery" | "highlights" | "listings" | "neighborhood" | ["hero","stats","about","details","gallery","highlights","listings","neighborhood"] | panda only: blocks to render (the websites order is kept; your own order is NewDevelopmentRoot + NewDevelopmentBlock). amenities / downloads are only for your own layout: the page shows them inside details / about. |
| parts | object | — | panda only: your own element in place of one piece, keeping the rest of the page: { hero: <MyHero /> }, { gallery: <Mosaic /> } (the Gallery heading and legal disclaimer stay). Build them with useNewDevelopmentDetail() (photos, openPhotos(i) = the page viewer, openRequest, openVideo). |
Parts
<NewDevelopmentBlock />One block of the Panda project page by id (hero | stats | about | details | gallery | highlights | listings | neighborhood, the websites order; standalone amenities (Additional details) and downloads (brochure + floor plan, behind sign-up)), anywhere inside the root. Renders nothing without data.
Hooks
useNewDevelopmentDetail()inside NewDevelopmentRoot{ newDevelopment, listings, application, leadForm, displayName, address, photos, photoAlt(i), stats: { label, value }[], keyDetails: { label, value }[], deposits: string[], highlights: string[], amenities: { amenities, features }, amenityRows { building, additional } (draw with BuildingAmenityList), downloads: { brochure, floorplans }, downloadLink(href, label, icon?) (behind sign-up), openRequest(), openPhotos(i?), openVideo() | null } — import from @pandaidx/widgets/client; inside NewDevelopmentRoot or the parts of NewDevelopmentDetail variant="panda"
useBuildingListings()inside NewDevelopmentRootThe project's units (for sale and for rent in one list) headless: { filtered, listings, bedsTab, setBedsTab, bedOptions, unitFilter, setUnitFilter, bathsMin, setBathsMin, sort, chooseSort, sortOptions, view, chooseView, openListing(listing), statusLabel(listing), unitLabel(listing), isRentGroup(listing) } — draw them with BuildingListingCard, BuildingListingsToolbar and BuildingListingsResults, or your own markup
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.
fetchNewDevelopmentDetail()widget loaderProject (GET /v1/new-developments/:slug) with its first listing pages, the public application and the lead form; null when unknown (call notFound()). Returns NewDevelopmentDetailData | null → spread into the widget ({...data})
fetchNewDevelopmentDetail()typed dataTyped project for a page you design yourself: render it inside <NewDevelopmentRoot {...data}> (Request Information, viewer, video, sign-up-gated downloads) with blocks or useNewDevelopmentDetail(); your own markup between the blocks (and parts.hero / parts.gallery) renders outside the widget styles, so it keeps the site's DESIGN.md variables, type scale and kit.