logo
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:

  1. SharePoint creates the site
  2. The site design is applied
  3. Each referenced site script runs in sequence
  4. SPFx extensions are registered via the associateExtension action

πŸ“‹ 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 site
  • 1 β€” 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

VerbPurpose
installSolutionInstall the .sppkg from the App Catalog on the site
associateExtensionRegister an Application Customizer or Command Set
associateFieldCustomizerRegister a Field Customizer on a specific column (inside createSPList or setSPFieldCustomFormatter)
applyThemeApply a tenant theme
createSPListCreate a list with columns, views, and extension registrations

βœ… Summary

  • Site scripts automate SPFx extension provisioning β€” no manual PowerShell per site.
  • Use installSolution to activate the .sppkg on the site, then associateExtension to 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-PnPSiteDesign to apply a design to existing sites for bulk rollout.
  • To update ClientSideComponentProperties on existing registrations, iterate sites with Get-PnPCustomAction and Set-PnPCustomAction.

Happy coding!

Ad image