> Markdown version of [Badge](https://vaadin.com/docs/next/components/badge). Section index: [llms.txt](https://vaadin.com/docs/next/components/llms.txt)

# Badge (since V25.1)

Badges are colored text elements containing small bits of information. They’re used for labeling content, displaying metadata and/or highlighting information.

**Lit** — `badge-basic.ts`

```html
<vaadin-badge>Pending</vaadin-badge>
<vaadin-badge theme="success">Confirmed</vaadin-badge>
<vaadin-badge theme="warning">Warning</vaadin-badge>
<vaadin-badge theme="error">Denied</vaadin-badge>
```

**Flow** — `BadgeBasic.java`

```java
Badge pending = new Badge("Pending");

Badge confirmed = new Badge("Confirmed");
confirmed.addThemeVariants(BadgeVariant.SUCCESS);

Badge warning = new Badge("Warning");
warning.addThemeVariants(BadgeVariant.WARNING);

Badge denied = new Badge("Denied");
denied.addThemeVariants(BadgeVariant.ERROR);
```

**React** — `badge-basic.tsx`

```tsx
<Badge>Pending</Badge>
<Badge theme="success">Confirmed</Badge>
<Badge theme="warning">Warning</Badge>
<Badge theme="error">Denied</Badge>
```

## <a id="label"></a>Label

Badges should contain a label. Labels should be clear, concise, and written using sentence case. Aim for 1 to 2 words.

## <a id="icons"></a>Icons

Badges can contain icons as well as text. Icons are placed before the text using the `icon` slot.

**Lit** — `badge-icons.ts`

```html
<vaadin-horizontal-layout theme="spacing">
  <vaadin-badge>
    <vaadin-icon icon="vaadin:clock" slot="icon"></vaadin-icon>
    Pending
  </vaadin-badge>
  <vaadin-badge theme="success">
    <vaadin-icon icon="vaadin:check" slot="icon"></vaadin-icon>
    Confirmed
  </vaadin-badge>
  <vaadin-badge theme="warning">
    <vaadin-icon icon="vaadin:warning" slot="icon"></vaadin-icon>
    Warning
  </vaadin-badge>
  <vaadin-badge theme="error">
    <vaadin-icon icon="vaadin:exclamation-circle" slot="icon"></vaadin-icon>
    Denied
  </vaadin-badge>
</vaadin-horizontal-layout>
```

**Flow** — `BadgeIcons.java`

```java
Badge pending = new Badge("Pending", VaadinIcon.CLOCK.create());

Badge confirmed = new Badge("Confirmed", VaadinIcon.CHECK.create());
confirmed.addThemeVariants(BadgeVariant.SUCCESS);

Badge warning = new Badge("Warning", VaadinIcon.WARNING.create());
warning.addThemeVariants(BadgeVariant.WARNING);

Badge denied = new Badge("Denied",
        VaadinIcon.EXCLAMATION_CIRCLE.create());
denied.addThemeVariants(BadgeVariant.ERROR);
```

**React** — `badge-icons.tsx`

```tsx
<HorizontalLayout theme="spacing">
  <Badge>
    <Icon icon="vaadin:clock" slot="icon" />
    Pending
  </Badge>
  <Badge theme="success">
    <Icon icon="vaadin:check" slot="icon" />
    Confirmed
  </Badge>
  <Badge theme="warning">
    <Icon icon="vaadin:warning" slot="icon" />
    Warning
  </Badge>
  <Badge theme="error">
    <Icon icon="vaadin:exclamation-circle" slot="icon" />
    Denied
  </Badge>
</HorizontalLayout>
```

> **Note: Use icons sparingly**
>
> The benefit of using icons should be weighed against the visual noise it adds.

### <a id="icon-only"></a>Icon-Only

Badges can also be used with icons without a label. For accessibility, the text should be still provided when using `icon-only` style variant. It’s only hidden visually but available for screen readers to ensure that all users can identify the meaning of the badge.

**Lit** — `badge-icons-only.ts`

```html
<vaadin-badge theme="success icon-only">
  <vaadin-icon icon="vaadin:check" slot="icon"></vaadin-icon>
  Confirmed
</vaadin-badge>
<vaadin-badge theme="error icon-only">
  <vaadin-icon icon="vaadin:close" slot="icon"></vaadin-icon>
  Cancelled
</vaadin-badge>
```

**Flow** — `BadgeIconsOnly.java`

```java
Badge confirmed = new Badge("Confirmed", VaadinIcon.CHECK.create());
confirmed.addThemeVariants(BadgeVariant.SUCCESS,
        BadgeVariant.ICON_ONLY);

Badge cancelled = new Badge("Cancelled", VaadinIcon.CLOSE.create());
cancelled.addThemeVariants(BadgeVariant.ERROR, BadgeVariant.ICON_ONLY);
```

**React** — `badge-icons-only.tsx`

```tsx
<Badge theme="success icon-only">
  <Icon icon="vaadin:check" slot="icon" />
  Confirmed
</Badge>
<Badge theme="error icon-only">
  <Icon icon="vaadin:close" slot="icon" />
  Cancelled
</Badge>
```

Icon-only badges should primarily be used for common recurring content with highly standardized, universally understood icons (such as a checkmark for "*yes*"), and for content that’s repeated, for example in lists and tables.

**Lit** — `badge-icons-only-table.ts`

```typescript
const renderBoolean: GridColumnBodyLitRenderer<UserPermissions> = (item, _model, column) => {
  let icon: string;
  let title: string;
  let theme: string;

  if (item[column.id as keyof UserPermissions]) {
    icon = 'vaadin:check';
    title = 'Yes';
    theme = 'success';
  } else {
    icon = 'vaadin:close';
    title = 'No';
    theme = 'error';
  }

  return html`
    <vaadin-badge theme="${theme} icon-only">
      <vaadin-icon icon="${icon}" slot="icon"></vaadin-icon>
      ${title}
    </vaadin-badge>
  `;
};

return html`
  <vaadin-grid .items="${this.items}">
    <vaadin-grid-column path="name" header="Name"></vaadin-grid-column>
    <vaadin-grid-column
      id="view"
      header="View"
      ${columnBodyRenderer(renderBoolean, [])}
    ></vaadin-grid-column>
    <vaadin-grid-column
      id="comment"
      header="Comment"
      ${columnBodyRenderer(renderBoolean, [])}
    ></vaadin-grid-column>
    <vaadin-grid-column
      id="edit"
      header="Edit"
      ${columnBodyRenderer(renderBoolean, [])}
    ></vaadin-grid-column>
  </vaadin-grid>
`;
```

**Flow** — `BadgeIconsOnlyTable.java`

```java
grid.addColumn(UserPermissions::getName).setHeader("Name");
grid.addComponentColumn(userPermissions -> createPermissionBadge(
        userPermissions.getView())).setHeader("View");
grid.addComponentColumn(userPermissions -> createPermissionBadge(
        userPermissions.getComment())).setHeader("Comment");
grid.addComponentColumn(userPermissions -> createPermissionBadge(
        userPermissions.getEdit())).setHeader("Edit");

...

private Badge createPermissionBadge(boolean hasPermission) {
    Badge badge;
    if (hasPermission) {
        badge = new Badge("Yes", VaadinIcon.CHECK.create());
        badge.addThemeVariants(BadgeVariant.SUCCESS,
                BadgeVariant.ICON_ONLY);
    } else {
        badge = new Badge("No", VaadinIcon.CLOSE.create());
        badge.addThemeVariants(BadgeVariant.ERROR, BadgeVariant.ICON_ONLY);
    }
    return badge;
}
```

**React** — `badge-icons-only-table.tsx`

```tsx
const renderBadge = ({
  item,
  original: column,
}: {
  item: any;
  original: GridColumnElement<UserPermissions>;
}) => {
  let icon;
  let title;
  let theme;

  if (item[column.id]) {
    icon = 'vaadin:check';
    title = 'Yes';
    theme = 'success';
  } else {
    icon = 'vaadin:close';
    title = 'No';
    theme = 'error';
  }

  return (
    <Badge theme={`${theme} icon-only`}>
      <Icon icon={icon} slot="icon" />
      {title}
    </Badge>
  );
};

return (
  <Grid items={items.value}>
    <GridColumn path="name" header="Name" />
    <GridColumn id="view" header="View" renderer={renderBadge} />
    <GridColumn id="comment" header="Comment" renderer={renderBadge} />
    <GridColumn id="edit" header="Edit" renderer={renderBadge} />
  </Grid>
);
```

## <a id="number"></a>Number

Badges can display a numeric value using the `number` property. This is useful for showing counts, such as unread messages or notifications. When both text content and a number are set, both are displayed.

**Lit** — `badge-number.ts`

```html
<vaadin-badge number="12">New messages</vaadin-badge>
<vaadin-badge number="3" theme="success">Completed</vaadin-badge>
<vaadin-badge number="1" theme="error">Failed</vaadin-badge>
```

**Flow** — `BadgeNumber.java`

```java
Badge inbox = new Badge("New messages", 12);

Badge completed = new Badge("Completed", 3);
completed.addThemeVariants(BadgeVariant.SUCCESS);

Badge failed = new Badge("Failed", 1);
failed.addThemeVariants(BadgeVariant.ERROR);
```

**React** — `badge-number.tsx`

```tsx
<Badge number={12}>New messages</Badge>
<Badge number={3} theme="success">
  Completed
</Badge>
<Badge number={1} theme="error">
  Failed
</Badge>
```

### <a id="number-only"></a>Number-Only

Badges can display a number without a visible label using the `number-only` style variant. For accessibility, the text label should still be provided — it’s only hidden visually but available for screen readers to ensure that all users can identify the meaning of the badge.

**Lit** — `badge-number-only.ts`

```html
<vaadin-badge number="12" theme="number-only">New messages</vaadin-badge>
<vaadin-badge number="3" theme="error number-only">Alerts</vaadin-badge>
```

**Flow** — `BadgeNumberOnly.java`

```java
Badge inbox = new Badge("New messages", 12);
inbox.addThemeVariants(BadgeVariant.NUMBER_ONLY);

Badge alerts = new Badge("Alerts", 3);
alerts.addThemeVariants(BadgeVariant.ERROR, BadgeVariant.NUMBER_ONLY);
```

**React** — `badge-number-only.tsx`

```tsx
<Badge number={12} theme="number-only">
  New messages
</Badge>
<Badge number={3} theme="error number-only">
  Alerts
</Badge>
```

## <a id="use-cases"></a>Use Cases

### <a id="highlighting-and-distinguishing-information"></a>Highlighting and Distinguishing Information

A typical use case for badges is to highlight an item’s status, for example in a Grid.

**Lit** — `badge-highlight.ts`

```typescript
return html`
  <vaadin-grid .items="${this.items}">
    <vaadin-grid-column path="report" header="Report"></vaadin-grid-column>
    <vaadin-grid-column
      header="Due date"
      ${columnBodyRenderer<Report>(({ due }) => html`${format(new Date(due), 'MMM d, yyyy')}`, [])}
    ></vaadin-grid-column>
    <vaadin-grid-column path="assignee" header="Assignee"></vaadin-grid-column>
    <vaadin-grid-column
      header="Status"
      ${columnBodyRenderer<Report>(({ status }) => {
        let title: string;
        let theme: string;

        switch (status) {
          case ReportStatus.COMPLETED:
            title = 'Completed';
            theme = 'success';
            break;
          case ReportStatus.IN_PROGRESS:
            title = 'In progress';
            theme = '';
            break;
          case ReportStatus.CANCELLED:
            title = 'Cancelled';
            theme = 'error';
            break;
          case ReportStatus.ON_HOLD:
            title = 'On hold';
            theme = 'warning';
            break;
        }

        return html`<vaadin-badge theme="${theme} filled">${title}</vaadin-badge>`;
      }, [])}
    ></vaadin-grid-column>
  </vaadin-grid>
`;
```

**Flow** — `BadgeHighlight.java`

```java
grid.addComponentColumn(report -> createStatusBadge(report.getStatus()))
        .setHeader("Status");

...

private Badge createStatusBadge(String status) {
    Badge badge = new Badge(status);
    badge.addThemeVariants(BadgeVariant.FILLED);
    switch (status) {
    case "Completed":
        badge.addThemeVariants(BadgeVariant.SUCCESS);
        break;
    case "Cancelled":
        badge.addThemeVariants(BadgeVariant.ERROR);
        break;
    case "On hold":
        badge.addThemeVariants(BadgeVariant.WARNING);
        break;
    default:
        break;
    }
    return badge;
}
```

**React** — `badge-highlight.tsx`

```tsx
function renderDueDate({ item: report }: { item: Report }) {
  return <span>{format(new Date(report.due), 'MMM d, yyyy')}</span>;
}

function renderStatus({ item: report }: { item: Report }) {
  let title: string;
  let theme: string;

  switch (report.status) {
    case ReportStatus.COMPLETED:
      title = 'Completed';
      theme = 'success';
      break;
    case ReportStatus.IN_PROGRESS:
      title = 'In progress';
      theme = '';
      break;
    case ReportStatus.CANCELLED:
      title = 'Cancelled';
      theme = 'error';
      break;
    case ReportStatus.ON_HOLD:
      title = 'On hold';
      theme = 'warning';
      break;
  }

  return <Badge theme={`${theme} filled`}>{title}</Badge>;
}

function Example() {
  const items = useSignal<Report[]>([]);
  useEffect(() => {
    getReports().then((reports) => {
      items.value = reports as Report[];
    });
  }, []);

  return (
    <Grid items={items.value}>
      <GridColumn path="report" header="Report" />
      <GridColumn header="Due date" renderer={renderDueDate} />
      <GridColumn path="assignee" header="Assignee" />
      <GridColumn header="Status" renderer={renderStatus} />
    </Grid>
  );
}
```

They are also often used for displaying metadata tags.

### <a id="interactive-content"></a>Interactive Content

Badges can house interactive content such as Anchors and Buttons. For example, Badges that highlight active filters might contain a "Clear" Button which removes the associated filter.

**Lit** — `badge-interactive.ts`

```typescript
return html`
  <vaadin-vertical-layout theme="spacing">
    <vaadin-combo-box
      label="Profession"
      .items="${this.items}"
      @change="${this.onChange}"
    ></vaadin-combo-box>
    <vaadin-horizontal-layout theme="spacing wrap">
      ${repeat(
        this.selectedProfessions,
        (profession) => profession,
        (profession) => html`
          <vaadin-badge style="padding-right: 0">
            <span>${profession}</span>
            <vaadin-button
              aria-label="Clear filter: ${profession}"
              data-profession="${profession}"
              theme="tertiary"
              title="Clear filter: ${profession}"
              style="height: 1.5rem; min-width: 1.5rem; margin: 0; padding: 0"
              @click="${this.onClick}"
            >
              <vaadin-icon icon="vaadin:close"></vaadin-icon>
            </vaadin-button>
          </vaadin-badge>
        `
      )}
    </vaadin-horizontal-layout>
  </vaadin-vertical-layout>
`;
```

**Flow** — `BadgeInteractive.java`

```java
HorizontalLayout badges = new HorizontalLayout();
badges.addThemeNames("wrap");

ComboBox<String> comboBox = new ComboBox<>("Profession");
comboBox.setItems(DataService.getProfessions());
comboBox.addValueChangeListener(e -> {
    Badge filterBadge = createFilterBadge(e.getValue());
    badges.add(filterBadge);
});

...

private Badge createFilterBadge(String profession) {
    Button clearButton = new Button(VaadinIcon.CLOSE.create());
    clearButton.addThemeVariants(ButtonVariant.TERTIARY);
    clearButton.getStyle().set("height", "1.5rem")
            .set("min-width", "1.5rem").set("margin", "0")
            .set("padding", "0");
    // Accessible button name
    clearButton.getElement().setAttribute("aria-label",
            "Clear filter: " + profession);
    // Tooltip
    clearButton.getElement().setAttribute("title",
            "Clear filter: " + profession);

    Badge badge = new Badge(profession);
    badge.getStyle().set("padding-right", "0");
    badge.getElement().appendChild(clearButton.getElement());

    // Add handler for removing the badge
    clearButton.addClickListener(event -> {
        badge.getElement().removeFromParent();
    });

    return badge;
}
```

**React** — `badge-interactive.tsx`

```tsx
return (
  <VerticalLayout theme="spacing">
    <ComboBox label="Profession" items={items.value} onChange={onChange} />

    <HorizontalLayout theme="spacing wrap">
      {selectedProfessions.value.map((profession) => (
        <Badge key={profession} style={{ paddingRight: '0' }}>
          <span>{profession}</span>
          <Button
            aria-label={`Clear filter: ${profession}`}
            data-profession={profession}
            theme="tertiary"
            title={`Clear filter: ${profession}`}
            style={{ height: '1.5rem', minWidth: '1.5rem', margin: '0', padding: '0' }}
            onClick={onClick}
          >
            <Icon icon="vaadin:close" />
          </Button>
        </Badge>
      ))}
    </HorizontalLayout>
  </VerticalLayout>
);
```

### <a id="counter"></a>Counter

Badges can be used as counters, for example to show the number of unread/new messages, selection count, etc.

**Lit** — `badge-counter.ts`

```html
<vaadin-tabs>
  <vaadin-tab>
    <span>Inbox</span>
    <vaadin-badge number="12" theme="filled number-only">unread messages</vaadin-badge>
  </vaadin-tab>
  <vaadin-tab>
    <span>Important</span>
    <vaadin-badge number="3" theme="filled number-only">unread messages</vaadin-badge>
  </vaadin-tab>
  <vaadin-tab>
    <span>Spam</span>
    <vaadin-badge number="45" theme="filled number-only">unread messages</vaadin-badge>
  </vaadin-tab>
  <vaadin-tab>
    <span>Archive</span>
    <vaadin-badge number="23" theme="filled number-only">unread messages</vaadin-badge>
  </vaadin-tab>
</vaadin-tabs>
```

**Flow** — `BadgeCounter.java`

```java
Tabs tabs = new Tabs(createTab("Inbox", 12), createTab("Important", 3),
        createTab("Spam", 45), createTab("Archive", 23));

...

private static Tab createTab(String labelText, int messageCount) {
    Span label = new Span(labelText);
    Badge counter = new Badge("unread messages", messageCount);
    counter.addThemeVariants(BadgeVariant.FILLED, BadgeVariant.NUMBER_ONLY);
    counter.getStyle().set("margin-inline-start", "var(--vaadin-gap-s)");

    return new Tab(label, counter);
}
```

**React** — `badge-counter.tsx`

```tsx
<Tabs>
  <Tab>
    <span>Inbox</span>
    <Badge number={12} theme="filled number-only" style={badgeStyle}>
      unread messages
    </Badge>
  </Tab>
  <Tab>
    <span>Important</span>
    <Badge number={3} theme="filled number-only" style={badgeStyle}>
      unread messages
    </Badge>
  </Tab>
  <Tab>
    <span>Spam</span>
    <Badge number={45} theme="filled number-only" style={badgeStyle}>
      unread messages
    </Badge>
  </Tab>
  <Tab>
    <span>Archive</span>
    <Badge number={23} theme="filled number-only" style={badgeStyle}>
      unread messages
    </Badge>
  </Tab>
</Tabs>
```

Assistive technologies, such as screen readers, interpret badges solely based on their content. Without proper context, they may end up confusing the user. To provide context for people using screen readers, set the badge’s `aria-label` attribute.

## <a id="best-practices"></a>Best Practices

### <a id="badge-vs-button"></a>Badge vs Button

Badges and Buttons are similar in appearance. This might lead users to think that badges are interactive.

Placement, language, and color can all help mitigate any confusion. First, badges shouldn’t be labeled with active verbs. They aren’t actions, but rather static text/content. Second, avoid placing badges directly next to Buttons, in particular if they are using similar themes.

`69AF8882-CA67-4AB8-BF9F-69A1EA6CE84C`
