SharePoint Approval Status Badges with Column Formatting JSON
Turn the built-in moderation status into a clean icon + color “pill” (plus 3 variations you can reuse).
When you enable Content Approval on a SharePoint list or library, SharePoint exposes an internal moderation value (commonly shown as Approval Status). Out of the box, it’s not very “scan-friendly” in a busy view. With Column Formatting JSON, you can transform that value into a modern badge with:
- A colored background (pill)
- A status icon
- Optional border rules
- A tooltip for clarity
This is pure UI formatting: it doesn’t change data, permissions, or workflow behavior—only how the column is rendered in views.
Why Approval Status formatting is worth doing
In real list views, people scan status first. The difference between Pending vs Approved vs Draft should be visible instantly—without opening the item.
A status badge provides:
- Immediate recognition (color + icon)
- Faster triage in approvals-heavy lists
- Consistent UX across lists if you standardize the JSON
The key detail: Approval Status numeric mapping
Your JSON uses Number(@currentField) which means the column is being treated as a numeric moderation value.
Here is the commonly used mapping for moderation status:
| Numeric value | Meaning |
|---|---|
| 0 | Approved |
| 1 | Denied / Rejected |
| 2 | Pending |
| 3 | Draft |
| 4 | Scheduled |
That’s why your if(Number(@currentField)==0, ...) must be interpreted as Approved, not Draft.
How your JSON works (what each part does)
Your formatter renders this structure:
- Outer container (
div)
Used mostly for layout and spacing. - Pill container
- Flex row layout
border-radius: 20px→ pill shape- Conditional background color using
ms-bgColor-*classes - Optional border when Draft (
==3)
- Icon bubble
- A small circle (
border-radius: 50%) - White background
- Icon and icon color change per status
- A small circle (
- Text label
- Uses
@currentField(so it might show numeric value in some views) - Conditional font color (white on dark backgrounds, dark on white)
- Uses
Step-by-step: How to apply Column Formatting JSON
Step 1 — Confirm Content Approval is enabled
- Go to your list/library → Settings
- Open Versioning settings
- Enable Content Approval
- Save
Without content approval, the moderation status may not be meaningful.
Step 2 — Open the formatting panel
- Go to your view
- Find the Approval Status column
- Column menu → Column settings
- Click Format this column
Step 3 — Paste JSON + Preview
- Paste the JSON you want (base or a variation below)
- Use Preview and test items in multiple states
- Click Save
Step 4 — Validate in a real view
- Check readability on both light and dark list themes
- Make sure Draft doesn’t look like “Disabled”
- Confirm the icon choices match what your users understand
BASE FORMATTER (Your JSON, ready to paste)
This is your formatter as provided. It produces a pill badge with icon + text.
{ "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/column-formatting.schema.json", "elmType": "div", "children": [ { "elmType": "div", "style": { "display": "flex", "flex-direction": "column", "align-items": "flex-start", "margin": "7px 0px", "white-space": "nowrap" }, "children": [ { "elmType": "div", "style": { "display": "flex", "flex-direction": "row", "align-items": "center", "padding": "3px", "border-radius": "20px", "border": "=if(Number(@currentField)==3,'1px solid','')" }, "attributes": { "class": "=if(Number(@currentField)==0,'ms-bgColor-tealLight',if(Number(@currentField)==1,'ms-bgColor-sharedRed10',if(Number(@currentField)==2,'ms-bgColor-sharedBlueMagenta10',if(Number(@currentField)==3,'ms-bgColor-white','ms-bgColor-blue'))))" }, "children": [ { "elmType": "div", "style": { "padding": "4px", "border-radius": "50%", "font-weight": "bold" }, "attributes": { "iconName": "=if(Number(@currentField)==0,'Accept',if(Number(@currentField)==1,'Cancel',if(Number(@currentField)==2,'Forward',if(Number(@currentField)==3,'CalculatorSubtract','EventAccepted'))))", "class": "='ms-fontSize-10 ms-bgColor-white '+if(Number(@currentField)==0,'ms-fontColor-teal',if(Number(@currentField)==1,'ms-fontColor-sharedRed10',if(Number(@currentField)==2,'ms-fontColor-sharedBlueMagenta10',if(Number(@currentField)==3,'','ms-fontColor-blue'))))" } }, { "elmType": "div", "txtContent": "@currentField", "style": { "margin": "0px 7px" }, "attributes": { "class": "=if(Number(@currentField)==3,'','ms-fontColor-white')" } } ] } ] } ]}
Three variations you can publish and reuse
Each variation solves a practical UX need you’ll run into in real SharePoint views.
Variation 1 — Human-readable labels (instead of 0/1/2/3/4)
If your view displays the numeric value, users will see “2” instead of “Pending”. This variation generates a friendly label using the mapping.
When to use
- Your list shows numeric status values
- You want consistent text across all views
{ "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/column-formatting.schema.json", "elmType": "div", "style": { "display": "flex", "align-items": "center", "padding": "4px 10px", "border-radius": "999px", "white-space": "nowrap" }, "attributes": { "class": "=if(Number(@currentField)==0,'ms-bgColor-tealLight',if(Number(@currentField)==1,'ms-bgColor-sharedRed10',if(Number(@currentField)==2,'ms-bgColor-sharedBlueMagenta10',if(Number(@currentField)==3,'ms-bgColor-white',if(Number(@currentField)==4,'ms-bgColor-sharedOrange10','ms-bgColor-neutralLight')))))", "title": "=if(Number(@currentField)==0,'Approved',if(Number(@currentField)==1,'Denied',if(Number(@currentField)==2,'Pending',if(Number(@currentField)==3,'Draft',if(Number(@currentField)==4,'Scheduled','Unknown')))))" }, "children": [ { "elmType": "span", "style": { "margin-right": "8px" }, "attributes": { "iconName": "=if(Number(@currentField)==0,'Accept',if(Number(@currentField)==1,'Cancel',if(Number(@currentField)==2,'Clock',if(Number(@currentField)==3,'Edit',if(Number(@currentField)==4,'Calendar','StatusCircleQuestionMark')))))", "class": "='ms-fontSize-14 ms-bgColor-white ' + if(Number(@currentField)==0,'ms-fontColor-teal',if(Number(@currentField)==1,'ms-fontColor-sharedRed20',if(Number(@currentField)==2,'ms-fontColor-sharedBlueMagenta20',if(Number(@currentField)==3,'ms-fontColor-neutralPrimary',if(Number(@currentField)==4,'ms-fontColor-sharedOrange20','ms-fontColor-neutralPrimary')))))" } }, { "elmType": "span", "txtContent": "=if(Number(@currentField)==0,'Approved',if(Number(@currentField)==1,'Denied',if(Number(@currentField)==2,'Pending',if(Number(@currentField)==3,'Draft',if(Number(@currentField)==4,'Scheduled','Unknown')))))", "attributes": { "class": "=if(Number(@currentField)==3,'ms-fontColor-neutralPrimary','ms-fontColor-white')" } } ]}
Variation 2 — Icon-only “compact mode” (best for dense lists)
Sometimes you don’t have horizontal space. This version shows only a colored circle + icon, and uses tooltip for the label.
When to use
- You have many columns
- You want a minimal “status indicator”
{ "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/column-formatting.schema.json", "elmType": "div", "style": { "display": "flex", "align-items": "center", "justify-content": "center", "width": "28px", "height": "28px", "border-radius": "50%" }, "attributes": { "title": "=if(Number(@currentField)==0,'Approved',if(Number(@currentField)==1,'Denied',if(Number(@currentField)==2,'Pending',if(Number(@currentField)==3,'Draft',if(Number(@currentField)==4,'Scheduled','Unknown')))))", "class": "=if(Number(@currentField)==0,'ms-bgColor-tealLight',if(Number(@currentField)==1,'ms-bgColor-sharedRed10',if(Number(@currentField)==2,'ms-bgColor-sharedYellow10',if(Number(@currentField)==3,'ms-bgColor-neutralLight',if(Number(@currentField)==4,'ms-bgColor-sharedOrange10','ms-bgColor-neutralLight')))))" }, "children": [ { "elmType": "span", "attributes": { "iconName": "=if(Number(@currentField)==0,'Accept',if(Number(@currentField)==1,'Cancel',if(Number(@currentField)==2,'Clock',if(Number(@currentField)==3,'Edit',if(Number(@currentField)==4,'Calendar','StatusCircleQuestionMark')))))", "class": "=if(Number(@currentField)==3,'ms-fontColor-neutralPrimary','ms-fontColor-white')" } } ]}
Variation 3 — Explicit “Scheduled” (4) + “Unknown” fallback (robust governance)
Your base JSON has a final “else” that implicitly covers “Scheduled” and unknown values. This version handles them explicitly to avoid confusion later.
When to use
- You want long-term reliability
- You expect more moderation states or odd values in some libraries
{ "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/column-formatting.schema.json", "elmType": "div", "style": { "display": "flex", "align-items": "center", "padding": "3px 8px", "border-radius": "16px", "white-space": "nowrap" }, "attributes": { "class": "=if(Number(@currentField)==0,'ms-bgColor-tealLight',if(Number(@currentField)==1,'ms-bgColor-sharedRed10',if(Number(@currentField)==2,'ms-bgColor-sharedBlueMagenta10',if(Number(@currentField)==3,'ms-bgColor-white',if(Number(@currentField)==4,'ms-bgColor-sharedOrange10','ms-bgColor-neutralLight')))))", "title": "=if(Number(@currentField)==0,'Approved',if(Number(@currentField)==1,'Denied',if(Number(@currentField)==2,'Pending',if(Number(@currentField)==3,'Draft',if(Number(@currentField)==4,'Scheduled','Unknown')))))" }, "children": [ { "elmType": "span", "style": { "padding": "4px", "border-radius": "50%", "margin-right": "6px" }, "attributes": { "iconName": "=if(Number(@currentField)==0,'Accept',if(Number(@currentField)==1,'Cancel',if(Number(@currentField)==2,'Forward',if(Number(@currentField)==3,'CalculatorSubtract',if(Number(@currentField)==4,'Calendar','StatusCircleQuestionMark')))))", "class": "='ms-fontSize-12 ms-bgColor-white ' + if(Number(@currentField)==0,'ms-fontColor-teal',if(Number(@currentField)==1,'ms-fontColor-sharedRed20',if(Number(@currentField)==2,'ms-fontColor-sharedBlueMagenta20',if(Number(@currentField)==3,'ms-fontColor-neutralPrimary',if(Number(@currentField)==4,'ms-fontColor-sharedOrange20','ms-fontColor-neutralPrimary')))))" } }, { "elmType": "span", "txtContent": "=if(Number(@currentField)==0,'Approved',if(Number(@currentField)==1,'Denied',if(Number(@currentField)==2,'Pending',if(Number(@currentField)==3,'Draft',if(Number(@currentField)==4,'Scheduled','Unknown')))))", "attributes": { "class": "=if(Number(@currentField)==3,'ms-fontColor-neutralPrimary','ms-fontColor-white')" } } ]}
Practical recommendations (what I’d standardize)
If you want this to be a reusable “pattern” in your environment:
- Use Variation 1 as default (readable labels, explicit mapping).
- Use Variation 2 for “dashboard-like” list views with many columns.
- Use Variation 3 in libraries where “Scheduled” is actively used.
Final table — Implementation steps (copy-paste checklist)
| Step | Action | Result |
|---|---|---|
| 1 | Enable Content Approval in Versioning Settings | Approval Status becomes meaningful |
| 2 | Open Format this column in the Approval Status column | JSON editor opens |
| 3 | Paste Base or a Variation JSON | Badge UI is rendered |
| 4 | Preview items in multiple states | Validate icon/color logic |
| 5 | Save formatting | View is updated for users |
Final table — Technical cheat sheet (expressions + UX)
| Topic | Best practice | Why it matters |
|---|---|---|
Number(@currentField) | Always assume numeric mapping 0–4 | Prevents wrong status interpretation |
| Background classes | Use ms-bgColor-* classes | Keeps Fluent UI look consistent |
| Light backgrounds | Switch text to neutral/dark | White on white becomes unreadable |
Tooltips (title) | Add human-readable meaning | Helps icon-only and quick scanning |
| Scheduled/Unknown | Handle explicitly (Variation 3) | Improves long-term reliability |
