SharePoint column formatting

SharePoint choice column formatting

A choice column is the easiest column to make useful: give every value its own color, borrow Microsoft's native severity styles, or add a button that moves the item to the next state.

A Choice column in SharePoint or Microsoft Lists is where most of a list's meaning lives — status, stage, category, size, decision — and by default every value is rendered as the same grey text. Column formatting JSON is the fix, and because the values are a known, finite set, it's also the easiest kind of formatting to get right: map each value to a look, pick a fallback for anything else, done.

The status column guide covers the single most common recipe — a green/yellow/red pill. This page is the wider toolbox: solid pills in your own colors, Microsoft's built-in severity styles with icons, a click-to-cycle button, and what to expect when the column allows multiple values or grows new choices later.

Before and after

ItemStatus
Q1 launch planIn Progress
Onboarding flowDone
API migrationHas Issues
Brand refreshNot Started
Solid pills in your colors · or the native severity tint with an icon (row 3)
Build a choice pill → Opens the generator on the Click-to-Edit Pill template. Enter your field name and choices, copy the JSON, done.

What the SharePoint choice formatting JSON looks like

The pill JSON is in the status guide; here is the other flavour — the Severity Badge, which applies Microsoft's own sp-field-severity classes and a Fluent icon per value, so the result looks like SharePoint drew it itself:

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/column-formatting.schema.json",
  "elmType": "div",
  "inlineEditField": "[$Status]",
  "attributes": {
    "class": {
      "operator": "?",
      "operands": [
        { "operator": "==", "operands": ["[$Status]", ""] },
        "",
        {
          "operator": "?",
          "operands": [
            { "operator": "==", "operands": ["[$Status]", "Done"] },
            "sp-field-severity--good",
            {
              "operator": "?",
              "operands": [
                { "operator": "==", "operands": ["[$Status]", "In Progress"] },
                "sp-field-severity--low",
                {
                  "operator": "?",
                  "operands": [
                    { "operator": "==", "operands": ["[$Status]", "Has Issues"] },
                    "sp-field-severity--warning",
                    "sp-field-severity--severeWarning"
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  },
  "style": {
    "cursor": "pointer"
  },
  "children": [
    {
      "elmType": "span",
      "attributes": {
        "iconName": {
          "operator": "?",
          "operands": [
            { "operator": "==", "operands": ["[$Status]", ""] },
            "",
            {
              "operator": "?",
              "operands": [
                { "operator": "==", "operands": ["[$Status]", "Done"] },
                "CheckMark",
                {
                  "operator": "?",
                  "operands": [
                    { "operator": "==", "operands": ["[$Status]", "In Progress"] },
                    "Forward",
                    {
                      "operator": "?",
                      "operands": [
                        { "operator": "==", "operands": ["[$Status]", "Has Issues"] },
                        "Warning",
                        "Info"
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      },
      "style": {
        "padding-right": "6px"
      }
    },
    {
      "elmType": "span",
      "txtContent": "=if([$Status] == '', '—', [$Status])"
    }
  ]
}

Two expressions do the mapping — one picks a severity class, the other picks an icon — and both share the same shape: a chain of ? (if/else) objects, one per choice value, ending in a fallback. That's the pattern behind every choice formatter, whatever it looks like. The generator writes these comparisons in SharePoint's object form rather than the shorter =if([$Status] == 'Done', …) string on purpose: the string syntax has no way to escape a quote, so a choice value like Client's review could never match. In the object form the value is a plain JSON string and matches exactly. (The example above is shortened to three values.) The five severity tiers are good, low, warning, severeWarning, and blocked; there is no neutral grey tier, and they render as a light tint rather than a solid fill. When you want solid, saturated color, use the pill instead.

The third option changes what a click does. Status Cycle Button renders the choice as a button that advances the value each time it's pressed — Not Started → In Progress → Done — using a customRowAction of type setValue. No form, no dropdown; one click per state. It's ideal for kanban-style flows where the next state is always obvious. T-Shirt Size and Chevron Tag are two more shapes for the same choice-to-look mapping.

How to apply it

  1. Open the generator and pick a choice template: Click-to-Edit Pill for solid colors, Severity Badge for the native look, or Status Cycle Button for one-click changes.
  2. Enter your column's internal field name — see the note below.
  3. Map each choice value to its color, severity, or next state. The values must match the column's choices exactly.
  4. Click Copy.
  5. In SharePoint: Column settings → Format this column → Advanced mode.
  6. Delete what's in the box, paste, click Preview, then Save.
Most common reason it doesn't work: the field name must match your column's internal name, which can differ from the display name (a column shown as "Request Status" might be RequestStatus or Request_x0020_Status internally). See the internal field name guide to find yours.

Common questions

Is this different from SharePoint's built-in choice pills option?
Yes. The built-in option gives each value a color from a fixed palette and stops there. The JSON route lets you pick exact colors, add an icon per value, use Microsoft's native severity tints, keep the cell click-to-edit, or replace the pill with a button that advances to the next state — and values you add later simply get the fallback color instead of breaking.
Does it work for a choice column that allows multiple selections?
Not as generated. A multi-select choice column holds an array of values, so a single [$Status] comparison never matches. Showing each value as its own pill needs a forEach loop in the JSON; the templates target single-value choice columns.
What happens if I add a new choice later?
It shows with the fallback color — grey in the pill template — rather than breaking anything. When you want it styled, open the generator, add the new value with a color, copy, and paste over the old JSON.
Do the values have to match exactly?
Yes — comparisons are case-sensitive and include spaces, so In progress and In Progress are different values. If people have typed free-text variations into a text column, tidy the data or add both spellings to the mapping.