SharePoint Intermediate · Updated on

Deploy an SPFx solution to the app catalog

Run npm run build: with SPFx 1.24, Heft writes the .sppkg file to sharepoint/solution. Upload it to the tenant app catalog or a site collection app catalog, then select Enable app. Have the API permissions approved on the API access page. To ship an update, increment the version and upload the package again.

Before you start

  • An SPFx 1.24 project generated with the Yeoman generator and the Heft toolchain.
  • Node.js 26 LTS (or 22 / 24) installed on the build machine.
  • The SharePoint Administrator role, or an administrator account for the target site collection app catalog.
  • To approve Microsoft Graph permissions: a Global Administrator.

Step 1: Check the project toolchain

Since SPFx 1.22, new projects use Heft instead of gulp. With SPFx 1.24, released on October 28, 2026, the gulp toolchain is no longer supported.

Open package.json and read the scripts. A standard 1.24 project defines build as heft test --clean --production && heft package-solution --production.

A project still on 1.21 or earlier uses gulp bundle --ship followed by gulp package-solution --ship. With Heft, the --ship option becomes --production.

# Show the npm scripts defined in the project
npm pkg get scripts

Check: The build script calls heft, not gulp.

Step 2: Configure package-solution.json

The config/package-solution.json file describes the package. The solution.version field uses the four-part format 1.0.0.0.

includeClientSideAssets: true bundles the JavaScript and CSS into the .sppkg. SharePoint then serves them from the app catalog, or from the Office 365 CDN if it is enabled.

skipFeatureDeployment: true allows tenant-wide deployment. The administrator can then select "Enable this app and add it to all sites". In that case, Feature Framework definitions (lists, fields) are ignored.

{
  "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
  "solution": {
    "name": "my-webpart-client-side-solution",
    "id": "<solution GUID>",
    "version": "1.0.0.0",
    "includeClientSideAssets": true,
    "skipFeatureDeployment": true,
    "isDomainIsolated": false
  },
  "paths": {
    "zippedPackage": "solution/my-webpart.sppkg"
  }
}

Check: The file contains a stable id, a version and the intended value for skipFeatureDeployment.

Step 3: Declare API permissions

If the solution calls Microsoft Graph or an API secured with Entra ID, add webApiPermissionRequests to the solution object.

Add one entry per resource / scope pair. The resource value is the display name of the Entra ID application, not its objectId: an objectId causes an error at approval time.

Deployment creates pending requests. Whether they are approved or not, the solution can still be deployed: the code must never assume that access has been granted.

"webApiPermissionRequests": [
  { "resource": "Microsoft Graph", "scope": "Calendars.Read" },
  { "resource": "Microsoft Graph", "scope": "User.ReadBasic.All" }
]

Check: Each required scope has its own entry.

Step 4: Build the production package

Stop the local server (heft start), then run the build. The script runs the tests, compiles in production mode and packages the solution.

The .sppkg is written to sharepoint/solution, with the name set in paths.zippedPackage. The raw contents are visible in sharepoint/solution/debug.

npm run build

# Explicit equivalent described in the Microsoft tutorial
npx heft build --production
npx heft package-solution --production

