Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Data Display

Contact card

Data Display

Contact card React component for Tailwind CSS v4

Comment ThreadCountdown
Live props
Layout
layout
Appearance
variant
size
radius
tone
color
State
defaultFavorite
selected
loading
Content
showFavorite
showFields
showTags
showActivity
Behavior
copyable
locale
Code
import { ContactCard } from "@/components/ui/contact-card"

<ContactCard />
  1. Home
  2. Components
  3. Data Display
  4. Contact card
Pro

Contact card React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React contact card: a person or company at a glance with avatar, role, status, call, email and WhatsApp buttons, tags and last activity.

Tap Call, Email or WhatsApp to open the phone, the mail app or a WhatsApp chat (wa.me with the Romanian 40 prefix added to 07… numbers); an onCall / onEmail / onMessage handler replaces the link, for a softphone or an in-app composer, and the email and phone in the full card go through the same handlers. Tap the name (or anywhere on a linked card) to open the record: the card tints on hover and dips slightly while pressed (a row only tints), but neither dips nor darkens when the press lands on a button or link inside. The company, email, phone and website are underlined links of their own. The star saves the contact optimistically and rolls back, with a spoken error, when the request fails. The copy buttons next to email, phone and CUI copy the value and confirm with a check; they show on hover or keyboard focus of their row with a mouse and are always visible on touch. The ⋯ button opens the actions menu, a bottom sheet with the contact's name on phones. On narrow cards the buttons stack their icon over the label; in the row layout they appear on hover (always on touch), rows narrower than 30rem keep only the first one (Call), below 18rem none, and the row's ⋯ menu always lists every channel on top. Tab reaches the name link, the company link, the star, the menu, each contact button, then each field link and copy button, in reading order, with the focus ring drawn around the whole card for the name; Enter or Space activates; in the menu the arrows move and Escape closes it and returns focus to ⋯. Hover a round button for its label; long-press it on touch.

Contact card is a Pro React component in the React data display components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add contact-card, together with Avatar, Badge, Button, Copy Button, Dropdown Menu and 10 more and the npm packages lucide-react and motion.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add contact-card

Contact card is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import ContactCard from @/components/ui/contact-card.

import { Pencil, PhoneCall, Trash2 } from "lucide-react"
import { ContactCard, type ContactData } from "@/components/ui/contact-card"

const emma: ContactData = {
  id: "c-emma",
  name: "Emma Carter",
  title: "Head of Procurement",
  company: { name: "Northwind Tech Inc.", href: "/clients/c01" },
  email: "emma.carter@northwindtech.com",
  phone: "+1 415 555 0132",
  whatsapp: true,
  location: "San Francisco, CA",
  status: { label: "Active client", tone: "success" },
  tags: [{ label: "Decision maker" }, { label: "2027 budget", tone: "info" }],
  owner: { name: "Anna Price", avatar: "/demo/ana-pop.jpg" },
  lastActivity: { label: "Call", at: new Date(2026, 8, 24, 15, 55), icon: <PhoneCall /> },
  href: "/contacts/emma-carter",
}

export function ContactPanel({ onFavorite }: { onFavorite: (favorite: boolean) => Promise<unknown> }) {
  return (
    <ContactCard
      contact={emma}
      defaultFavorite={false}
      onFavoriteChange={(favorite) => onFavorite(favorite)}
      actions={[
        { label: "Edit", icon: <Pencil /> },
        { type: "separator" },
        { label: "Delete contact", icon: <Trash2 />, tone: "danger" },
      ]}
      locale="en-US"
      labels={{ owner: "Account owner" }}
    />
  )
}

