Share Dialog

Modal dialog for sharing a page across social platforms with a copy-link action and overridable labels. Install Share Dialog from the VLLNT UI registry with the shadcn CLI.

Report a bug

Modal dialog for sharing a page across social platforms with a copy-link action and overridable labels. Part of the overlay family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

Add Share Dialog to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/share-dialog.json

Source

"use client"; import * as React from "react"; import { cn } from "../../lib/utils"; import { useCopyToClipboard } from "../copy-button/copy-button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "../dialog/dialog"; export type SharePlatform = { buildUrl: (pageUrl: string, pageTitle: string) => string; key: string; label: string; }; export type ShareDialogLabels = { copied: string; copyLink: string; }; export type ShareDialogProps = { /** Function to build the URL for copying (should add UTM params) */ buildCopyUrl?: (url: string) => string; description?: string; labels?: ShareDialogLabels; onCopy?: () => void; onOpenChange?: (open: boolean) => void; onShare?: (platformKey: string) => void; open?: boolean; platforms: SharePlatform[]; title?: string; }; function PlatformButton({ onClick, platform, }: { onClick: (key: string) => void; platform: SharePlatform; }) { const handleSelectPlatform = () => { onClick(platform.key); }; return ( <button className={cn( "w-full rounded-md border border-border bg-background px-4 py-3", "text-sm font-medium text-foreground", "transition-colors duration-150", "hover:bg-accent hover:text-accent-foreground", "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", )} onClick={handleSelectPlatform} type="button" > {platform.label} </button> ); } function CopyButton({ buildCopyUrl, labels, onCopy, }: { buildCopyUrl?: (url: string) => string; labels: ShareDialogLabels; onCopy?: () => void; }) { const { copied, copy } = useCopyToClipboard(); const handleCopy = React.useCallback(async () => { const url = buildCopyUrl ? buildCopyUrl(window.location.href) : window.location.href; const text = `${document.title} - ${url}`; if (await copy(text)) { onCopy?.(); } }, [buildCopyUrl, copy, onCopy]); return ( <button className={cn( "col-span-2 w-full rounded-md border px-4 py-3", "text-sm font-medium", "transition-colors duration-150", "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", copied ? "border-green-500 bg-green-500/10 text-green-600 dark:text-green-400" : "border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground", )} onClick={handleCopy} type="button" > {copied ? labels.copied : labels.copyLink} </button> ); } const defaultLabels: ShareDialogLabels = { copied: "Copied!", copyLink: "Copy link", }; export function ShareDialog({ buildCopyUrl, description, labels = defaultLabels, onCopy, onOpenChange, onShare, open, platforms, title = "Share", }: ShareDialogProps) { const handlePlatformClick = React.useCallback( (key: string) => { const platform = platforms.find((p) => p.key === key); if (platform) { const url = platform.buildUrl(window.location.href, document.title); window.open(url, "_blank", "noopener,noreferrer"); onShare?.(key); onOpenChange?.(false); } }, [platforms, onShare, onOpenChange], ); return ( <Dialog onOpenChange={onOpenChange} open={open}> <DialogContent className="sm:max-w-sm"> <DialogHeader> <DialogTitle>{title}</DialogTitle> {description ? ( <DialogDescription>{description}</DialogDescription> ) : null} </DialogHeader> <div className="grid grid-cols-2 gap-2"> {platforms.map((platform) => ( <PlatformButton key={platform.key} onClick={handlePlatformClick} platform={platform} /> ))} <CopyButton buildCopyUrl={buildCopyUrl} labels={labels} onCopy={onCopy} /> </div> </DialogContent> </Dialog> ); }

Stories

Explore every variant and state in the interactive Storybook:

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Dependencies

  • @vllnt/ui@^0.3.0