Skip to main content
iVentureTeam

badge

The badge widget renders a field's value as a colored pill, and its color comes from decoration expressions you write in the view or an integer color field on the record.

August 11, 2026Updated August 11, 20266 min read
Odoo 19 invoice list where the payment status column renders as colored pills via the badge widget.
Studio nameBadge
Technical namebadge
Field typesselection, many2one, char
Viewslist, form, kanban
Moduleweb, present in every Odoo database
Used in core99 occurrences across 41 modules, including account, purchase, stock, project, hr_recruitment
VersionsOdoo 20.0, Odoo 19.0, Odoo 18.0, Odoo 17.0, Odoo 16.0
No-code setupPartly: Studio sets the widget; decoration conditions are written in XML
Alternativesselection_badge, state_selection, statusbar, label_selection

What the badge widget does

Status reads faster as color than as text. The badge widget takes a field's display value, the label of a selection, the name of a many2one, a plain char, and wraps it in a rounded pill whose color you control per record.

The source resolves the color in a strict order. If the widget has a color_field option, it reads that integer from the record and applies the corresponding palette class, o_badge_color_0 through o_badge_color_11, the same palette tags use. Otherwise it walks the field's decoration-* attributes in order and applies the Bootstrap class of the first expression that evaluates true, text-bg-danger, text-bg-warning and friends. If nothing matches, the pill falls back to a neutral gray, class text-bg-300.

It is purely a display widget: readable everywhere, editable nowhere. Clicking a badge does nothing, which is exactly right for values that buttons and workflows are supposed to change.

What this means for your team

Lists are where your team actually lives, and a status column of colored pills is scannable in a way text never is. Overdue in red, on-hold in yellow, done in green: a hundred-row list becomes a traffic report. The payoff is fewer missed exceptions, because the exceptional rows stop looking like every other row.

The discipline is a consistent color language. Red should mean the same severity on invoices, deliveries and tasks, or the colors stop carrying information. We keep a one-line legend per project, danger equals blocked or overdue, warning equals waiting on someone, success equals done, and apply it in every view we touch, because the decoration conditions live per view.

Badge versus full status widgets is also a cost decision: a badge is one attribute on an existing list column, no model change, no workflow, which makes it the cheapest visual win available on most screens.

Setting it up in Odoo Studio (no code)

Studio covers the widget itself; the color logic is XML.

  1. Open the list or form in Studio and select the selection, many2one or char field.

  2. In Properties, set Widget to Badge. The value now renders as a neutral pill.

What Studio cannot do here

Studio stops at the neutral pill. The two coloring mechanisms, decoration-* condition attributes on the field and the color_field option, are view-XML edits. They are one-line edits, shown below, and the usual moment teams hand a view to a developer for five minutes.

Supported options in Odoo 19

Verified against badge_field.js in the Odoo 19.0 web module. One declared option; the decoration attributes are standard view syntax the widget consumes via its decorations prop.

OptionTypeWhat it does
color_fieldfield nameInteger field on the record (0 to 11) selecting a palette color, applied as class o_badge_color_N. When set, all decoration-* attributes are ignored; the source checks it first.

The color field wins outright. The source checks colorField before evaluating any decoration, so setting both means the decorations are dead code. Pick one mechanism per field: conditions in the view when color follows state, a color field when color is data users manage (like tag colors).

Working examples

State-driven colors with decorations

<field name="state" widget="badge"
       decoration-danger="state == 'cancel'"
       decoration-warning="state == 'pending'"
       decoration-success="state == 'done'"/>

First matching condition sets the pill color; anything unmatched renders the neutral gray.

Color stored on the record

<field name="tag_id" widget="badge"
       options="{'color_field': 'tag_color'}"/>

The integer field tag_color (0 to 11) on the record picks the palette color, the same indexes the tags widget uses. Remember it overrides all decorations.

Full condition set including neutral

<field name="payment_state" widget="badge"
       decoration-success="payment_state == 'paid'"
       decoration-info="payment_state == 'in_payment'"
       decoration-muted="payment_state == 'not_paid'"/>

decoration-muted is legal here and maps to the same neutral gray as no match, verified in the source's explicit muted fallback.

Where the colors really come from

Where the pill's color class comes from is worth thirty seconds, because it explains every styling question about this widget.

Decorations map to Bootstrap background classes. decoration-danger becomes text-bg-danger, and so on for warning, success, info, primary. The palette is your Bootstrap theme; restyle the theme and every badge follows.

The muted special case. Bootstrap has no text-bg-muted, so the source maps decoration-muted to text-bg-300, the same class used when nothing matches. Muted and unmatched are therefore visually identical, by design.

