Skip to content

Deployment guide

From build to app catalog to page to Teams/Outlook.

Build outputs are covered in building-packages.md; ZIP layout in reference/FORMATS.md; file paths in project-structure.md.

Pipeline

rspfx build     → dist/ + release/manifests + release/assets   (no .sppkg)
rspfx package   → sharepoint/solution/<name>.sppkg            (implies build)
rspfx deploy    → upload .sppkg to app catalog via REST        (implies package)
CommandProducesWhen
rspfx builddist/<bundle>.js + release/manifests/*.manifest.json + release/assets/*CI intermediate, rspfx analyze
rspfx packagesharepoint/solution/<name>.sppkg from paths.zippedPackageShip
rspfx package --no-buildSame .sppkg from existing release/Incremental
rspfx deployPackages then POST to catalogAutomated upload

npm scripts build / package proxy to rspfx build / rspfx package — use your package manager (pnpm build, npm run build, etc.) as selected elsewhere on this site.

National clouds use the same pipeline — only the catalog hostname differs.

Tip: For most web parts leave includeClientSideAssets: true and cdnBasePath: "" — simplest, no external CDN.

1. Build

npm ci
yaml
rspfx doctor              # Node ≥20, config loads, sp-* versions match, port free
rspfx build               # minified, no sourcemap by default
# debuggable:
rspfx build --no-minify --sourcemap
# native bundler (identical output):
npx rspack build --mode production
npx vite build
npx rsbuild build

Emits dist/<bundle>.js (AMD define('<id>_<version>', …)), release/manifests/<id>.manifest.json (with entryModuleId and internalModuleBaseUrls), and release/assets/*.

See building-packages.md.

2. Package

sh
rspfx package   # or bun run package / pnpm package / npm run package
unzip -l sharepoint/solution/<name>.sppkg | head -20

Expect [Content_Types].xml, _rels/.rels, AppManifest.xml, feature_<id>.xml, <featureId>/WebPart_<id>.xml (or Extension_ / Library_), and ClientSideAssets/ when includeClientSideAssets is true.

ZIP layout: reference/FORMATS.md.

3. Choose catalog and skipFeatureDeployment

CatalogURL patternScope
Tenant App Cataloghttps://{tenant}.sharepoint.com/sites/appcatalogEvery site — recommended
Site Collection App Cataloghttps://{tenant}.sharepoint.com/sites/<site>/_layouts/15/tenantAppCatalog.aspxSingle site only

config/package-solution.json solution.skipFeatureDeployment:

  • true (scaffold default) — tenants see Deploy on upload; solution available on all sites without per-site Add an app; required for Sync to Teams.
  • false — each site owner must Site Contents → Add an app → <solution>; use for staged rollouts.

Microsoft docs: site collection app catalog · tenant-scoped deployment · app catalog.

4. Upload to app catalog

Manual

  1. Open https://{tenant}.sharepoint.com/sites/appcatalogSite ContentsApps for SharePoint.
  2. Drag-drop sharepoint/solution/<name>.sppkg — confirm Replace if version exists.
  3. In the dialog check Enable this app and add it to all sites (when skipFeatureDeployment: true).
  4. Click Deploy — catalog shows Deployed / Enabled.

PowerShell:

powershell
Connect-PnPOnline -Url https://contoso-admin.sharepoint.com -Interactive
Add-PnPApp -Path ./sharepoint/solution/my-app.sppkg -Publish -SkipFeatureDeployment -Overwrite

Automated — rspfx deploy

sh
export RSPFX_ACCESS_TOKEN='<bearer-token>'        # Entra token with Sites.Manage.All
export RSPFX_APP_CATALOG_URL='https://contoso.sharepoint.com/sites/appcatalog'
rspfx deploy
  • Catalog URL: config.deploy.appCatalogSiteUrlRSPFX_APP_CATALOG_URL → prompt.
  • Bearer token: RSPFX_ACCESS_TOKEN — 120 s timeout.
  • Without a token prints manual steps and exits 0 (so CI without creds still succeeds).

See commands.md and real-tenant-validation.md.

Tip: Store the token as a CI secret; rspfx deploy works headless when both env vars are set.

5. Approve API permissions

If config/package-solution.json solution.webApiPermissionRequests is non-empty:

  1. Go to https://{tenant}-admin.sharepoint.com/_layouts/15/online/AdminHome.aspx#/webApiPermissionManagement.
  2. Find pending request → Approve.

Until approved, MSGraphClient / AadHttpClient return 403.

Docs: use AadHttpClient.

6. CDN considerations

includeClientSideAssetscdnBasePathBehavior
true (default)""Bundles embedded in .sppkg under ClientSideAssets/; manifests use HTTPS://SPCLIENTSIDEASSETLIBRARY/ — SharePoint serves them; no external CDN needed
false""No ClientSideAssets/ — load fails unless you self-host; avoid
false or true"https://cdn.contoso.com/<name>/"Manifests use that URL; upload release/assets/* to that CDN
true + non-empty cdnBasePathNon-emptyCDN URL wins; ClientSideAssets/ still embedded but manifests point to CDN

cdnBasePath must end with / — toolchain appends one if missing.

Schema: write-manifests · Docs: CDN hosting.

Tip: Stay on the first row unless you have an external CDN requirement — one fewer moving part.

7. Add to site and page

  1. After catalog Deploy, go to https://{tenant}.sharepoint.com/sites/<test>Site ContentsAdd an app → your solution → Add (skip when skipFeatureDeployment: true).
  2. Edit a page → + → search by preconfiguredEntries[0].titleAdd → configure property pane → Republish.
  3. Verify in DevTools → Network — bundle URLs should be https://{tenant}.sharepoint.com/sites/appcatalog/ClientSideAssets/<bundle>.js (or CDN URL).

8. Teams and Outlook

When teams/manifest.json was present at rspfx package time (auto-included under ClientSideAssets/teams/), the catalog shows Sync to Teams.

  1. Catalog → select app → Sync to Teams (or Teams Admin Center → Manage apps → Upload).
  2. Teams Admin Center → find app → Allowed + permission policy.
  3. Users: Teams → Apps → Built for your orgAdd; with personal scope the same app appears in new Outlook (Outlook → Apps → Built for your org) after 10–120 min sync.

Required teams/manifest.json fields:

  • id and staticTabs[0].entityId must equal the SharePoint component id — see project-structure.md.
  • validDomains must include *.sharepoint.com, *.office.com, etc. — scaffold list in packages/templates.
  • contentUrl uses TeamsLogon.aspx?SPFX=true&dest=teamshostedapp.aspx%3F...%26componentId=<id>.

Full reference: teams-outlook-install.md · Docs: Teams SPFx · Sync to Teams.

Tip: For Outlook add *.outlook.office.com to validDomains manually — scaffold omits it.

9. Update and rollback

  • Update: bump package.json version + config/package-solution.json solution.version (4-part) → rspfx package → re-upload → ReplaceDeploy.
  • Rollback: re-upload previous .sppkg (catalog keeps one version).
  • Remove: catalog → Remove (clear Recycle Bin) + Teams Admin → Block; pages show missing-web-part placeholder.

10. Env vars and serve.json tokens

config/serve.json string values support dotenv + shell expansion — see commands.md#environment-variables and project-structure.md.

Key tokens:

  • ${VAR} / ${VAR:-default} / $VAR — replaced from env / .env.
  • {tenantdomain} in initialPage — replaced by dev.tenantUrl / SPFX_SERVE_TENANT_DOMAIN / --tenant.

Dev server runs at https://localhost:4321 (SharePoint mode) or http://localhost:4321 (local preview) — see getting-started.md and commands.md.

11. CI

yaml
steps:
npm ci
yaml
  - run: rspfx doctor
  - run: rspfx package
  - upload: sharepoint/solution/*.sppkg
  # or automated:
  # - run: RSPFX_ACCESS_TOKEN=${{ secrets.SPFX_TOKEN }} RSPFX_APP_CATALOG_URL=https://contoso.sharepoint.com/sites/appcatalog rspfx deploy

Cache node_modules and .rspack-cache between runs.

Comparison vs official

AreaOfficialRSPFx
Package pathpaths.zippedPackage in package-solution.jsonSame — read directly
CDNdeploy-azure-storage.json + cdnBasePathwrite-manifests.json cdnBasePath only — simpler
UploadManual or PowerShell / PnPrspfx deploy with bearer token, or manual
API permissionsSame webApiPermissionRequestsAppManifest.xmlSame
Teams syncSame teams/ auto-detectionSame

Troubleshooting

SymptomFix
No web part or extension bundles foundsrc/webparts/<name>/ missing *.manifest.json or entrypoint — see project-structure.md
Bundle 404 in workbench / pageBundle name vs entryModuleId mismatch — default: folder name = bundle key
External 'X' could not be resolvedexternals key not in node_modules — remove or add X with your package manager (npm i X / pnpm add X / yarn add X / bun add X / deno add npm:X)
Can't resolve 'XxxWebPartStrings'localizedResources pattern must contain {locale}
paths.zippedPackage missingpackage-solution.json must have solution.id, solution.name, paths.zippedPackage
Catalog Sync to Teams missingteams/ not at package time, or includeClientSideAssets: false, or skipFeatureDeployment: false
Invalid Teams manifestteams/manifest.json id ≠ component id, or validDomains missing *.sharepoint.com
App in Teams but not OutlookWait 10–120 min sync, check personal scope, use new Outlook, add *.outlook.office.com
API 403Approve in Admin API access
DEPLOY_TIMEOUTCatalog throttling or large .sppkg — retry (120 s timeout)
{tenantdomain} literal in URLNo tenant configured — set dev.tenantUrl or SPFX_SERVE_TENANT_DOMAIN or --tenant
IsValidAppPackage: falseSharePoint OPC parser rejected package — verify AppManifest.xml GUID formatting and ZIP rels — see reference/FORMATS.md

For internals see building-packages.md and real-tenant-validation.md.

Released under the MIT License.