The App Catalog & Package Lifecycle

Lesson 7: The App Catalog & Package Lifecycle

Everything you build finally becomes a .sppkg โ€” a zip-like SharePoint package holding your bundled code, manifests, and assets. That single file is what moves through the deployment lifecycle, and the app catalog is the staging ground where it lives. Understanding these states is what makes CI/CD (next lesson) feel obvious instead of magical.

Where packages live: the app catalog

  • Tenant app catalog โ€” a special "Apps for SharePoint" site for the whole tenant. The default destination for SPFx solutions; apps here can be deployed tenant-wide. Enabled once in the SharePoint admin center (or via CLI/PowerShell โ€” it's just a site with a flag).
  • Site collection app catalog โ€” an optional catalog scoped to one site collection, for when a solution must not be visible tenant-wide (e.g. a customer-specific customization in a multi-tenant-style setup).

The lifecycle states

add upload .sppkg to catalog deploy make available (catalog-wide) install add app to a specific site upgrade new version โ†’ re-deploy + update retract / remove
VerbMeaningWhen you need it
AddUpload the .sppkg file into the catalogFirst deployment, and every new version
DeployMake the app available (tenant-wide or catalog-wide)After add โ€” it won't appear anywhere until deployed
InstallAdd the app to a specific site (enables its site-scoped features)Per site that should use the web parts/extensions
UpgradeMove installed sites to a newer deployed versionEvery release after the first
RetractUndeploy โ€” app stays in catalog but is no longer availableRollbacks / end of life
RemoveDelete the app from the catalog entirelyCleanup

The package-solution.json flags that matter

Your deployable identity lives in config/package-solution.json. The scaffolded file with the interesting bits:

{
  "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
  "solution": {
    "name": "my-company-announcements-client-side-solution",
    "id": "b1c3f2a4-0000-4a1e-9c9d-1234567890ab",
    "version": "1.0.0.0",
    "includeClientSideAssets": true,
    "isDomainIsolated": false,
    "skipFeatureDeployment": true,
    "developer": {
      "name": "Your Company",
      "websiteUrl": "https://github.com/your-org/your-repo",
      "privacyUrl": "https://contoso.com/privacy",
      "termsOfUseUrl": "https://contoso.com/terms",
      "mpnId": ""
    }
  },
  "paths": { "zippedPackage": "solution/my-company-announcements.sppkg" }
}
  • id โ€” the solution's identity (separate from each component's manifest id). Fixed for life.
  • version โ€” bump this on every release. It's what the catalog and upgrade use to detect "newer."
  • includeClientSideAssets: true โ€” your JS bundles get hosted by SharePoint itself (inside the app), so you don't need a separate CDN. Keep it true unless you deliberately use your own host.
  • skipFeatureDeployment: true โ€” "deploy tenant-wide, no per-site install needed for availability." Perfect for pure web-part libraries and global extensions. When false (default), the app must be installed per site to light up its features.
  • developer โ€” surfaces in the catalog UI; nice place to point back at your GitHub repo. ๐Ÿ˜‰
Where the magic "install" step lives: whether an app needs per-site install depends on skipFeatureDeployment and on the components inside. Web parts typically work tenant-wide once deployed; application customizers run per site collection after the app is installed there. Your deployment script (next lesson) must encode that choice deliberately.
Version discipline: uploading a changed .sppkg with the same solution version overwrites the package but sites that already installed the old one won't reliably pick it up โ€” that's what an upgrade (triggered by a version bump) is for. Never ship twice on one version number.

๐Ÿง  Knowledge Check

1. Which action makes a just-uploaded app actually available to sites?

2. What does skipFeatureDeployment: true mean in package-solution.json?

3. Your users report an old version still loads after you re-uploaded a fixed .sppkg with the same solution version. What's the likely cause?

Further Reading