Command Sets and Application Customizers scope to specific list templates the same way, through `RegistrationType`/`RegistrationId` in `elements.xml`. Field Customizers don’t — they’re registered against a specific column, not a list template, a genuinely different mechanism worth knowing before assuming one pattern covers all three extension types this post touches on.
In this post: Scoping a Command Set to a list template · Common list template IDs · Field Customizers work differently · Application Customizers: the third type · A worked example · Tradeoffs · Related reading
Scoping a Command Set to a list template
`elements.xml` defines the deployment scope via a `CustomAction` element — `RegistrationType=”List”` combined with `RegistrationId` set to the target list template’s numeric ID restricts where the extension shows up:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Title="My Custom Command Set"
RegistrationType="List"
RegistrationId="101"
Location="ClientSideExtension.ListViewCommandSet.CommandBar"
ClientSideComponentId="GUID_OF_YOUR_COMPONENT">
</CustomAction>
</Elements>
Worth a direct correction to a syntax mistake that shows up in a lot of examples of this pattern: an XML comment (`<!– … –>`) can’t sit inside a tag’s attribute list the way `RegistrationId=”100″ <!– List template ID –>` sometimes gets written — that’s invalid XML, not just unconventional. Comments belong between elements, never inside an opening tag itself; the corrected example above drops the inline comment entirely rather than placing it incorrectly.
To target several list templates from one extension instead of a single `RegistrationId`, `ClientSideComponentProperties` accepts a `listTemplateIds` array — worth remembering this value is a JSON string embedded inside an XML element, not XML itself, so it needs valid JSON syntax inside the quotes and no XML comment syntax mixed into it:
<ClientSideComponentProperties>{"listTemplateIds": ["100", "101"]}</ClientSideComponentProperties>
Common list template IDs
| Template ID | Template Name |
|---|---|
| 100 | Generic List (Custom List) |
| 101 | Document Library |
| 102 | Survey |
| 103 | Links |
| 104 | Announcements |
| 105 | Contacts |
| 106 | Events (Calendar) |
| 107 | Tasks |
| 108 | Discussion Board |
| 109 | Picture Library |
For the complete, authoritative list, Microsoft’s own list templates schema reference is worth checking directly rather than a partial table like this one, since the full list runs well past a dozen entries. A genuinely custom list definition provisioned through a SharePoint Feature uses its own Type ID, defined in that feature’s own manifest — not necessarily one of the common base-type IDs in the table above. Worth checking the specific custom definition’s actual Type ID directly rather than assuming it maps to a familiar number from this list.
RegistrationType and RegistrationId scope Command Sets and Application Customizers to a list template. Field Customizers don’t use this mechanism at all — they’re registered against a specific column instead.
Field Customizers work differently
Worth a direct correction: Field Customizers don’t use `RegistrationType`/`RegistrationId` at all — they’re registered against a specific column, not a list template, via a `Field` element in `elements.xml` (when provisioning a new column) or by setting the `ClientSideComponentId` property directly on an existing column through PnP PowerShell or the REST API. A Field Customizer genuinely doesn’t know or care what list template it’s attached to; it renders wherever that specific column appears, which could be one list or many, depending on where the column itself is used — a meaningfully different scoping model from the list-template-based approach Command Sets and Application Customizers use.
Application Customizers: the third type
Application Customizers — used for injecting a header, footer, or other page-wide UI element — follow the same `RegistrationType`/`RegistrationId` list-template scoping as Command Sets, registered the same way via a `CustomAction` in `elements.xml`. The distinction from Command Sets is where they render (`ClientSideExtension.ApplicationCustomizer` locations, like `Top` and `Bottom` placeholders, rather than the command bar), not how they’re scoped to a list template — worth knowing the scoping mechanism is shared across two of the three extension types, and genuinely different for the third (Field Customizers, covered above).
Whatever gets set in `ClientSideComponentProperties` is read at runtime through `this.properties` inside the extension’s own code — worth confirming the property names used in the JSON match exactly what the TypeScript interface expects, since a mismatch here fails silently at runtime (the property comes back `undefined`) rather than as a deployment-time error. Case sensitivity matters too — `listTemplateIds` and `ListTemplateIds` are different property names to a JSON parser, even though they read as the same thing to a person skimming the file.
A worked example
A Command Set for managing document approvals, meant to appear only in document libraries, not every list type in the tenant:
- Set `RegistrationType=”List”` and `RegistrationId=”101″` (Document Library) in `elements.xml`.
- Add any additional configuration the command set needs via `ClientSideComponentProperties`.
- Build, package, and deploy the solution.
- Verify: the command appears in document libraries and is genuinely absent from a generic list and at least one other template beyond the target one, confirming the scoping actually excludes them rather than just confirming it includes the intended type.
Tradeoffs
Real benefits: the extension only renders where it’s actually relevant, avoiding both unnecessary processing on lists that don’t need it and UI clutter for users who’d never use it there anyway. Real cost: the scope lives in a static XML/JSON configuration baked into the deployed package, not something adjustable at runtime — extending coverage to a new list template later means updating `elements.xml` and redeploying, not a settings change. Worth testing against every list template actually in scope before considering the extension finished, since a template that behaves unexpectedly won’t surface until someone with that specific list type notices, and a mistyped template ID fails the same way as the JSON property-name mismatch above — silently, with the extension simply not appearing, rather than an error pointing at the actual cause.
Related reading
- Start Your SharePoint with SPFx — current toolchain setup for building and deploying the extension itself.
The scoping mechanism differs by extension type — list-template-based for Command Sets and Application Customizers, column-based for Field Customizers — worth confirming which one actually applies before assuming a single pattern covers all three. Getting this distinction right up front avoids the specific, common failure mode of copying a Command Set’s `elements.xml` scoping block into a Field Customizer project and wondering why it has no effect at all.
App Catalog Authentication Automation Backup Compliance Content Type CSS Flows Google Javascript Limitations List Metadata MFA Microsoft Node NodeJs O365 OneDrive Permissions PnP PnPJS Policy PowerApps Power Automate PowerAutomate PowerPlatform PowerShell React ReactJs Rest API Rest Endpoint Security Send an HTTP Request to SharePoint SharePoint SharePoint List SharePoint Modern SharePoint Online SPFX SPO Sync Tags Teams Termstore Versioning