Props

  • contact*
    ContactData

    { id; kind?: "person" | "company"; name; avatar?; title?; company?: { name; href?; logo? }; email?; phone?; whatsapp?: string | boolean (true uses phone); website?; location?; cui?; status?: { label; tone?; icon? }; tags?: { label; tone?; color? }[]; owner?: { name; avatar? }; lastActivity?: { label; at; icon? }; fields?: ContactFact[]; href? }. Anything missing simply does not render.

  • layoutdefault "full"
    "full" | "compact" | "row"

    full: every part, for a record's side panel. compact: centered avatar, name, role, status and round buttons, for grids. row: one 56px line with icon buttons, for lists.

  • variantdefault "elevated"
    "elevated" | "outline" | "soft" | "ghost"

    The card surface: a raised panel, a hairline frame, the flat gray tile or nothing (ghost, for rows inside a list group).

  • sizedefault "md"
    "sm" | "md"

    md: a 48px avatar and a 17px name in the full layout (64px in compact); sm one step smaller.

  • radiusdefault "md"
    "none" | "sm" | "md" | "lg"

    Card corners: none, 12px, 16px or 24px. Rows inside a list group use none.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    Colors the selected ring and the star when it is on.

  • colordefault ""
    string

    Any CSS color for the selected ring and the star. Replaces the tone.

  • defaultFavoritedefault false
    boolean

    The star's starting state when uncontrolled. Setting it (or favorite, onFavoriteChange) shows the star.

  • favorite
    boolean

    Controlled star.

  • onFavoriteChange
    ((favorite: boolean, contact: ContactData) => void | Promise<unknown>)

    Called when the star is toggled. Return a promise to keep it optimistic while pending; a rejection rolls it back and announces labels.favoriteError.

  • showFavoritedefault false
    boolean

    Shows the star even without favorite props (an uncontrolled star that starts off).

  • selecteddefault false
    boolean

    A 2px ring in the tone and data-selected, for the picked contact in a grid (drawn inside the edge on rows, so a list group never clips it).

  • loadingdefault false
    boolean

    Skeletons in the layout's shape (aria-busy); the content fades in when it turns false.

  • showFieldsdefault true
    boolean

    Full layout: the rows for email, phone, website, location and CUI, then contact.fields.

  • showTagsdefault true
    boolean

    Full layout: the tag row.

  • showActivitydefault true
    boolean

    Full layout: the footer with the owner and the last activity (a <time>, relative to now).

  • copyabledefault true
    boolean

    Copy buttons on email, phone, CUI and facts with copyValue: on hover or focus with a mouse, always on touch.

  • channelsdefault "["call", "email", "whatsapp"]"
    ("call" | "email" | "whatsapp" | "sms" | "website")[]

    Which contact buttons show, in order. A channel without data (no phone, no email) is hidden; with none left the row disappears.

  • onCall
    (contact: ContactData) => void

    Replaces the tel: links of the Call button and the phone field (a softphone).

  • onEmail
    (contact: ContactData) => void

    Replaces the mailto: links of the Email button and the email field (an in-app composer).

  • onMessage
    (contact: ContactData) => void

    Replaces the wa.me and sms: links.

  • onChannelSelect
    (channel: ContactChannel, contact: ContactData, event: ContactChannelEvent) => void

    Runs first for every contact button, every channel picked in the row menu and the email / phone / website links of the fields. event.preventDefault() stops the link and the handler (tracking, a confirmation).

  • actions
    MenuEntry[]

    The ⋯ menu (Edit, Add note, Merge, Delete with tone "danger"), a bottom sheet on phones. Rows put the channels on top.

  • localedefault ""
    string

    Formats the last activity time ("45 min. ago"). Empty uses the UIProvider locale, then the browser's.

  • now
    Date | number

    The reference time for the relative activity time. Defaults to a live clock that ticks every minute.

  • linkComponent
    (props: LinkComponentProps) => ReactNode

    Renders the name, company and fact links through your router's Link. Defaults to the LinkProvider's, else <a>.

  • labelsdefault "DEFAULT_CONTACT_CARD_LABELS"
    Partial<ContactCardLabels>

    call, email, whatsapp, sms, website, phone, location, copy, copied, favorite, favoriteAdd, favoriteRemove, favoriteError, actions, cancel, owner, lastActivity, cui, noActivity, tags (the tag list's name), actionLabel(action, name) ("Call Emma Carter"), copyField(field) ("Copy Email").

  • classNames
    Partial<Record<"media" | "heading" | "name" | "title" | "company" | "status" | "favorite" | "menu" | "actions" | "action" | "fields" | "field" | "tags" | "footer" | "owner" | "activity", string>>

    Extra classes for the inner parts.

  • className
    string

    Extra classes for the <article>, merged last.

Parts

  • ContactActions

    component

    The contact buttons alone, for other layouts: contact, channels, layout ("tiles" labelled buttons in equal columns, "icons" round buttons with tooltips), variant (soft), tone, radius, onCall / onEmail / onMessage / onChannelSelect, labels. Props: ContactActionsProps.

  • whatsappHref

    function

    https://wa.me/40745123456 from "0745 123 456"; international numbers are kept as given, text becomes ?text=. Signature: (phone: string, text?: string) => string.

  • DEFAULT_CONTACT_CARD_LABELS

    constant

    The English strings. Spread it and override a few keys, or pass a full translated set through labels. Type: ContactCardLabels.

  • telHref

    function

    tel:0745123456 from "0745 123 456", tel:+40745123456 from "+40 745 123 456": spaces and punctuation dropped, a leading + kept. Signature: (phone: string) => string.

Examples

Company

A company card: logo initials in a rounded square, a tax ID (EIN) with copy, website and facts like the balance.

Compact grid

The client contacts in a 2-column phone / 4-column desktop grid, with one selected.

Rows in a list

One 56px line per contact with favorites; buttons appear on hover and stay visible on touch.

Custom channels

Email and Call only, with onCall opening an in-app softphone instead of a tel: link.

Loading

Skeletons in the shape of each layout; the content fades in when it arrives.

Tone and color

Brand tone and a custom color paint the selected ring and the star.

British locale, scheduled call

An en-GB locale; a future activity reads as “in 2 hr”.

Dependencies

lucide-reactmotionavatarbadgebuttoncopy-buttondropdown-menufavorite-buttonskeletontagtooltipuse-localemotionnavigationrelative-timetonesui-config

Files

  • components/ui/contact-card.tsx

FAQ

Is the React Contact card component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Contact card?

Run npx wingo-ui@latest add contact-card. It also adds the Wingo UI items it builds on (Avatar, Badge, Button, Copy Button, Dropdown Menu and 10 more) and the npm packages lucide-react and motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/contact-card.

How does Contact card behave on phones?

The ⋯ button opens the actions menu, a bottom sheet with the contact's name on phones.

See also

Entity HeaderAvatarHover CardFavorite buttonListCard

Used in

CRM Contact PageCRM Pipeline Page

More React data display components

Description ListDiff ViewerFile ListFolderGanttJSON Viewer
All 23 React data display components
PreviousComment ThreadNext Countdown
EC

Emma Carter

Head of Procurement

Northwind Tech Inc.

Active client
CallEmailWhatsApp
  • Email: emma.carter@northwindtech.com
  • Phone: +1 415 555 0132
  • Location: San Francisco, CA
  • Decision maker
  • 2027 budget
Owner: Anna PriceLast activity: Call·45 min. ago

Tap a button: links are caught here

Links are intercepted here; the star saves with a fake request