- 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:
| Type | Scaffolded as | Primary DOM target |
|---|---|---|
| Web Part | --extension-type webpart | A content zone on a page |
| Application Customizer | --extension-type applicationCustomizer | Page-level header / footer placeholders |
| Field Customizer | --extension-type fieldCustomizer | A single list column cell |
| Command Set | --extension-type listViewCommandSet | List / 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.propertiesbag — 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
UserCustomActionon a site or tenant - Cannot render arbitrary content zones on the page — only
TopandBottomplaceholders - 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 viaReactDOM.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
ClientSideComponentIdin the field's schema XML or via PnPjs/PowerShell provisioning - Receives
this.event.fieldValue(the raw column value) andthis.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
ClientSideComponentIdon 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:
| Requirement | Reach for |
|---|---|
| Drop a component onto a specific page | Web Part |
| Appears on every page automatically | Application Customizer |
| Add a global header or footer | Application Customizer |
| Change how a list column looks | Field Customizer (or JSON formatting first) |
| Add a button to the list toolbar | Command Set |
| Action on selected list items | Command Set |
| Per-page configuration by editors | Web Part |
| Works in Teams / Viva / Outlook | Web 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!
Author
Ravichandran@Hi_Ravichandran
