How to enumerate SPFx components used on SharePoint pages
Which client-side web parts are actually on the pages of this site?
30-second answer
foreach ($page in Get-PnPPage) {
foreach ($c in $page.Controls) {
[pscustomobject]@{ Page = $page.Name; WebPartId = $c.WebPartId }
}
}
Expensive by design: every page is opened and read. Scope it before running it on anything large.
What this proves
Which web part ids appear on which modern pages of this site, page by page. This is the usage side of the SPFx question: not what is available, but what is placed.
What it does not prove
- Which package a component came from.
WebPartIdidentifies the component, not the .sppkg. The product does not offer a supported join from page controls to catalog packages, so "this package is unused" cannot be honestly concluded from this data. - Anything about pages that could not be read. A page your identity cannot open is not a page without components.
PowerShell
$all = @(Get-PnPPage)
$inspected = 0
$failed = 0
$found = foreach ($page in $all) {
try {
$inspected++
foreach ($c in $page.Controls) {
[pscustomobject]@{ Page = $page.Name; WebPartId = $c.WebPartId }
}
}
catch { $failed++ }
}
"{0} pages, {1} inspected, {2} failed" -f $all.Count, $inspected, $failed
$found | Group-Object WebPartId | Sort-Object Count -Descending
Example output
11 pages, 9 inspected, 2 failed
Count Name
----- ----
6 544dd15b-cf3c-441b-96da-004d5a8cea1d
2 8c88f208-6c77-4bdb-86a0-0c47b4316588
Explanation
Keep the reconciliation line. On a real tenant a first version of this sweep reported counts that could not all be true at once (9 pages, 8 inspected, 7 unreadable), and the defect was in the accounting, not the tenant. When inspected plus failed plus skipped does not equal the total, the inventory is invalid and should say so rather than publish arithmetic that cannot happen.
Production considerations
- Cost scales with page count. Filter by modification date when sweeping, and declare what was skipped; a skipped page is not an empty page.
- Well-known Microsoft web part ids (Text, Image, and the rest of the built-ins) appear alongside custom ones. Expect most placements to be first-party.
- Site read access is enough, but individual pages can still refuse it.
References
- Get-PnPPage (PnP PowerShell)
- SharePoint Framework overview (Microsoft Learn)
