WordPress has no single complete screen listing every use of one block. A reliable audit must scan stored block content across several locations. It must also follow shared references.
Start with the block’s registered name, such as acme/notice. Search delimiter comments for candidates. Then parse those candidates and record exact locations.
Include posts, pages, custom post types, templates, parts, patterns, and widget areas. Drafts, private items, and revisions may also matter.
The complete usage map
Posts, pages, and custom types.
Synced patterns and reusable records.
Templates, parts, and widget areas.
Hooks, filters, and dynamic code.
First identify the registered block name
Inserter titles are not reliable identifiers. Several plugins can offer blocks called “Notice.” Translations and product updates can also change displayed titles.
The registered name uses a namespace and slug. Examples include core/image and acme/notice. That name appears in metadata and serialized delimiters.
Core comments omit the core/ namespace when serialized. The Image block appears as wp:image. APIs generally accept the full registered name.
Inspect the block through the code editor when uncertain. Copy its opening delimiter. Do not rely on CSS classes alone.
Why CSS classes produce false results
A class can remain after conversion to Custom HTML. Themes can also reproduce provider classes. Conversely, dynamic blocks may store no frontend class inside content.
Class searches answer a different question. They find matching text, not registered block instances. Use them only as supporting evidence.
Delimiter names provide stronger candidates. Full parsing confirms structure. Frontend crawling then verifies actual rendered output where necessary.
Use has_block() for a fast candidate check
WordPress provides has_block() for checking one post or content string. It accepts the full block name and an optional post identifier. We work through it in replacing a block across a site.
if ( has_block( 'acme/notice', $post_id ) ) {
// Record this post as a candidate.
}
The function favours speed over strict accuracy. The official reference says it does not validate structure. It also does not follow synced patterns.
That limitation makes it useful for a first pass. It is not enough for destructive migration. Confirm candidates through a proper parser.
Search stored delimiters for candidate records
A database text search can shortlist records containing a delimiter name. Search the exact serialized opener. Include a suitable namespace and surrounding syntax.
This approach can use direct SQL, WP-CLI, or application code. Read-only queries are safer initially. Return identifiers, types, statuses, and titles.
Raw searches can match code examples containing block comments. They can also miss indirect usage through synced patterns. Treat every match as a candidate.
Avoid broad database replacement tools during discovery. The audit should change nothing. Export the results before planning any mutation.
Use parse_blocks() for exact document structure
parse_blocks() converts a content string into a block tree. Each parsed record includes its name, attributes, inner blocks, and HTML fragments.
The tree makes nested usage visible. A target might sit inside Columns, Groups, templates, or custom containers. Recursive traversal finds every depth.
function count_target_blocks( array $blocks, string $target ): int {
$count = 0;
foreach ( $blocks as $block ) {
$count += ( $block['blockName'] === $target ) ? 1 : 0;
$count += count_target_blocks( $block['innerBlocks'], $target );
}
return $count;
}
The parser reference warns that full parsing can use substantial memory. Deep or attribute-heavy documents cost more. Process records in bounded batches.
Use WP_Block_Processor for streaming scans
Current WordPress versions provide WP_Block_Processor for lower-memory scans. It advances through delimiters without parsing every document detail automatically.
The processor can count block types and inspect nesting. It can also extract selected blocks for controlled changes. This suits large audits with simple matching.
$processor = new WP_Block_Processor( $post_content );
$count = 0;
while ( $processor->next_block( 'acme/notice' ) ) {
$count++;
}
The class reference documents streaming and extraction tradeoffs. Check your minimum WordPress version before depending on it.
Scan every relevant post type
Posts and pages are only two storage types. Products, portfolios, documentation, forms, and custom records can also use block content.
Query post types with editor support or known block storage. Include public and private types. Exclude technical records only after confirming their purpose.
Record the post type in the report. Migration priorities differ. A checkout page matters more than an expired draft.
Include every meaningful post status
Published content shows current visitor exposure. Drafts and scheduled posts represent future exposure. Private content can support important internal workflows.
Trash usually deserves separate reporting. Revisions can prove historical use or restore content. They should not inflate the active replacement count.
Label active, future, recoverable, and discarded records differently. One total hides operational meaning. Keep the status beside every result.
Follow synced pattern references
A page can reference shared pattern content without storing each nested block directly. A simple page scan can therefore miss the target block.
Scan synced pattern records separately. Then map which posts reference those records. Report direct and indirect usage as different relationship types.
One synced pattern can affect hundreds of pages. Count the source instance once. Also count every downstream placement for impact planning.
A cycle or broken reference should not crash the audit. Track visited identifiers. Report unresolved references for manual review.
Scan templates and template parts
Block themes store customised templates and parts as dedicated records. Theme files can also supply default block markup. Both locations matter.
A block inside a header template part can appear site-wide. Counting only one stored record understates visitor exposure. Report estimated reach separately.
Database customisations can override theme file templates. Scan the active resolved set and the source files. A future reset can reveal earlier markup.
Remember block-based widget areas
Classic themes can store blocks inside widget areas. Those records may appear through specialised storage rather than normal pages. Sidebars and footers deserve inspection.
Legacy widgets can also contain arbitrary HTML or shortcodes. A delimiter search will not identify functionally similar content. Define whether the audit targets identity or outcome.
Generated and hooked blocks need code review
Plugins and themes can inject blocks during rendering or editing. Block Hooks can add instances near anchor blocks. Those uses may not exist literally inside posts.
Search registrations, hook metadata, filters, and render callbacks in code. Inspect patterns created during activation. Ask providers about automatic insertion features.
A content-only scan cannot prove absence from generated output. State this limitation in the report. Runtime crawling can provide another evidence layer.
Frontend crawling answers a different question
A crawler can search rendered HTML for stable block classes or markers. This finds current public output. It misses drafts, hidden routes, and unrendered stored blocks.
Dynamic conditions can change results by user, date, locale, or query. Crawl representative states. Treat the findings as rendered evidence, not storage inventory.
Combine stored scans and frontend crawls for high-risk migrations. Differences reveal caches, injection, conditional visibility, or unreachable content.
Count instances and affected records separately
One page can contain many target blocks. A record count alone hides that density. An instance count alone hides the number of editing locations.
Report both figures. Add nesting depth, shared-source status, and public reach when useful. This supports migration estimates and testing priorities.
A replacement project cares about editing locations. A performance project cares about rendered instances. A deactivation project needs both.
What a useful inventory contains
- Record identifier and edit link.
- Title, type, status, and language.
- Direct or indirect relationship.
- Target instance count.
- Maximum nesting depth.
- Relevant template or pattern source.
- Estimated frontend reach.
- Last modified date.
- Migration priority and assigned owner.
- Verification status after any change.
Export a stable CSV or JSON report. Include the scan date and code version. A reproducible inventory becomes the migration baseline.
Use REST when the audit runs remotely
The WordPress REST API can expose editable content to authenticated tools. Request raw content with appropriate context. Rendered HTML alone loses delimiter details.
Permissions determine which statuses and types are visible. A public request cannot inventory private drafts. Use a dedicated account with necessary read access.
Paginate every collection and preserve stable identifiers. Respect rate limits and server capacity. Resume from checkpoints instead of restarting large scans.
Batch large sites deliberately
Loading every post into memory can exhaust PHP limits. Query identifiers in batches. Fetch, scan, record, and release each batch before continuing.
Keep batch order deterministic. The post identifier works well. Record the last successful identifier for safe retries.
Avoid running heavy scans during peak publishing hours. Read-only work still consumes database and CPU capacity. Monitor errors and duration throughout execution.
Decide whether revisions belong
Revisions can contain blocks absent from the current post. They matter for recovery and historical compliance. They do not represent current visitor output.
Run revision scans as a separate mode. Link each revision to its parent record. Never mix revision instances into the active usage total.
A migration may leave older revisions untouched intentionally. Restoring one later can reintroduce retired blocks. Document that recovery risk for administrators.
Multilingual sites need relationship context
Translation plugins can store each language as separate content. They may also synchronise selected fields. Scan every language record and note relationships.
One source page can have several translated variants. Their block structures may differ. Do not assume a source match proves every translation matches.
Include locale in exports and test samples. Assign translators when replacements alter visible words. Technical migration cannot approve translated meaning.
Multisite needs site-by-site scans
Each multisite site owns separate content tables. A network-active plugin can register blocks everywhere. Scanning the main site proves little about others.
Iterate sites with explicit scope and restore context after each scan. Report site identifiers and domains. Network totals should remain traceable to records.
Archived sites may still need retention. Deleted sites should follow existing governance. Ask network owners before excluding inactive sites.
Manually verify representative matches
Automated counts can be correct while operational conclusions remain wrong. Open samples from every storage type. Confirm the block performs the expected job.
Inspect deeply nested, shared, dynamic, and translated examples. Include at least one false-positive candidate. Verification tests both detection and exclusion logic.
Record reviewer names and sample identifiers. A repeatable audit includes human evidence. That trail helps later teams trust the migration baseline.
Common audit mistakes
- Searching the display title instead of the registered name.
- Scanning only published posts and pages.
- Ignoring nested blocks.
- Missing synced pattern references.
- Ignoring templates, parts, and widget areas.
- Counting code examples as real instances.
- Assuming CSS classes prove block identity.
- Changing content during a discovery scan.
- Reporting totals without record identifiers.
- Claiming generated usage was excluded nowhere.
The worst mistake is false certainty. State coverage and exclusions explicitly. Another maintainer should understand exactly what “zero uses” means.
A repeatable block-usage audit
- Confirm the exact registered block name.
- Define included record types and statuses.
- Run a fast delimiter candidate search.
- Parse candidates and count nested instances.
- Scan synced pattern source records.
- Map shared references into consuming content.
- Scan templates, parts, and widget areas.
- Review automatic insertion and rendering code.
- Crawl public output when risk requires it.
- Export identifiers, counts, and coverage notes.
- Manually verify a sample from every location.
- Freeze the report before replacement begins.
Run the same audit after migration. Remaining target instances should equal the approved exceptions. Unexplained differences require investigation before deactivation.
Frequently asked questions
Can WordPress show every page using one block?
Not through one complete core screen. Reliable audits scan and parse several storage locations.
What does has_block() check?
It quickly checks one post or string for a named block. It favours speed.
Does has_block() find blocks inside synced patterns?
No. Scan shared pattern records and map their references separately.
Can I search the database for a block name?
Yes, for candidates. Parse matches afterward because code examples and indirect usage distort results.
Should block discovery change any content?
No. Keep discovery read-only. Export the inventory before planning replacements or migrations.
The verdict
Focused block plugins make ownership easier to inventory. They still require an exit plan. Test every free WP Block Suite plugin before adoption. Then compare the $299 lifetime suite when several Pro blocks fit your verified needs.

Leave a Reply