The real deciding factor between a tenant-wide app catalog and a site collection app catalog isn’t governance philosophy — it’s one hard technical constraint that makes the decision for you in a specific, common scenario: only the tenant app catalog can sync an SPFx solution to the Microsoft Teams app store. Everything else genuinely is a judgment call; this one specifically isn’t.
In this post: The Teams-sync constraint · Who can actually set each one up · Deploying to the tenant catalog · Enabling a site collection catalog · What’s worth knowing about conflicts · The real decision table · A worked example · Related reading
The Teams-sync constraint
Worth stating precisely rather than as a general “tenant catalog is better for Teams” impression: the “Sync to Teams” button that pushes an SPFx solution’s Teams-tab manifest into the Microsoft Teams app store only exists in the tenant app catalog’s ribbon. A site collection app catalog has no equivalent action — if the SPFx solution includes a Teams tab component and that component genuinely needs to reach the Teams app store, the tenant catalog isn’t the better option, it’s the only one that works at all. Worth checking this specifically before building anything with a Teams-tab component targeted at a site-scoped deployment.
Enabling a site collection app catalog only needs Site Collection Administrator rights, not SharePoint Administrator — a real, practical reason a development team can self-serve a test environment without waiting on the tenant admin queue.
Who can actually set each one up
Worth knowing this permission distinction concretely, since it changes who’s actually blocked waiting on whom: creating or uploading to the tenant app catalog needs SharePoint Administrator (or Global Administrator) rights, full stop. Enabling a site collection app catalog needs only Site Collection Administrator rights on that specific site — a meaningfully lower bar that a development team lead can often satisfy themselves, without opening a ticket to the tenant admin team for every dev/test iteration.
Deploying to the tenant catalog
Connect-PnPOnline -Url "https://yourtenant-admin.sharepoint.com" -Interactive
Add-PnPApp -Path "./your-solution.sppkg" -Scope Tenant -Publish
Set-PnPApp -Identity "your-solution" -Scope Tenant -Enabled
`-Publish` deploys the app immediately after adding it — worth leaving this off in a controlled rollout and running `Publish-PnPApp` as a separate, deliberate step once the package has actually been reviewed, rather than defaulting to an immediate, un-gated publish for anything going tenant-wide.
Enabling a site collection catalog
Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/devkudos" -Interactive
Add-PnPSiteCollectionAppCatalog -Site "https://yourtenant.sharepoint.com/sites/devkudos"
Add-PnPApp -Path "./your-solution.sppkg" -Scope Site -Publish
`Add-PnPSiteCollectionAppCatalog` is the current, cross-platform PnP equivalent of the older `Add-SPOSiteCollectionAppCatalog` (SharePoint Online Management Shell) — both still work, but PnP PowerShell’s version is worth defaulting to for consistency with everything else in a typical deployment script, since it runs the same way on Windows, macOS, and Linux.
What’s worth knowing about conflicts
Worth knowing this precedence rule before it causes a confusing “why is the old version still showing” investigation: when the same solution exists in both the tenant catalog and a site’s own site collection app catalog, the site collection version wins on that specific site, even if the tenant version is newer. This is genuinely useful during a staged rollout — a site can pin an older, known-stable version locally while the tenant catalog moves ahead to a newer one for everyone else — but it’s also the exact mechanism behind a common support confusion: a tenant-wide update that seems to apply everywhere except one specific site, where a leftover site-scoped copy from an old test deployment is silently still winning. Worth checking a site’s own app catalog for a stale duplicate before assuming a tenant-wide update failed to propagate.
Upgrading an already-deployed app in either catalog is the same underlying pattern regardless of scope — upload the new package version, then run `Update-PnPApp` (or let `-Publish` on `Add-PnPApp` handle it, since it upgrades in place if the app already exists) to push existing installations to the new version rather than leaving them pinned to whatever version was installed originally.
The real decision table
| Scenario | Tenant catalog | Site collection catalog |
|---|---|---|
| Solution includes a Teams tab that needs the Teams app store | Required — the only option | Not possible at all |
| Company-wide rollout, production-ready | Yes | No — would need per-site duplication |
| Dev/test iteration without a tenant-admin ticket | Slower — needs SharePoint Admin | Faster — Site Collection Admin is enough |
| Department-specific tool, never leaving that site | Overkill | Right fit |
| Central version control across the org | Easier — one catalog to track | Harder — risk of drift between per-site copies |
A worked example
Building an employee-recognition (“Kudos”) SPFx web part, no Teams tab component involved:
- Enable a site collection app catalog on a dev/test site and deploy there first — fast iteration, no tenant-admin dependency for every build.
- Once validated, deploy the finished package to the tenant catalog for the actual org-wide rollout, retiring the site-scoped test deployment rather than leaving both versions live and drifting apart.
If the same web part later grows a Teams tab, the tenant-catalog deployment step stops being optional — worth knowing that in advance if a Teams integration is even a plausible future direction for the solution, since it changes what “production” deployment has to look like from the start.
Retiring the site-scoped test deployment once the tenant-wide one is live is worth doing explicitly rather than leaving it in place indefinitely — given the precedence rule above, a forgotten site-scoped copy is exactly what causes a future tenant-wide update to silently not take effect on that one site:
Remove-PnPApp -Identity "your-solution" -Scope Site
Related reading
- Create SharePoint Site Collection App Catalog — a full walkthrough of the site-scoped setup covered briefly here.
- Start Your SharePoint with SPFx — the current Heft-based build toolchain the `.sppkg` package in this post’s examples comes from.
Most of this decision is genuinely about tradeoffs — speed versus governance, autonomy versus drift risk. The Teams-sync limitation isn’t a tradeoff, though; it’s a hard constraint that settles the question outright the moment a Teams tab component is actually part of the solution.
App Catalog Authentication Automation Backup Compliance Content Type CSS Flows Google Javascript Limitations List Metadata MFA Microsoft Node NodeJs O365 OneDrive Permissions PnP PnPJS Policy PowerApps Power Automate PowerAutomate PowerPlatform PowerShell React ReactJs Rest API Rest Endpoint Security Send an HTTP Request to SharePoint SharePoint SharePoint List SharePoint Modern SharePoint Online SPFX SPO Sync Tags Teams Termstore Versioning