The color-field palette is Odoo's, not Bootstrap's. With color_field set, classes are o_badge_color_0 to o_badge_color_11, the eleven-color palette shared with tags. Index 0 renders the neutral style, which is why a tag with no color and a badge with no match look the same.

Evaluation order is declaration order. Decorations are checked in the order they appear on the field, first match wins. Put the most specific condition first when states overlap.

Version compatibility

VersionStatusNotes
Odoo 20.0In developmentNot released. The development branch shows no behavior changes; see below.
Odoo 19.0VerifiedVerified against the shipped source and tested on a clean database.
Odoo 18.0VerifiedSame behavior. No XML changes needed.
Odoo 17.0VerifiedSame behavior. No XML changes needed.
Odoo 16.0VerifiedSame behavior. No XML changes needed.

Upgrade note. Types, the option and both color mechanisms are unchanged from Odoo 16 through 19; views migrate untouched. If badges lost color after a migration, the usual cause is a customized list view that dropped the decoration-* attributes, not the widget.

What is changing in Odoo 20

Odoo 20 is expected at Odoo Experience in Brussels, 24 to 26 September 2026. The changes below are read from the public development branch and are not final until release.

Nothing changes for this widget. The development version of badge_field.js carries the same types, the same single color_field option, the same decoration-to-class mapping including the muted fallback. Only the internal component syntax moves to the new Owl props system, which affects JavaScript patches and nothing in XML. We re-verify once Odoo 20 ships.

Common problems and fixes

SymptomCause and fix
All badges render grayNo decoration condition matches and no color_field is set; gray text-bg-300 is the designed fallback. Add decoration-* conditions covering your states, or set color_field.
Decorations set but colors ignore themA color_field option is also set, and it takes absolute precedence in the source. Remove one mechanism; they cannot combine.
decoration-muted looks the same as no decorationVerified in the source: muted maps to the same text-bg-300 fallback class. Expected; use another decoration if you need a distinct look.
Users try to click the badge to change statusThe widget is display-only by design. Use selection_badge or state_selection for in-place editing, or a button/statusbar for workflow moves.
Wrong color when two conditions overlapDecorations evaluate in declaration order and the first match wins. Order conditions from most to least specific.

Badge widget vs the alternatives

WidgetBest forKey difference
badgeRead-only status color in lists and formsPure display pill; color from view conditions or a stored color integer
selection_badgePicking a value by clicking badgesEditable: renders every choice as a clickable badge
state_selectionKanban-style status dots with a quick menuCompact colored dot with an inline dropdown to change state
statusbarWorkflow position in the form headerShows the pipeline of stages, clickable to move the record
label_selectionPlain read-only selection labels with decoration colorsText label instead of a pill

The practical test: if the value should only be read, badge. If users should click to change it in place, use selection_badge (edit as buttons) or state_selection (kanban-style dot menu). If it is a workflow the record moves through, that is the statusbar in the form header.

Frequently asked questions

How do I show a colored status in an Odoo list view?+
Set widget="badge" on the field and add condition attributes such as decoration-danger="state == 'cancel'" and decoration-success="state == 'done'". Each matched condition colors the pill with the corresponding Bootstrap class.
Which colors can a badge have?+
With decorations: the Bootstrap set (danger, warning, success, info, primary), rendered as text-bg-* classes, plus a neutral gray fallback. With color_field: Odoo's eleven-color palette via o_badge_color_0 to o_badge_color_11, the same palette tags use.
Why are my badge decorations being ignored?+
Almost always because color_field is also set. Verified in the source: when a color field is configured the decorations are never evaluated. Remove one of the two mechanisms.
Can users change the value by clicking the badge?+
No, it is display-only. For click-to-change behavior use selection_badge (choices as clickable badges) or state_selection (dot with quick menu); keep workflow moves on buttons or the statusbar.
Does the badge widget work on many2one and char fields?+
Yes. Supported types verified in the source are selection, many2one and char; the pill shows the display name or text, and both coloring mechanisms work the same way.
Badge or selection_badge, which one do I need?+
Badge is read-only status display; selection_badge is an editor that renders every choice as a clickable badge. If the question is "show the state", badge. If it is "let users set the state here", selection_badge.

Lists your team can read at a glance?

Consistent status colors across invoices, deliveries and projects are a small view-level change with daily payoff. We apply exactly this kind of UX polish, one coherent color language across all your Odoo views, on Odoo 16 through 19.

Book a free consultation

How this page was produced

The option table, the color resolution order, the Bootstrap class mapping and the muted fallback were read from the Odoo 19.0 web module source (badge_field.js) and confirmed on a clean Odoo 19 database, where the screenshot was captured. The Odoo 20 statement comes from the same file on the public development branch, where it is unchanged. Spotted an error? Tell us and we will correct the page.