SharePoint JSON Formatting: Conditional Colors, Action Buttons and Overdue Row Highlights That Work

Column formatting changes how one column is drawn; view formatting changes whole rows. Both are JSON stored on the column (CustomFormatter) or the view, and neither changes the data. Use @currentField for the column's value, [$InternalName] for other columns, @now for today, and customRowAction with setValue to give users a one-click button that updates the item. Apply them from the column menu (Column settings, Format this column) or in bulk with PowerShell.

Published:

Last updated: 2026-10-29

What JSON formatting can and can't do

JSON formatting changes how a value is displayed in modern list views. The value stored in the item stays the same, so sorting, filtering, exports and Power Automate see the raw value. Two kinds:

Column formattingView formatting
Stored onThe column (CustomFormatter property)The view (CustomFormatter on the view)
Applies toEvery view that shows the columnOnly that view
Good forPills, icons, progress bars, buttonsRow colors, card layouts, group headers
Schemasp/v2/column-formatting.schema.jsonsp/v2/row-formatting.schema.json

It doesn't work in classic views, in the Lists app on some older mobile clients, or in exports to Excel. Use the internal name of a column in [$Name], not the display name; our "Status" column's internal name is TaskStatus.

Example 1: Status pills

This colors a choice column by value. The sp-css-* classes are theme-aware, so the colors stay readable in dark mode and with any site theme:

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/column-formatting.schema.json",
  "elmType": "div",
  "style": { "padding": "0 10px", "height": "22px", "line-height": "22px",
             "border-radius": "11px", "display": "inline-block", "font-weight": "600" },
  "attributes": {
    "class": "=if(@currentField == 'Done', 'sp-css-backgroundColor-BgMintGreen sp-css-color-MintGreenFont',
              if(@currentField == 'Blocked', 'sp-css-backgroundColor-BgCoral sp-css-color-CoralFont',
              if(@currentField == 'In progress', 'sp-css-backgroundColor-BgGold sp-css-color-GoldFont',
              'sp-css-backgroundColor-BgLightGray30 sp-css-color-LightGrayFont')))"
  },
  "txtContent": "@currentField"
}

Write the expression on one line when you paste it; it's split here to fit. The last value in the nested if is the fallback, so a choice added later still gets a gray pill instead of no style.

Example 2: A "Mark done" button that updates the item

customRowAction with setValue updates columns on the item when clicked, without opening the form. We put it in the Priority column, so it sits on every row, and hid it when the task is already done:

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/column-formatting.schema.json",
  "elmType": "div",
  "style": { "display": "flex", "align-items": "center", "white-space": "nowrap" },
  "children": [
    { "elmType": "span", "txtContent": "@currentField", "style": { "width": "64px" } },
    { "elmType": "button", "txtContent": "Mark done",
      "style": { "display": "=if([$TaskStatus] == 'Done', 'none', 'inline-block')",
                 "border": "1px solid #0b62b8", "background-color": "white", "color": "#0b62b8",
                 "border-radius": "4px", "padding": "1px 8px", "cursor": "pointer" },
      "customRowAction": { "action": "setValue", "actionInput": { "TaskStatus": "Done" } } }
  ]
}
Project Tasks view with colored Status pills (Done, Not started, In progress, Blocked) and a Mark done button in the Priority column on rows that are not done
Status pills and the Mark done button. Rows already Done have no button.

Things to know about setValue:

  • It needs edit permission on the item. For read-only users the click fails. JSON has no permission check, so to show the button only to the assignee, compare [$AssignedTo.email] with @me in the display expression.
  • The keys in actionInput are internal names. Choice values must match a choice exactly.
  • It creates a new version and fires flows on "item modified", like any edit.
  • For a date, use "@now"; for a person, "@me"; to clear a value, "".

Example 3: Highlight overdue rows with view formatting

View formatting applies a class to the whole row. This marks tasks that are past due and not done:

{
  "$schema": "https://developer.microsoft.com/json-schemas/sp/v2/row-formatting.schema.json",
  "additionalRowClass": "=if([$DueDate] < @now && [$TaskStatus] != 'Done', 'sp-css-backgroundColor-BgCoral sp-css-borderColor-CoralFont', '')"
}
All Items view with overdue rows highlighted red and rows that are done or not yet due left white
Rows past due and not done turn red. The Done row due in March 2025 stays white.

