- Published on
Deploying SPFx Extensions via Site Scripts
Manually running PowerShell to register an SPFx extension on each new site is not sustainable beyond a handful of sites. In a large enterprise, you might have hundreds of team sites created every month β and your global navigation, governance extensions, or field customizers need to be present on all of them automatically.
Site scripts and site designs solve this problem. They are SharePoint's built-in provisioning mechanism β when a new site is created from a site design, the associated site script runs automatically and provisions everything you have defined, including SPFx extensions.
πΊοΈ How Site Scripts Work
A site script is a JSON document that describes a set of provisioning actions. Actions include creating lists, setting themes, installing SPFx solutions, adding custom actions (extensions), and more.
A site design is a named, bookmarkable template that references one or more site scripts. When a user creates a new SharePoint site and selects a site design, all referenced site scripts run automatically.
The provisioning order is:
- SharePoint creates the site
- The site design is applied
- Each referenced site script runs in sequence
- SPFx extensions are registered via the
associateExtensionaction
π Step 1 β The Site Script JSON
Create a file site-script.json that installs your SPFx solution and registers the extensions:
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/site-design/sp-site-design-script-actions.schema.json",
"actions": [
{
"verb": "installSolution",
"id": "your-solution-id-from-package-solution.json"
},
{
"verb": "associateExtension",
"title": "GlobalNavBar",
"location": "ClientSideExtension.ApplicationCustomizer",
"clientSideComponentId": "your-component-id-from-manifest.json",
"clientSideComponentProperties": "{\"siteName\":\"Contoso Intranet\"}",
"scope": "Site"
}
],
"bindata": {},
"version": 1
}
For a field customizer registered on a specific list column:
{
"actions": [
{
"verb": "installSolution",
"id": "your-solution-id"
},
{
"verb": "createSPList",
"listName": "Projects",
"templateType": 100,
"subactions": [
{
"verb": "addSPField",
"fieldType": "Choice",
"internalName": "Status",
"displayName": "Status",
"addToDefaultView": true,
"choices": ["Not Started", "In Progress", "Blocked", "Completed"]
},
{
"verb": "associateFieldCustomizer",
"internalName": "Status",
"clientSideComponentId": "your-field-customizer-component-id",
"clientSideComponentProperties": "{}"
}
]
}
],
"bindata": {},
"version": 1
}
βοΈ Step 2 β Register the Site Script via PowerShell
Connect-PnPOnline -Url "https://contoso-admin.sharepoint.com" -Interactive
# Read the JSON file
$scriptContent = Get-Content -Path "./site-script.json" -Raw
# Register the site script
$siteScript = Add-PnPSiteScript `
-Title "Contoso Team Site β Base Configuration" `
-Description "Installs GlobalNavBar and base lists" `
-Content $scriptContent
Write-Host "Site Script ID: $($siteScript.Id)"
βοΈ Step 3 β Create the Site Design
# Create a site design that references the site script
$siteDesign = Add-PnPSiteDesign `
-Title "Contoso Team Site" `
-WebTemplate "64" ` # 64 = Team site (no group), 68 = Communication site
-SiteScripts $siteScript.Id `
-Description "Standard Contoso team site with global nav and governance extensions" `
-PreviewImageUrl "https://contoso.sharepoint.com/sites/intranet/SiteAssets/team-site-preview.png"
Write-Host "Site Design ID: $($siteDesign.Id)"
WebTemplate values:
64β Team site (no Microsoft 365 Group)68β Communication site1β Team site with Microsoft 365 Group
βοΈ Step 4 β Apply the Site Design to an Existing Site
Site designs do not automatically apply to existing sites β only to new ones created from that design. To apply to an existing site:
# Apply the site design to an already-existing site
Invoke-PnPSiteDesign `
-Identity $siteDesign.Id `
-WebUrl "https://contoso.sharepoint.com/sites/existingsite"
This triggers the site script actions against the target site β useful for rolling out a new extension to existing sites in bulk.
π Bulk Apply to All Sites
To roll out an extension to all existing team sites:
Connect-PnPOnline -Url "https://contoso-admin.sharepoint.com" -Interactive
# Get all team sites (adjust filter as needed)
$allSites = Get-PnPTenantSite -Template "STS#3" -Detailed
foreach ($site in $allSites) {
Write-Host "Applying site design to: $($site.Url)"
try {
Invoke-PnPSiteDesign `
-Identity $siteDesign.Id `
-WebUrl $site.Url
Write-Host " β
Applied"
} catch {
Write-Host " β Failed: $_"
}
}
Run this once after registering a new extension. New sites created from the design will get it automatically going forward.
π§ Updating Extension Properties in an Existing Registration
When you need to update ClientSideComponentProperties on an already-registered extension across all sites, use PnP PowerShell against each site:
$sites = Get-PnPTenantSite | Where-Object { $_.Template -eq "STS#3" }
foreach ($site in $sites) {
Connect-PnPOnline -Url $site.Url -Interactive
$action = Get-PnPCustomAction -Scope Site |
Where-Object { $_.ClientSideComponentId -eq "your-component-id" }
if ($action) {
Set-PnPCustomAction `
-Identity $action.Id `
-ClientSideComponentProperties '{"siteName":"Updated Name"}' `
-Scope Site
Write-Host "Updated: $($site.Url)"
}
}
π Site Script Verbs Reference for SPFx
| Verb | Purpose |
|---|---|
installSolution | Install the .sppkg from the App Catalog on the site |
associateExtension | Register an Application Customizer or Command Set |
associateFieldCustomizer | Register a Field Customizer on a specific column (inside createSPList or setSPFieldCustomFormatter) |
applyTheme | Apply a tenant theme |
createSPList | Create a list with columns, views, and extension registrations |
β Summary
- Site scripts automate SPFx extension provisioning β no manual PowerShell per site.
- Use
installSolutionto activate the.sppkgon the site, thenassociateExtensionto register the custom action. - Create a site design that references the site script β new sites created from this design automatically get your extensions.
- Use
Invoke-PnPSiteDesignto apply a design to existing sites for bulk rollout. - To update
ClientSideComponentPropertieson existing registrations, iterate sites withGet-PnPCustomActionandSet-PnPCustomAction.
Happy coding!
Author
Ravichandran@Hi_Ravichandran
