Defines badge types for dashboard tab indicators.
Badges provide visual feedback on tabs to show counts, status, or draw attention. They can be static or update live via PubSub subscriptions.
Badge Types
:count- Numeric count badge (e.g., "5", "99+"):dot- Simple colored dot indicator:status- Status indicator with color (green/yellow/red):new- "New" text indicator:text- Custom text badge
Static Badge
%Badge{type: :count, value: 5}
%Badge{type: :dot, color: :success}
%Badge{type: :status, value: :online, color: :success}Live Badge with PubSub
%Badge{
type: :count,
subscribe: {"farm:stats", fn msg -> msg.printing_count end}
}Context-Aware Badge
For badges that show different values per user/organization/context, use context_key
and optionally loader. The badge value is stored per-context in socket assigns
instead of globally.
# Badge depends on current organization context
%Badge{
type: :count,
context_key: :organization,
loader: {MyApp.Alerts, :count_for_org}, # Called with (context)
subscribe: "org:{id}:alerts" # {id} replaced with context.id
}Context Placeholders
Subscribe topics support {field} placeholders that are resolved from the current context:
{id}- context.id or context[:id]{name}- context.name or context[:name]- Any field accessible on the context struct/map
Loader Function
The loader is called when the LiveView mounts to get the initial badge value:
{Module, :function}- Called asModule.function(context)fn context -> value end- Anonymous function
Badge with Attention Animation
%Badge{
type: :count,
value: 3,
color: :error,
pulse: true
}
Summary
Functions
Returns the CSS color class for the badge.
Creates a compound badge with multiple colored segments.
Creates a context-aware compound badge that loads segments per-context.
Creates a context-aware badge that loads values per-context.
Checks if this badge is context-aware (requires per-context value storage).
Creates a count badge.
Formats the badge value for display.
Creates a dot badge.
Returns the CSS background color class for dot badges.
Extracts value from a PubSub message using the badge's subscription config.
Gets the resolved topic for this badge given a context.
Gets the PubSub topic for this badge, if it has a subscription.
Creates a live badge that subscribes to PubSub updates.
Checks if this badge has a live subscription.
Loads the initial badge value using the loader function.
Creates a new Badge struct from a map or keyword list.
Creates a new Badge struct, raising on error.
Creates a "New" badge.
Resolves placeholders in a topic string using context data.
Creates a status badge.
Creates a text badge with custom text.
Updates segments for a compound badge.
Updates the badge value.
Checks if the badge should be visible.
Returns visible segments for a compound badge.
Types
@type badge_color() ::
:primary
| :secondary
| :accent
| :info
| :success
| :warning
| :error
| :neutral
@type badge_type() :: :count | :dot | :status | :new | :text | :compound
@type compound_segment() :: %{ :value => integer() | String.t(), :color => badge_color(), optional(:label) => String.t() }
@type compound_style() :: :text | :blocks | :dots
@type t() :: %PhoenixKit.Dashboard.Badge{ animate: boolean(), color: badge_color(), compound_style: compound_style(), context_key: atom() | nil, format: (any() -> String.t()) | nil, hidden_when_zero: boolean(), hide_zero_segments: boolean(), loader: loader_config() | nil, max: integer() | nil, metadata: map(), pulse: boolean(), segments: [compound_segment()], separator: String.t(), subscribe: subscribe_config() | nil, type: badge_type(), value: any() }
Functions
Returns the CSS color class for the badge.
@spec compound( [compound_segment()], keyword() ) :: t()
Creates a compound badge with multiple colored segments.
A compound badge displays multiple values with different colors in a single badge. Useful for showing status breakdowns like "10 success / 5 pending / 2 error".
Styles
:text(default) - Colored text values with separator (e.g., "10 / 5 / 2"):blocks- Colored background pills side by side:dots- Colored dots with numbers beside them
Options
:style- Display style::text,:blocks,:dots(default::text):separator- Separator string for:textstyle (default: "/"):hide_zero_segments- Hide segments with value 0 (default: false):pulse- Enable pulse animation (default: false)
Segment Format
Each segment is a map with:
:value(required) - Integer or string value to display:color(required) - Badge color atom (:success, :warning, :error, etc.):label(optional) - Text label shown after value (e.g., "done", "pending")
Examples
# Simple compound badge
Badge.compound([
%{value: 10, color: :success},
%{value: 5, color: :warning},
%{value: 2, color: :error}
])
# With labels and blocks style
Badge.compound([
%{value: 10, color: :success, label: "done"},
%{value: 5, color: :warning, label: "pending"}
], style: :blocks)
# Hide zero segments
Badge.compound([
%{value: 10, color: :success},
%{value: 0, color: :error}
], hide_zero_segments: true)
# Only shows: "10"
@spec compound_context(atom(), loader_config(), keyword()) :: t()
Creates a context-aware compound badge that loads segments per-context.
The loader function should return a list of segment maps.
Examples
# Loader returns list of segments for current organization
Badge.compound_context(:organization, {MyApp.Tasks, :get_status_counts},
style: :blocks
)
# In MyApp.Tasks
def get_status_counts(org) do
[
%{value: count_completed(org.id), color: :success},
%{value: count_pending(org.id), color: :warning},
%{value: count_overdue(org.id), color: :error}
]
end
@spec context(atom(), loader_config(), keyword()) :: t()
Creates a context-aware badge that loads values per-context.
Examples
# Badge that shows alert count for current organization
Badge.context(:organization, {MyApp.Alerts, :count_for_org}, color: :error)
# With live updates via context-specific PubSub topic
Badge.context(:farm, {MyApp.Farms, :printing_count},
subscribe: "farm:{id}:stats",
color: :info
)
Checks if this badge is context-aware (requires per-context value storage).
Creates a count badge.
Examples
Badge.count(5)
Badge.count(10, color: :error, max: 99)
Formats the badge value for display.
Examples
iex> badge = Badge.count(5)
iex> Badge.display_value(badge)
"5"
iex> badge = Badge.count(150, max: 99)
iex> Badge.display_value(badge)
"99+"
Creates a dot badge.
Examples
Badge.dot()
Badge.dot(color: :success)
Badge.dot(color: :error, pulse: true)
Returns the CSS background color class for dot badges.
Extracts value from a PubSub message using the badge's subscription config.
Gets the resolved topic for this badge given a context.
For context-aware badges, resolves placeholders. For regular badges, returns the topic as-is.
Gets the PubSub topic for this badge, if it has a subscription.
Creates a live badge that subscribes to PubSub updates.
Examples
Badge.live("user:notifications", :count)
Badge.live("farm:stats", fn msg -> msg.printing_count end, color: :info)
Checks if this badge has a live subscription.
Loads the initial badge value using the loader function.
Examples
iex> badge = Badge.context(:org, {MyApp.Alerts, :count_for_org})
iex> Badge.load_value(badge, %{id: 123})
5 # Result from MyApp.Alerts.count_for_org(%{id: 123})
Creates a new Badge struct from a map or keyword list.
Options
:type- Badge type: :count, :dot, :status, :new, :text (default: :count):value- The value to display (number for count, atom/string for status/text):color- Badge color: :primary, :secondary, :accent, :info, :success, :warning, :error, :neutral (default: :primary):max- Maximum display value for count badges (e.g., 99 shows "99+") (optional):pulse- Enable pulse animation (default: false):animate- Enable value change animation (default: true):hidden_when_zero- Hide count badge when value is 0 (default: true):subscribe- PubSub subscription config for live updates (optional):format- Custom formatter function for the value (optional):metadata- Custom metadata map (default: %{}):context_key- Context selector key for per-context badges (optional, e.g., :organization):loader- Function to load initial value for context:{Module, :function}orfn context -> value end
Subscribe Configuration
The :subscribe option can be:
A tuple of {topic, extractor_function}:
{"farm:stats", fn msg -> msg.printing_count end}A tuple of {topic, key_atom} to extract from message:
{"farm:stats", :printing_count}Just a topic string (uses full message as value):
"user:notifications:count"
Topics support {field} placeholders for context-aware badges:
"org:{id}:alerts" - resolves to "org:123:alerts" when context.id is 123
Examples
iex> Badge.new(type: :count, value: 5)
{:ok, %Badge{type: :count, value: 5}}
iex> Badge.new(type: :dot, color: :error, pulse: true)
{:ok, %Badge{type: :dot, color: :error, pulse: true}}
iex> Badge.new(type: :count, subscribe: {"orders:count", :count})
{:ok, %Badge{type: :count, subscribe: {"orders:count", :count}}}
# Context-aware badge
iex> Badge.new(type: :count, context_key: :organization, loader: {MyApp.Alerts, :count_for_org})
{:ok, %Badge{type: :count, context_key: :organization, loader: {MyApp.Alerts, :count_for_org}}}
Creates a new Badge struct, raising on error.
Creates a "New" badge.
Examples
Badge.new_indicator()
Badge.new_indicator(color: :accent)
Resolves placeholders in a topic string using context data.
Supports {field} syntax where field is accessed from the context.
Examples
iex> Badge.resolve_topic("org:{id}:alerts", %{id: 123})
"org:123:alerts"
iex> Badge.resolve_topic("farm:{farm_id}:stats", %{farm_id: "abc"})
"farm:abc:stats"
Creates a status badge.
Examples
Badge.status(:online, color: :success)
Badge.status(:busy, color: :warning)
Badge.status(:offline, color: :error)
Creates a text badge with custom text.
Examples
Badge.text("Beta")
Badge.text("Pro", color: :accent)
@spec update_segments(t(), [compound_segment()]) :: t()
Updates segments for a compound badge.
Updates the badge value.
Checks if the badge should be visible.
Count badges with value 0 are hidden by default when hidden_when_zero is true. Compound badges are hidden when all segments have zero value (and hide_zero_segments is true).
@spec visible_segments(t()) :: [compound_segment()]
Returns visible segments for a compound badge.
If hide_zero_segments is true, filters out segments with value 0 or nil.
Examples
badge = Badge.compound([
%{value: 10, color: :success},
%{value: 0, color: :error}
], hide_zero_segments: true)
Badge.visible_segments(badge)
# => [%{value: 10, color: :success}]