logo
Published on

SPFx Web Part vs Application Customizer vs Field Customizer

One of the most common questions from developers new to the SharePoint Framework is: "I need to customise this page — which SPFx type do I use?"
The answer depends entirely on what you need to customise and where it needs to appear.
This guide cuts through the confusion with a clear breakdown of each extensibility model, when to reach for it, and when to rule it out.


🗂️ The Four SPFx Extensibility Types

SPFx gives you four distinct ways to extend SharePoint:

TypeScaffolded asPrimary DOM target
Web Part--extension-type webpartA content zone on a page
Application Customizer--extension-type applicationCustomizerPage-level header / footer placeholders
Field Customizer--extension-type fieldCustomizerA single list column cell
Command Set--extension-type listViewCommandSetList / library toolbar and context menu

Each type has a different registration mechanism, a different rendering surface, and a different lifecycle. Picking the wrong one wastes days.


🧩 Web Parts

A web part is a self-contained UI component that a page editor drops onto a modern SharePoint page from the toolbox.

Use a web part when:

  • The feature is optional, page-by-page — editors choose where and whether to add it
  • The component needs to occupy space within a page content area
  • You want property pane configuration per instance
  • The feature is standalone: a dashboard, a news feed, a form, a directory

Do not use a web part when:

  • You need the same UI to appear on every page automatically (use an application customizer instead)
  • You need to change how a list column renders in list view (use a field customizer)
  • You need a toolbar button on a list (use a command set)

Key characteristics:

  • Registered in the page via drag-and-drop by a site editor
  • Each instance has its own this.properties bag — isolated configuration
  • Deployed via App Catalog; activated per site
  • React, no-framework, or any JS framework supported
  • Available in SharePoint, Teams tabs, Viva Connections, and Outlook (with manifest changes)

Real examples: Employee directory, approval dashboard, document request form, weather widget, announcements roller.


🖼️ Application Customizer

An application customizer runs on every page within a site (or the entire tenant if deployed broadly). It renders into two named placeholders: Top and Bottom.

Use an application customizer when:

  • You need a consistent UI element that appears on every page — a global nav bar, a cookie banner, a help button, a footer with links
  • The feature must activate automatically without editors adding anything to a page
  • You need to inject JavaScript or CSS that runs globally per page load

Do not use an application customizer when:

  • The feature should be optional per-page (use a web part)
  • You need to customise a list column cell (use a field customizer)

Key characteristics:

  • Deployed via the App Catalog and provisioned with a UserCustomAction on a site or tenant
  • Cannot render arbitrary content zones on the page — only Top and Bottom placeholders
  • Runs for every user on every modern page within its scope
  • One application customizer per feature definition; multiple can coexist on the same site
  • SharePoint injects the placeholder <div> — your code renders into it via ReactDOM.render

Real examples: Global navigation mega-menu, sticky header with breadcrumb, GDPR cookie banner, tenant-wide notification bar, footer with corporate links.

// Accessing the Top placeholder in an Application Customizer
const topPlaceholder = this.context.placeholderProvider.tryCreateContent(
  PlaceholderName.Top,
  { onDispose: this._onDispose }
);

if (topPlaceholder) {
  ReactDOM.render(React.createElement(GlobalNavBar, { context: this.context }), topPlaceholder.domElement);
}

🏷️ Field Customizer

A field customizer replaces the default cell renderer for a specific list column. Instead of showing plain text or a date, you render any React component inside each cell.

Use a field customizer when:

  • You want to show a status badge, a coloured pill, an icon, a sparkline, or a progress bar in a list column
  • The column value needs to be formatted beyond what JSON column formatting can do
  • Your rendering logic requires external data, complex conditionals, or interactive elements (tooltips, expand/collapse)

Do not use a field customizer when:

  • JSON column formatting is sufficient — it's faster to deploy, requires no app catalog, and is editable by power users directly in the list settings. JSON formatting handles the vast majority of colour-coding, icon, and badge scenarios. Only reach for a field customizer when you genuinely need TypeScript logic or React components.
  • You need something on every page (use an application customizer)
  • You need a toolbar action (use a command set)

Key characteristics:

  • Registered on a specific list column via ClientSideComponentId in the field's schema XML or via PnPjs/PowerShell provisioning
  • Receives this.event.fieldValue (the raw column value) and this.event.listItem (the full item) as context
  • Renders in every row of list view for that column — keep rendering fast and side-effect-free
  • Each column instance is independent; multiple field customizers can run on the same list view

Real examples: Traffic-light status indicator, file size formatted as KB/MB/GB, person card with presence indicator, date displayed as relative time ("3 days ago"), clickable rating stars.

// Field Customizer render method
public renderCell(): void {
  const value = this.event.fieldValue;
  const element = React.createElement(StatusBadge, { status: value });
  ReactDOM.render(element, this.event.domElement);
}

⚡ Command Set (List View Command Set)

A command set adds custom buttons to the command bar and right-click context menu of a list or library. It fires an action when the user selects one or more items and clicks the button.

Use a command set when:

  • You need a "do something with selected items" action — bulk edit, bulk move, export, send for approval
  • The action is contextual — enabled only when certain items are selected, or only for certain users
  • You want a custom action in the list toolbar that is not available natively

Key characteristics:

  • Registered via ClientSideComponentId on a list or library (or via site/tenant-scoped deployment)
  • Receives this.context.listView.selectedRows — the currently selected list items
  • Can control button visibility and enabled state dynamically via onListViewUpdated
  • Opens panels, dialogs, or redirects — does not render inline in the list

Real examples: "Submit for approval" button on document library, bulk-tag selected items, export selected rows to Excel, copy items between libraries.


🤔 Decision Guide

Use this table as a quick reference when a new requirement lands:

RequirementReach for
Drop a component onto a specific pageWeb Part
Appears on every page automaticallyApplication Customizer
Add a global header or footerApplication Customizer
Change how a list column looksField Customizer (or JSON formatting first)
Add a button to the list toolbarCommand Set
Action on selected list itemsCommand Set
Per-page configuration by editorsWeb Part
Works in Teams / Viva / OutlookWeb Part (with manifest changes)

🔀 Combining Types

Real-world solutions often combine types. A document management solution might use:

  • A web part for the document request submission form on a page
  • An application customizer for a persistent "My Requests" notification badge in the header
  • A field customizer to show a coloured approval status in the document library
  • A command set to let users bulk-submit selected documents for review

Each type handles what it is designed for. The combination covers the full experience without forcing any one type to do a job it was not built for.


✅ Summary

  • Web part — optional, page-by-page, editor-controlled component inside a content zone.
  • Application customizer — automatic, page-level, header/footer injection across all pages in a site or tenant.
  • Field customizer — custom cell renderer for a specific list column; replaces the default display with any React component.
  • Command set — toolbar and context menu actions on list and library items.
  • When in doubt: if it goes on a page voluntarily → web part. If it must be everywhere → application customizer. If it changes a column cell → field customizer (or JSON formatting). If it acts on selected rows → command set.

Choosing the right type upfront saves you from a painful refactor halfway through the project.

Happy coding!

Ad image