ServiceMessage
service-messageDisplays a service-notification card with source header, structured fields or custom content, and footer actions.
Usage
Basic usage
Avatar + source + key value field + bottom entry, replica WeChat service notification card.
<ServiceMessage
avatar={{ fallback: "Rui", className: "bg-primary/10 text-primary" }}
source="luckincoffee Luckin Coffee"
onMore={() => openMore()}
title="Product collection reminder"
fields={[
{ label: "Meal pickup number", value: "361" },
{ label: "Item quantity", value: "1" },
{ label: "Product Details", value: "Orange C Ice Tea" },
]}
action={{ icon: <MiniProgramIcon /> }}
onAction={() => openMiniProgram()}
/>Custom text
Pass children covering fields, carrying the content of non-key-value structure.
Your shipment has been sent by Me Sign for receipt, thank you for using SF Express. Looking forward to serving you again.
Tracking number SF1234567890123 · Today 14:32
<ServiceMessage
avatar={{ fallback: "Shun", className: "bg-warning/15 text-warning" }}
source="SF Express"
title="Your package has been signed for"
footer="View logistics details"
action={{ label: "Details", icon: <MiniProgramIcon /> }}
onAction={() => openTracking()}
>
<p className="text-sm leading-relaxed text-foreground">
Your shipment has been signed for by <span className="font-medium">I</span>. Thank you for using SF Express.
</p>
</ServiceMessage>No more buttons
If onMore is not passed, the head ⋯ button will be hidden; action.label can customize the action text.
<ServiceMessage
avatar={{ fallback: "OA", className: "bg-success/15 text-success" }}
source="Enterprise OA · Approval Assistant"
title="The reimbursement form has been passed"
fields={[
{ label: "Document number", value: "BX-2026-000812" },
{ label: "Reimbursement amount", value: "¥ 1,280.00" },
{ label: "Approval result", value: "Passed" },
]}
footer="Enter the Approval Center"
action={{ label: "View", icon: <MiniProgramIcon /> }}
onAction={() => openApproval()}
/>Minimalist
Title only + bottom entry (no avatar/fields).
<ServiceMessage
source="System Notification"
title="You have 1 new system message to view"
footer="View details"
onAction={() => openDetail()}
/>When to use
Use ServiceMessage inside a message or conversation feed for a service-generated template message such as pickup reminders, delivery confirmation, or approval outcomes. It combines a source header, structured body, and mini-app footer. This is a declarative static card in the content flow; use Notification or Toast for transient imperative feedback.
Import
import { ServiceMessage } from "@hulianui/ui"Props
Inherits Omit<HTMLAttributes<HTMLDivElement>, "title">.
| Name | Type | Default | Description |
|---|---|---|---|
| avatar | AvatarProps | — | Header avatar using HulianUI Avatar props such as {src, fallback}. |
| fields | ServiceMessageField[] | — | Body key/value rows in the shape {label, value}. Ignored when children are provided. |
| action | ServiceMessageAction | label "\u5c0f\u7a0b\u5e8f" | Right-side footer action in the shape {label?, icon?} plus a chevron. The built-in Chinese label means “Mini program.” |
Events
| Event | Type | Description |
|---|---|---|
| onMore | () => void | When provided, renders the header overflow button. Its built-in Chinese accessible label is "\u66f4\u591a", meaning “More.” |
| onAction | () => void | When provided, turns the entire footer row into a button. |
Slots
| Slot | Type | Description |
|---|---|---|
| source | ReactNode | Service name in the header. |
| title | ReactNode | Primary body title. |
| children | ReactNode | Custom body that replaces fields. |
| footer | ReactNode | Left footer guidance. Defaults to built-in Chinese "\u8fdb\u5165\u5c0f\u7a0b\u5e8f\u67e5\u770b", meaning “Open the mini program to view”; pass null to hide the footer. |
Example
<ServiceMessage
avatar={{ fallback: "R", className: "bg-primary/10 text-primary" }}
source="Coffee service"
onMore={() => {}}
title="Pickup reminder"
fields={[
{ label: "Pickup number", value: "361" },
{ label: "Quantity", value: "1" },
{ label: "Item", value: "Orange iced tea" },
]}
action={{ icon: <LayoutGrid className="size-3.5 text-primary" /> }}
onAction={() => {}}
/>
<ServiceMessage
avatar={{ fallback: "S", className: "bg-warning/15 text-warning" }}
source="Delivery service"
title="Package delivered"
footer="View tracking details"
action={{ label: "Details" }}
onAction={() => {}}
>
<p className="text-sm text-foreground">The package was signed for by the recipient.</p>
</ServiceMessage>Usage guidelines
childrenandfieldsare mutually exclusive; children take precedence when both are present.- Without
onMore, the overflow control is absent. WithoutonAction, the footer is not interactive. Do not pass empty functions merely to make controls appear active. footer={null}hides the entire footer, including the action on the right.- No other known caveats.
Related
Playground
<ServiceMessage
avatar={{ src: logo, fallback: "Rui" }}
source="luckincoffee Luckin Coffee"
onMore={() => openMore()}
title="Product collection reminder"
fields={[
{ label: "Meal pickup number", value: "361" },
{ label: "Item quantity", value: "1" },
{ label: "Product Details", value: "Orange C Ice Tea" },
]}
footer="Enter the mini program to view"
action={{ icon: <MiniProgramIcon />, label: "MiniProgram" }}
onAction={() => openMiniProgram()}
/>