Like and Share
The websites listing heart for custom listing cards: signed-out visitors get the sign-up dialog; signed-in visitors like / unlike the listing (their account, the visitor drawer and the owner's CRM activity).
Anatomy
Every piece of Like and Share 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
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
import { LikeButton } from "@pandaidx/widgets/client"export function ListingCardActions({ listing }: { listing: { _id: string; ListingId: string; UnparsedAddress?: string; image?: string } }) {return <LikeButton listing={listing} appearance="circle" />}
Examples
Bookmark instead of a heart
<LikeButton listing={listing} icon={<Bookmark />} likedIcon={<BookmarkCheck className="text-[color:var(--panda-primary)]" />} />
Your own save control
"use client"import { useLike } from "@pandaidx/widgets/client"export const SaveChip = ({ listing }) => {const { liked, toggle, pending } = useLike(listing)return <button type="button" aria-pressed={liked} disabled={pending} onClick={toggle}>{liked ? "Saved" : "Save"}</button>}
Your own share dialog from a listing's menu (large photo, buttons at the bottom)
"use client"import Image from "next/image"import { useState } from "react"import { ListingActionsMenu, useShare } from "@pandaidx/widgets/client"const ShareSheet = ({ url, title, image, onClose }) => {const share = useShare({ url, title, image, context: "property" })return (<dialog open className="rounded-card bg-background p-0 shadow-raised">{image ? <Image src={image} alt={title} width={640} height={288} className="h-72 w-full object-cover" /> : null}<div className="flex flex-col gap-3 p-5"><h2>{title}</h2><div className="flex flex-wrap gap-2">{share.channels.map((channel) => <button key={channel.id} type="button" onClick={channel.open}>{channel.label}</button>)}<button type="button" onClick={() => void share.copy()}>{share.copied ? "Copied" : "Copy link"}</button></div><button type="button" onClick={onClose}>Close</button></div></dialog>)}export const ListingRowMenu = ({ listing }) => {const [sharing, setSharing] = useState(null)return (<><ListingActionsMenu listing={listing} onShare={() => setSharing({ url: `${location.origin}/mls/${listing._id}`, title: listing.UnparsedAddress, image: listing.image })} />{sharing ? <ShareSheet {...sharing} onClose={() => setSharing(null)} /> : null}</>)}
API reference
LikeButton
The websites listing heart for custom listing cards: signed-out visitors get the sign-up dialog; signed-in visitors like / unlike the listing (their account, the visitor drawer and the owner's CRM activity). The state follows the visitor's likes of the site. The only favorite/save control of a site: never store favorites yourself.
Import from @pandaidx/widgets/client · renders inside PandaProvider
| Prop | Type | Default | Description |
|---|---|---|---|
| listing* | object | — | The listing (any listing object or library item with these fields). |
| appearance | "icon" | "circle" | "outline" | "icon" | icon: borderless heart; circle: white round button over a photo; outline: heart + label. |
| label | string | — | Accessible name (visible with outline). Default Save / Saved. |
| className | string | — | Classes appended to the trigger. |
| icon | node | — | Icon while not liked (default: the outline heart), e.g. a bookmark or star element. |
| likedIcon | node | — | Icon while liked (default: the filled heart in --panda-like-active, red). To recolor only, set --panda-like-active (e.g. :root { --panda-like-active: var(--panda-primary) }). |
| 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). |
ShareButton
The Panda share button + dialog (listings, buildings, areas, collections, stories): preview photo and name, Copy Link, Email, WhatsApp, Facebook, LinkedIn, Telegram and X. Each channel reports a share analytics event ({ context, channel, url }) through <PandaAnalytics>. The only share UI of a site: custom cards pass their own trigger as children; never build share links (wa.me, facebook.com/sharer, navigator.share…) or a share dialog. The dialog follows the theme (dialog radius, buttons).
Import from @pandaidx/widgets/client
| Prop | Type | Default | Description |
|---|---|---|---|
| appearance | "outline" | "icon" | "ghost" | "circle" | "outline" | outline: share icon + label (websites detail/building button); icon: outlined icon button; ghost: borderless icon (cards, dialogs); circle: the websites mobile header round button. |
| children | node | — | Your own trigger (a button); without it the built-in appearance trigger is rendered. |
| label | string | "Share" | Built-in trigger text (visible with outline, accessible name otherwise). |
| className | string | — | Classes appended to the built-in trigger button. |
| url | string | — | Link to share. Default: the current page URL. |
| title | string | "Share" | Dialog title (Share Development, Share this property…). |
| image | string | — | Preview photo shown in the dialog. |
| name | string | — | Preview name (also the link title for LinkedIn, Telegram and email). |
| context | "property" | "building" | "area" | "collection" | "saved-search" | "story" | "new-development" | "page" | "page" | What is shared; sent with the share analytics event. |
| dialogVariant | "default" | "square" | "default" | default: rounded Panda dialog; square: sharp corners for template sites. |
| 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
useLike()inside LikeButtonuseLike(listing) → { liked, toggle(), pending, signedIn } — your own heart / save control (signed out, toggle opens sign-up); never store favorites yourself
useShare()inside ShareButtonuseShare({ url?, title?, image?, context? }) → { url, title, image, channels: { id, label, href, open() }[], copy(): Promise<boolean>, copied, native, nativeShare() } — your own share dialog or sheet; every action reports the share analytics event, so never open window.open / mailto links or write navigator.clipboard yourself