Badge
Small pill label for status, metadata, categories, and other compact read-only qualifiers across the dashboard.
Overview
Badge is one of the most common display primitives in the brand dashboard.
It is the default pill-style label for:
- entity and workflow status
- metadata qualifiers such as roles, categories, and types
- compact icon-plus-label pills in tables, cards, and panels
Badge is read-only. It has no onClick and no onRemove, deliberately — if a person can click or dismiss the pill, you want Chip, which renders a real button and is keyboard-operable. Removing the handlers from Badge is what stops the inaccessible version from being written again.
It also accepts only the semantic colour enum, so a raw hex cannot leak into an ordinary label. If the colour is user data, you want StatusBadge or Tag.
The full decision — Badge vs Chip vs StatusBadge vs Tag — is in Badges, Chips and Tags.
The primitive itself is intentionally small:
- one required
label - optional
color - optional
Icon,imageSrc, ordot - optional
suffix
Most of the meaning comes from the calling feature mapping domain values to badge labels and colors.
Basic Usage
import { Badge } from '@fsai/shared-ui';
<Badge label="Active" color="green" />
<Badge label="Pending Review" color="orange" />
<Badge label="Draft" color="gray" />With Icon Or Image
Use Icon for compact semantic reinforcement:
<Badge label="Registered" color="green" Icon="CircleCheck" />
<Badge label="Operations" color="blue" Icon="Suitcase" />Use imageSrc when the badge should show a tiny image or avatar-like thumbnail. imageSrc takes precedence over Icon.
<Badge
label="Jane Applicant"
imageSrc={leadAvatar}
color="white"
/>Sizes
Three steps. Default is md.
<Badge label="Small" size="sm" />
<Badge label="Medium" size="md" />
<Badge label="Large" size="lg" />A badge's height derives from the type token plus padding and border — it is not pinned to the control scale, and you must not pin it yourself. That is what lets the same badge sit correctly on both surfaces: the franchisee portal runs larger type than the brand dashboard, and a badge frozen at a brand-dashboard pixel would crowd its own label there. See Control Heights.
Dot Marker
Use dot for a leading colour dot instead of an icon. A badge carries at most one leading marker, so dot is ignored when Icon or imageSrc is set.
<Badge label="Unqualified" dot />dotColor is a narrow escape hatch for a colour that is user data rather than a design decision. It tints only the dot; the badge's surface and text stay on color. In most cases you want StatusBadge, which wraps this pattern properly.
Colors
Badge does not have semantic variants like success or warning. Instead, product code maps business meaning to one of the supported badge colors:
| Color | Common usage |
|---|---|
gray | neutral labels, inactive states, metadata with low emphasis |
blue | informational states, categories, sections, typed labels |
green | success, healthy, active, completed |
orange | warning, pending, needs attention |
red | failure, blocked, expired, destructive state |
dark-red | stronger negative emphasis when the red palette needs more contrast |
purple | specialized categorization where purple is already part of the domain language |
black | very high-contrast pills on light surfaces |
white | neutral pills on colored or busy surfaces, or image-driven badges |
Keep these mappings close to the feature or domain config instead of scattering ad hoc inline choices everywhere.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | ReactNode | — | Required badge content. |
color | 'blue' | 'gray' | 'orange' | 'red' | 'green' | 'dark-red' | 'black' | 'purple' | 'white' | gray | Visual color treatment. |
Icon | IconName | ComponentType<SVGAttributes<...>> | — | Optional leading icon. Ignored when imageSrc is present. |
imageSrc | string | null | — | Optional leading image thumbnail. |
className | string | — | Additional classes for spacing, hover treatment, or local layout tweaks. |
dot | boolean | false | Renders a leading colour dot. Ignored when Icon or imageSrc is set. |
dotColor | string | — | Tints only the dot, for a colour that is user data. Surface and text stay on color. |
size | 'sm' | 'md' | 'lg' | 'md' | Size step. Height derives from it. |
shape | 'rounded' | 'pill' | 'rounded' | Corner treatment. |
variant | 'solid' | 'ghost' | 'solid' | ghost drops the border and background. |
suffix | ReactNode | — | Trailing content, rendered after the label. |
style | CSSProperties | — | Inline styles on the root. |
Badge has no onClick, onRemove or removeIcon. Use Chip for an interactive pill.
Common Usage Patterns
Status Badge
The most common pattern is a small mapping helper:
const PROJECT_STATUS_CONFIG = {
active: { label: 'Active', color: 'green' },
paused: { label: 'Paused', color: 'orange' },
archived: { label: 'Archived', color: 'gray' },
} as const;
<Badge
label={PROJECT_STATUS_CONFIG[status].label}
color={PROJECT_STATUS_CONFIG[status].color}
/>This keeps label and color decisions consistent across tables, panels, and cards.
Metadata Pill
For roles, types, categories, and other qualifiers, badges are often neutral or lightly emphasized:
<Badge label="Operations" color="blue" Icon="Suitcase" />
<Badge label="Pending Invite" color="orange" />Clickable Pill — use Chip
A pill a person can click is a Chip, not a Badge:
// ✗ Badge has no onClick — a clickable span is not a control
<Badge label={lead.name} onClick={() => openLeadPanel(lead.id)} />
// ✓ real button, focusable, fires on Enter and Space
<Chip
label={lead.name}
imageSrc={lead.imageSrc}
color="white"
onClick={() => openLeadPanel(lead.id)}
/>Related-entity navigation in dense surfaces such as panel headers is the common case: a chip gives a compact way to jump to the linked entity without adding a larger row or card.
The exception is a pill inside a row that is already a control. A picker row is itself a button, so a Chip in there would nest a button inside a button — keep a Badge.
Brand Dashboard Usage Patterns
Representative usages:
| Pattern | Path | Notes |
|---|---|---|
| Table status chip | fsai/apps/brand-dashboard/src/pages/Projects/components/ProjectsTable.tsx | Canonical domain-config-driven status badge in a DataTable column. |
| State and compliance status | fsai/apps/brand-dashboard/src/pages/States/States.components.tsx | Strong example of mapping business states to badge color and icon. |
| Workflow status | fsai/apps/brand-dashboard/src/pages/Workflows/WorkflowRunPanel.tsx | Uses a badge in a panel header to show operational state. |
| Team and invite metadata | fsai/apps/brand-dashboard/src/pages/OrganizationSettings/OrganizationSettingsTeam/OrganizationSettingsTeam.tsx | Uses badges for roles and invite lifecycle labels. |
| Finder and analytics results | fsai/apps/brand-dashboard/src/pages/Marketing/Finder/FinderResultsTable.tsx | Shows status and quality badges inside dense result tables. |
| Event type badges | fsai/apps/brand-dashboard/src/pages/EmailAnalytics/EventsList.tsx | Small categorical badges in analytics/event streams. |
| Clickable lead pill | fsai/apps/brand-dashboard/src/pages/Deals/Deals.tsx | Uses Chip with imageSrc and color="white" for a secondary navigation affordance. |
| Section metadata | fsai/apps/brand-dashboard/src/pages/Projects/ProjectDetail/PhaseView/PhaseView.primitives.tsx | Uses icon-plus-label badge as compact structural metadata. |
Important Conventions
Badgeis a generic display primitive. It does not know your domain statuses by itself.- Prefer a small config object or helper to map domain values to badge
label,color, and optionalIcon. imageSrcoverridesIcon.- The root element is a
span, and it is not interactive. If the pill becomes a meaningful action, switch toChip— do not add a click handler to a wrapper. - Long labels truncate inside the pill, so keep labels short and stable.
- Some features use specialized badge-like components such as
VendorTypeBadgeor other local wrappers. Use the primitiveBadgewhen you need the generic platform pill rather than a domain-specific abstraction. - For a user-created status, use
StatusBadge(imported directly from@fsai/shared-ui). The oldInlineEntityManager.Badgealias has been removed — a status pill is not a part of a picker. - For cross-linking to related entities, a
Chipis best when the relationship is secondary metadata. Use a richer row or card when the relationship is more important than a compact pill can comfortably express.
Guidelines
- Do use
Badgefor short status and metadata labels in lists, tables, cards, and panels - Do centralize color-and-label mappings for business enums
- Do keep labels short enough to survive narrow columns
- Do use an icon when it adds meaning, not just decoration
- Don't treat
Badgeas an action control — reach forChip - Don't invent ad hoc pill styles when the standard badge already fits
- Don't pass a raw hex for the surface;
Badgetakes the semantic enum only - Don't pin a height on a badge — it must respond to the type scale