Badge
A small pill for counts, statuses, and short labels, optionally clickable.
Badge is a compact, themed label built on Roblox's native ImageLabel. It shows a count, status, or short piece of text, optionally preceded by an icon, and sizes itself to its content. It is non-interactive unless you give it onClick.
Import
import { Badge } from "@rbxts/react-clean-ui";Basic usage
The text prop is required and renders the label.
<Badge text="New" />
<Badge text="12" intent="danger" />Intents
Set intent to control the badge's background, border, and text/icon colors. When intent is omitted, primary is used.
<Badge text="Primary" intent="primary" />
<Badge text="Secondary" intent="secondary" />
<Badge text="Success" intent="success" />
<Badge text="Info" intent="info" />
<Badge text="Warning" intent="warning" />
<Badge text="Danger" intent="danger" />In the default theme, primary is a neutral light-grey pill with dark text, while the other intents use solid intent-colored backgrounds with white text.
Icons
Set icon to draw an icon before the text. By default the icon is tinted with the same color as the text; set iconColor to tint it separately (see Custom colors).
The gap between the icon and the text is half of the resolved spacing at the badge's scale, rounded up, the same gap HStack uses by default.
Scaling
Badge supports the shared scale prop, which moves the text size, icon size, padding, and icon/text gap together.
Available scales are xs, sm, md, lg, and xl. When scale is not provided, the component uses theme.default.scale.
The Wooden theme uses the same text style (PatrickHand, Size18, regular) and the same "2px 10px" padding at every scale, so Wooden badges look the same size whatever scale you pass. Only the icon size and the icon/text gap still follow scale.
Placing a badge
Badge sizes to its content and never stretches to fill its parent, so it sits naturally next to other content in a stack. Use LayoutOrder to order it inside a layout.
Outside a layout, set Position and AnchorPoint to place the badge yourself.
<Badge
text="Live"
intent="danger"
scale="xs"
Position={new UDim2(1, -4, 0, 4)}
AnchorPoint={new Vector2(1, 0)}
/>Custom colors
Each color can be overridden on a single badge. Anything you don't set falls back to the intent's theme value.
BackgroundColor3andBackgroundTransparencyset the fill.textColorsets the text color.iconColorsets the icon color. When unset, the icon uses the resolved text color, sotextColoralone tints both.borderColor,borderThickness,borderTransparency, andborderPosition("inner"or"outer", default"inner") shape the border.borderThicknessfalls back to the theme'sbadge.borderThickness, and the border only renders when the resolved thickness is greater than0.paddingoverrides the theme padding at the badge's scale, per side, in CSS shorthand.
The first row overrides colors, border, and padding per instance. The second row has clickable badges, described next: "Likes" uses the theme's hover/press shading, and "Follow" sets hoverStyle/pressStyle.
Clickable badges
Pass onClick to make the badge clickable. The whole pill is the click target.
import React, { useState } from "@rbxts/react";
import { Badge } from "@rbxts/react-clean-ui";
export function LikeBadge() {
const [likes, setLikes] = useState(0);
return (
<Badge
text={`Likes ${likes}`}
icon="thumbs-up"
intent="info"
onClick={() => setLikes(likes + 1)}
/>
);
}The badge is hovered while the pointer is over it or it holds gamepad selection, and pressed while the left mouse button or a touch is held down on it. By default the background is shaded in these states using the theme's badge.hoverDarken and badge.pressDarken (see Theme values). There is no disabled state.
Use hoverStyle and pressStyle to pick the colors yourself. Both take a BadgeStateStyle, with optional backgroundColor, backgroundTransparency, textColor, iconColor, and borderColor.
<Badge
text="Follow"
intent="primary"
hoverStyle={{ backgroundColor: Color3.fromHex("#2E9D63"), textColor: new Color3(1, 1, 1) }}
pressStyle={{ backgroundColor: Color3.fromHex("#1F6E45") }}
onClick={() => print("Followed")}
/>While pressed, pressStyle is layered over hoverStyle, so a field you only set in hoverStyle (like textColor above) still applies while pressed. Each color resolves, highest priority first:
- Hovered:
hoverStyle, then the instance prop (for exampletextColor), then the intent value. - Pressed:
pressStyle, thenhoverStyle, then the instance prop, then the intent value.
When neither style sets backgroundColor, the resting background is shaded by the theme instead. hoverStyle and pressStyle are ignored unless onClick is set.
Theming
theme.components.badge sets the look of every badge. Use createTheme (or extendTheme) to change it theme-wide, then apply it with ThemeProvider.
import { Badge, createTheme, ThemeProvider } from "@rbxts/react-clean-ui";
const themeWithSquareBadges = createTheme({
components: {
badge: {
cornerRadius: "4px",
borderThickness: 0,
padding: "2px 8px",
intents: {
danger: {
default: {
textColor: Color3.fromHex("#FFFFFF"),
backgroundColor: Color3.fromHex("#B91C1C"),
borderColor: Color3.fromHex("#B91C1C"),
},
},
},
},
},
});
<ThemeProvider theme={themeWithSquareBadges}>
<Badge text="Offline" intent="danger" />
</ThemeProvider>The default theme's badge.typography is keyed by scale (xs–xl), and themes are deep-merged. If you override it with a plain object such as { size: Enum.FontSize.Size14 }, the scale keys survive the merge and your plain fields are ignored. Override typography per scale key instead, as the Wooden theme does.
Set backgroundColor, borderColor, and textColor for every intent you customize. The component's primary entry is layered over each intent's theme colors, so any field left unset on an intent can inherit primary's neutral pill colors.
Props
Badge-specific props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | Required | The label shown in the badge. Never wraps. |
icon | IconName | undefined | Renders an icon before the text, tinted with iconColor or the text color. |
textColor | Color3 | undefined | Text color. Unset uses the intent's text color. |
iconColor | Color3 | undefined | Icon color. Unset uses the resolved text color. |
padding | CssPadding | undefined | Per-side padding override over the theme padding at the badge's scale. |
onClick | () => void | undefined | Makes the badge clickable and calls this when it is activated. |
hoverStyle | BadgeStateStyle | undefined | Colors used while hovered. Only used with onClick. |
pressStyle | BadgeStateStyle | undefined | Colors used while pressed, layered over hoverStyle. Only used with onClick. |
name | string | "Badge" | Name assigned to the underlying ImageLabel. |
LayoutOrder | number | undefined | Native Roblox layout order. |
Position | UDim2 | undefined | Native Roblox position. |
AnchorPoint | Vector2 | undefined | Native Roblox anchor point. |
BadgeStateStyle is exported. Its fields are all optional: backgroundColor, textColor, iconColor, and borderColor (Color3), and backgroundTransparency (number).
Shared props
The badge also supports props inherited from the following interfaces.
| Interface | Purpose |
|---|---|
IntentElementProps | Configures the badge's color scheme. Defaults to primary. |
ScalableElementProps | Configures text size, icon size, padding, and icon/text gap together. |
IconElementProps | Configures the icon prop's accepted values. |
BackgroundElementProps | Configures BackgroundColor3 and BackgroundTransparency, overriding the intent's background. Transparency falls back to the intent's value, then 0. |
BorderElementProps | Configures borderColor, borderThickness, borderTransparency, and borderPosition ("inner" by default). |
Behaviour
- The badge sizes to its content (
AutomaticSize.XYwith a zero base size) and never stretches to its parent's width. - Without
onClick, the badge never takes input: itsImageLabelhasActiveandSelectableset tofalse, and no events are wired. - With
onClick, the rootImageLabelkeeps the background, corners, border, and gradient, and its content moves into a transparent, content-sizedImageButtonchild named"BadgeButton". The root sizes to that button, so the whole pill is clickable. - Hover and press colors never use the theme's button hover colors. They come only from
hoverStyle/pressStyleand the badge's ownhoverDarken/pressDarkenshading. Intent colors always resolve from the intent'sdefaultstate. - Gamepad selection shows the hover state, but a gamepad button press does not show the pressed visual. Only a held mouse button or touch does. Activating with a gamepad still calls
onClick. - There is no disabled state.
- Removing
onClickclears the hover and press state, so adding it back doesn't show a stale state. - Content is laid out left to right and vertically centered, without wrapping: the optional icon first, then the text. Text wrapping is disabled.
- The border stroke only renders when the resolved
borderThickness(prop, then theme) is greater than0. backgroundImageandbackgroundGradientalways come from the theme intent; there are no instance props for them.- When the resolved intent has no
backgroundImage, the image is cleared (Image=""), so a flat badge never shows a stale image. - The ref forwards to the root
ImageLabel, whether or not the badge is clickable.
Theme values
The badge uses values from theme.components.badge for:
cornerRadius— the corner radius ("50%"for a pill in the default and dark themes,"4px"in Sandstone and Wooden)borderThickness— the inner border thickness;0renders no borderspacing/padding(optional) — internal padding, resolved per badgescale(nottheme.default.spacing). The default theme uses per-scale padding from"1px 4px"(xs) to"6px 14px"(xl)typography(optional) — a plain partial typography style or a per-scale one, merged over the theme typography for the badge's scale. The default theme uses bold text fromSize10(xs) toSize24(xl), with no font so each theme keeps its ownintents(optional) — per-intenttextColor,backgroundColor,borderColor,backgroundTransparency,backgroundImage, andbackgroundGradient, layered on top oftheme.colors.intentshoverDarken/pressDarken(optional) — how much a clickable badge's resting background is shaded while hovered or pressed, whenhoverStyle/pressStyleset nobackgroundColor. Positive values lerp toward black, negative values lerp toward white by their absolute value.pressDarkenfalls back tohoverDarken; unset or0means no shading. Default and Sandstone use0.08/0.16, Dark uses-0.1/-0.2(lightening its dark backgrounds), and Wooden inherits Default's values
These values can be changed by providing a custom Clean UI theme.