ls sharepoint/solution/*.sppkg

Check: A freshly timestamped .sppkg file exists in sharepoint/solution.

Step 5: Choose the app catalog: tenant or site collection

The tenant app catalog makes the solution installable in every site collection. Open it from the SharePoint admin center: More features, then Open under Apps.

A site collection app catalog limits the solution to that site only. An administrator enables it with Add-SPOSiteCollectionAppCatalog, Add-PnPSiteCollectionAppCatalog or m365 spo site appcatalog add. A tenant app catalog must already exist.

In a site collection app catalog, the "add to all sites" option only makes the solution available within that site collection.

Connect-SPOService -Url https://contoso-admin.sharepoint.com
Add-SPOSiteCollectionAppCatalog -Site https://contoso.sharepoint.com/sites/marketing

Check: The "Apps for SharePoint" library appears in the target site, or the Manage apps page opens for the tenant.

Step 6: Upload and enable the solution

On the Manage apps page, select Upload and choose the .sppkg. The Enable app panel opens.

If skipFeatureDeployment is true, select "Enable this app and add it to all sites" for a tenant-wide deployment. Otherwise, each site owner adds it through Add an app.

From the command line, the CLI for Microsoft 365 separates adding and deploying. PnP PowerShell does both with Add-PnPApp -Publish.

m365 login
m365 spo app add --filePath ./sharepoint/solution/my-webpart.sppkg --overwrite
m365 spo app deploy --name my-webpart.sppkg --skipFeatureDeployment

# PnP PowerShell equivalent
# Add-PnPApp -Path ./sharepoint/solution/my-webpart.sppkg -Overwrite -Publish -SkipFeatureDeployment

Check: The "App Package Error Message" column is empty and the component appears in a page's toolbox.

Step 7: Approve API permissions

Open the API access page in the SharePoint admin center. Select each pending request, then Approve.

Delegated permissions are granted to the "SharePoint Online Client Extensibility Web Application Principal". They apply to every SPFx solution in the tenant, not only to the one that requested them.

Microsoft Graph requires a Global Administrator. For a third-party API registered in the tenant, the Application Administrator role is enough.

m365 spo serviceprincipal permissionrequest list
m365 spo serviceprincipal permissionrequest approve --id <request-id>
m365 spo serviceprincipal grant list

Check: The request moves to the approved requests list and the API call responds without a 403 error.

Step 8: Ship a new version

Increment solution.version in package-solution.json (for example from 1.0.0.0 to 1.1.0.0), keep the same id, rebuild, then upload the .sppkg again, replacing the existing one.

For a tenant-wide solution, the new package updates every instance. For a solution installed site by site, apply the update with m365 spo app upgrade or Update-PnPApp.

If the solution provisions assets through the Feature Framework, also increment the feature version and declare upgradeActions.

npm run build
m365 spo app add --filePath ./sharepoint/solution/my-webpart.sppkg --overwrite
m365 spo app deploy --name my-webpart.sppkg
m365 spo app upgrade --id <app-id> --siteUrl https://contoso.sharepoint.com/sites/marketing

Check: The app catalog shows the new version number and the page loads the new files.

Common errors

After moving to SPFx 1.24, gulp bundle --ship fails or no longer exists.
The gulp toolchain is no longer supported in 1.24. Migrate the project to Heft, CI commands included, and use --production instead of --ship.
The API request cannot be approved and shows an error about the resource.
Set resource to the display name of the Entra ID application, not its objectId, then redeploy the package.
Duplicates appear in the Tenant Wide Extensions list after an extension update.
Do not select "add to all sites" again during an update: this option is only for the initial activation of extensions.
The list or field defined in the solution is not created.
With skipFeatureDeployment: true, Feature Framework definitions are ignored. Set the value to false and install the solution site by site.

Sources

Questions

Frequently asked questions

Do I need a specific SPFx version to use Heft?

Heft has been the default toolchain since SPFx 1.22. In 1.24, it is the only one supported. Projects on 1.21 and earlier stay on gulp until they are migrated.

Does removing the solution revoke its API permissions?

No. Granted permissions stay active. An administrator must revoke them from the API access page or with `m365 spo serviceprincipal grant revoke`.

Tenant app catalog or site collection app catalog?

The tenant app catalog suits solutions used across several sites. A site collection app catalog isolates a solution in a single site collection and delegates its management to that site's administrators.

Can deployment be automated in a pipeline?

Yes. The CLI for Microsoft 365 (`m365 spo app add` then `m365 spo app deploy`) and PnP PowerShell (`Add-PnPApp -Publish`) can be scripted. They are open source tools with no Microsoft SLA.

Need a hand?

We can do it with your team, or train your team to do it on its own.