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

Headless hookClient component@pandaidx/widgets 1.14.1

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

  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

LikeButton
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
<LikeButton listing={listing} icon={<Bookmark />} likedIcon={<BookmarkCheck className="text-[color:var(--panda-primary)]" />} />

Your own save control

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

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

PropTypeDefaultDescription
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.
labelstring—Accessible name (visible with outline). Default Save / Saved.
classNamestring—Classes appended to the trigger.
iconnode—Icon while not liked (default: the outline heart), e.g. a bookmark or star element.
likedIconnode—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.
themeobject—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

PropTypeDefaultDescription
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.
childrennode—Your own trigger (a button); without it the built-in appearance trigger is rendered.
labelstring"Share"Built-in trigger text (visible with outline, accessible name otherwise).
classNamestring—Classes appended to the built-in trigger button.
urlstring—Link to share. Default: the current page URL.
titlestring"Share"Dialog title (Share Development, Share this property…).
imagestring—Preview photo shown in the dialog.
namestring—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.
themeobject—Token overrides for this widget only, merged over the provider theme (same shape as PandaProvider.theme).

Hooks

useLike()inside LikeButton

useLike(listing) → { liked, toggle(), pending, signedIn } — your own heart / save control (signed out, toggle opens sign-up); never store favorites yourself

useShare()inside ShareButton

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