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
| Verb | Meaning | When you need it |
|---|---|---|
| Add | Upload the .sppkg file into the catalog | First deployment, and every new version |
| Deploy | Make the app available (tenant-wide or catalog-wide) | After add โ it won't appear anywhere until deployed |
| Install | Add the app to a specific site (enables its site-scoped features) | Per site that should use the web parts/extensions |
| Upgrade | Move installed sites to a newer deployed version | Every release after the first |
| Retract | Undeploy โ app stays in catalog but is no longer available | Rollbacks / end of life |
| Remove | Delete the app from the catalog entirely | Cleanup |
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 andupgradeuse 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?