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…

Root + blocksClient component@pandaidx/widgets 1.14.1

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 widget

A 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 order
id="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

  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

NewDevelopmentDetail
// app/new-developments/[slug]/page.tsx
import { 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 params
const 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

NewDevelopmentDetail
"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

NewDevelopmentDetail
<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")

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

PropTypeDefaultDescription
newDevelopment*object—Project detail.
listings*object—First for-sale / for-rent pages of resale units.
application*object—Public application (listing cards, detail dialog).
leadFormobject—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).
listingColumnsobject2Listing card columns (default 2 with the sidebar, else 3).
listingCardVariant"horizontal" | "classic" | "overlay" | "minimal""classic"Listing card design.
searchPathstring"/properties"Host page of <PropertyDetail> (<searchPath>/<ListingId>).
leadFormTitlestring"Get More Details"Heading of the lead form.
classNamesobject—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).
themeobject—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.
partsobject—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 NewDevelopmentRoot

The 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 loader

Project (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 data

Typed 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.