Unlike a calculated column with TODAY(), which only updates when the item is saved, @now is evaluated each time the view loads, so the highlight is always current. See SharePoint calculated column formulas for why TODAY() goes stale.

The formatting stays on in Edit in grid view, which is how our grid view screenshots in Edit in grid view not working still show red rows.

Option 1: Apply it in the browser

  1. Column formatting: open the column's header menu, then Column settings, then Format this column. Choose Advanced mode and paste the JSON. Preview shows it before you save.
  2. View formatting: open the view menu next to the view name, then Format current view, then Advanced mode.
  3. The JSON is saved for everyone. Column formatting needs permission to manage the list; view formatting on a public view does too.

If the preview shows nothing, check the browser console (F12): a JSON syntax error is reported there, and an internal name that doesn't exist silently evaluates to empty.

Option 2: Apply formatting to many lists with PowerShell

When the same format belongs on the same column in many lists (every "Status" column on every project site, say), set the CustomFormatter property with PnP PowerShell. This script previews by default and only writes when you set $WhatIf = $false. It needs PnP PowerShell 2.x and your own Entra ID app registration.

$ClientId  = "<your-app-id>"
$Sites     = Import-Csv .\sites.csv            # one column: Url
$ListTitle = "Project Tasks"
$Column    = "TaskStatus"                      # internal name
$Json      = Get-Content .\status-pills.json -Raw
$WhatIf    = $true

foreach ($s in $Sites) {
    Connect-PnPOnline -Url $s.Url -Interactive -ClientId $ClientId
    $list = Get-PnPList -Identity $ListTitle -ErrorAction SilentlyContinue
    if (-not $list) { "{0}: no list '{1}'" -f $s.Url, $ListTitle; continue }
    $field = Get-PnPField -List $list -Identity $Column -ErrorAction SilentlyContinue
    if (-not $field) { "{0}: no column '{1}'" -f $s.Url, $Column; continue }
    $same = $field.CustomFormatter -eq $Json
    "{0}: current format {1}" -f $s.Url, $(if ($same) { "already applied" } elseif ($field.CustomFormatter) { "is different" } else { "is empty" })
    if (-not $WhatIf -and -not $same) {
        Set-PnPField -List $list -Identity $Column -Values @{ CustomFormatter = $Json }
    }
}

For view formatting, use Set-PnPView -List $list -Identity "All Items" -Values @{ CustomFormatter = $Json }. Back up the old value first if a column already has a format; there's no history for it.

How SharePoint Essentials Toolkit relates

The toolkit doesn't edit column formatting on your lists. Two places where it touches the same feature:

  • Export to SharePoint on any report creates a SharePoint list with typed columns, and status columns get JSON pill formatting like Example 1, so a permissions or link report is readable in the browser straight away.
  • The Copy & move tasks create missing columns on the target from the source column's definition, so lists copied to new sites arrive with their columns, views and settings in place.
Community Edition is free on up to 20 sites per Microsoft 365 tenant.

Frequently asked questions

Does JSON formatting change the data in a SharePoint list?

No. It only changes how the value is shown in modern views. Sorting, filtering, exports and flows still see the stored value. The exception is customRowAction with setValue, which updates the item when a user clicks the button.

Why does my SharePoint column formatting show nothing?

Usually a column referenced by display name instead of internal name, or a JSON syntax error. Check the internal name in the column settings URL (Field=...), and look in the browser console for the error.

How do I highlight overdue items in a SharePoint list?

Use view formatting with additionalRowClass and compare the due date to @now, for example =if([$DueDate] < @now && [$TaskStatus] != 'Done', 'sp-css-backgroundColor-BgCoral', ''). @now is evaluated each time the view loads.

Can a SharePoint formatted button update the item?

Yes. customRowAction with action setValue and actionInput such as { "TaskStatus": "Done" } updates the item in place. The user needs edit permission, and it creates a new version.

Can I apply SharePoint column formatting to many lists at once?

Yes. Set the column's CustomFormatter property with Set-PnPField, or the view's CustomFormatter with Set-PnPView, in a loop over your sites and lists.

Get readable SharePoint reports, free

SharePoint Essentials Toolkit Community Edition is free. Inventory lists and columns, copy lists between sites, and export reports to SharePoint lists with formatting applied, on up to 20 sites per Microsoft 365 tenant. Get your free Community Edition license key.

About the author. The Cognillo SharePoint Team builds and supports SharePoint Essentials Toolkit and has worked with SharePoint administrators on permissions audits, migrations and cleanup projects since 2012.