Skip to content

Patterns: Speed up the Patterns screen on sites with many patterns - #85234

Draft
MaggieCabrera wants to merge 2 commits into
trunkfrom
claude/gracious-panini-64fe5b
Draft

MaggieCabrera wants to merge 2 commits into
trunkfrom
claude/gracious-panini-64fe5b

Conversation

@MaggieCabrera

Copy link
Copy Markdown
Contributor

What?

Follow-up to the performance review of #84507. The site editor's Patterns screen no longer freezes on sites with lots of patterns. With 2,000 extra patterns registered, the first previews show in about 1 s instead of 3.4 s.

Opening Appearance > Editor > Patterns Before After
Main-thread time 3,329 ms 920 ms
Longest task 2,487 ms 117 ms
First preview shown 3,350 ms 991 ms

Medians of 7 runs with Playwright, CDP Performance.getMetrics and a long task observer, on Twenty Twenty-Five plus a test plugin that registers 2,000 copies of its patterns. With 4× CPU throttling, the longest task goes from 10.6 s to 0.8 s.

Why?

The Patterns screen parsed every theme, plugin and core pattern as soon as it loaded, even though it only shows 20 per page. It also removed duplicate names with a findIndex inside a filter, which slows down quickly as the list grows. Sites with lots of plugin or theme patterns already hit this today, and #84507 would add many more.

How?

  • A pattern's blocks are parsed the first time its preview reads them, and cached per pattern. It's the approach the block editor store has used since Reusable blocks: fix performance of __experimentalGetAllowedPatterns #64871.
  • The extensible site editor already parses in the preview, so it only gets the faster dedupe.
  • edit-site gets a tsconfig.test.json, like packages/grid, so the new tests can be TypeScript.

Testing Instructions

  1. Install the test plugin from the first comment, or any plugin that registers a few thousand patterns.
  2. Go to Appearance > Editor > Patterns. It opens without the long freeze, and the previews look the same as on trunk.
  3. Switch categories, search and change pages. The previews still show as on trunk.

Testing Instructions for Keyboard

No UI changes.

Use of AI Tools

Written with Claude Code (Claude Opus 5.5), including the test plugin and the measuring script. I reviewed the changes and checked the numbers.

🤖 Generated with Claude Code

The Patterns screen parsed the blocks of every theme, plugin and core
pattern up front, and removed duplicate names with a findIndex inside a
filter. With thousands of patterns both are slow. Blocks are now parsed
when a preview first reads them and cached per pattern, and duplicates
are removed in one pass. The extensible site editor gets the same dedupe;
it already parses in the preview.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaggieCabrera MaggieCabrera added [Type] Performance Related to performance efforts [Feature] Patterns A collection of blocks that can be synced (previously reusable blocks) or unsynced labels Oct 20, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the [Package] Edit Site /packages/edit-site label Oct 20, 2026
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

Preview in WordPress Playground

📦 Bundle size

Size Change: +67 B (0%)

Total Size: 9.05 MB

📦 View Changed
Filename Size Change
build/scripts/edit-site/index.min.js 420 kB +67 B (+0.02%)

37c49c6 Run

⚡ Performance

Show the results

Client side metrics exclude the server response time.

front-end-block-theme

Metric 509d969 trunk % Change
timeToFirstByte 52.65 ms +10.64% -3.32% 53.6 ms +12.59% -6.34% -1.77%
largestContentfulPaint 96 ms +4.17% -10.42% 92 ms +2.17% -4.35% 4.35%
lcpMinusTtfb 38.75 ms +15.23% -11.74% 36.1 ms +14.68% -7.76% 7.34%
wpBeforeTemplate 27.28 ms +19.39% -1.47% 27.28 ms +19.72% -2.09% 0%
wpTemplate 20.6 ms +2.86% -3.59% 20.24 ms +5.48% -2.57% 1.78%
wpTotal 48.61 ms +11.38% -3.5% 49.6 ms +11.33% -6.53% -2%
wpMemoryUsage 7.64 MB +0% -0% 7.61 MB +0% -0% 0.46%
wpDbQueries 16 +0% -0% 16 +0% -0% 0%

front-end-classic-theme

Metric 509d969 trunk % Change
timeToFirstByte 43.75 ms +6.74% -3.31% 42.6 ms +9.15% -0.94% 2.7%
largestContentfulPaint 100 ms +2% -4% 96 ms +4.17% -0% 4.17%
lcpMinusTtfb 54.9 ms +1.91% -2% 53.6 ms +1.21% -0.47% 2.43%
wpBeforeTemplate 24.19 ms +6.53% -1.94% 23.67 ms +5.2% -0.89% 2.2%
wpTemplate 15.91 ms +4.15% -2.95% 15.76 ms +6.03% -1.84% 0.95%
wpTotal 40.55 ms +6.63% -3.4% 39.32 ms +8.83% -0.46% 3.13%
wpMemoryUsage 6.33 MB +0% -0% 6.30 MB +0% -0% 0.57%
wpDbQueries 13 +0% -0% 13 +0% -0% 0%

media-processing

Metric 509d969 trunk % Change
mediaProcessingJpeg 418.37 ms +0.77% -0.82% 412.34 ms +2.15% -0.03% 1.46%
mediaProcessingAvif 6166.01 ms +0.22% -0.03% 6166.71 ms +0.27% -0.25% -0.01%
mediaProcessingJpegToAvif 4313.32 ms +0.17% -0.07% 4327.12 ms +0.21% -0.42% -0.32%

media-upload

Metric 509d969 trunk % Change
jpegUploadProcessing 1926.01 ms +0.62% -24.4% 1442.2 ms +1.99% -1.18% 33.55%
pngUploadProcessing 218.76 ms +10.08% -14.01% 207.92 ms +3.86% -11.62% 5.21%
largeJpegUploadProcessing 1428.7 ms +1.12% -0.53% 1423.64 ms +0.53% -0.38% 0.36%
multipleImageUploadProcessing 2056.86 ms +1.27% -7.31% 1600.48 ms +28.74% -0.86% 28.52%

post-editor

Metric 509d969 trunk % Change
serverResponse 490.66 ms +7.09% -4.86% 498.21 ms +2.8% -1.26% -1.52%
firstPaint 302.26 ms +66.75% -17.45% 519.96 ms +2.61% -15.86% -41.87%
domContentLoaded 1208.4 ms +2.15% -3.14% 1203.83 ms +2% -1.65% 0.38%
loaded 1209.62 ms +2.14% -3.16% 1205.05 ms +1.99% -1.68% 0.38%
firstContentfulPaint 523.02 ms +1.89% -3.63% 519.96 ms +2.61% -2.24% 0.59%
firstBlock 3697.65 ms +3.25% -3.6% 3670.67 ms +3.3% -1.87% 0.74%
type 18.16 ms +6.55% -5.95% 18.02 ms +7.88% -5.94% 0.78%
typeWithoutInspector 19.04 ms +4.1% -8.77% 18.05 ms +6.87% -3.27% 5.48%
typeWithTopToolbar 25.23 ms +3.37% -2.77% 23.4 ms +5.26% -2.65% 7.82%
typeContainer 8.89 ms +10.57% -2.14% 9.84 ms +3.96% -11.89% -9.65%
focus 117.46 ms +4.03% -1.86% 116.53 ms +3.49% -3.2% 0.8%
firstFocus 240.47 ms +0% -0% 242.98 ms +0% -0% -1.03%
selectAll 573.03 ms +6.13% -3.51% 600.6 ms +2.75% -0.11% -4.59%
listViewOpen 79.25 ms +8.64% -6.56% 82.26 ms +6.94% -6.54% -3.66%
inserterOpen 42.82 ms +8.94% -9.86% 40.58 ms +7.64% -5.84% 5.52%
inserterHover 12 ms +7.58% -10.83% 11.17 ms +8.86% -4.21% 7.43%
inserterSearch 8.07 ms +9.29% -7.19% 9.14 ms +2.19% -7.55% -11.71%
loadPatterns 639.79 ms +2.46% -2.45% 667.85 ms +6.12% -1.77% -4.2%
wpTotal 480.42 ms +7.33% -4.83% 488.41 ms +2.82% -1.31% -1.64%
wpMemoryUsage 14.66 MB +0% -0% 14.63 MB +0% -0% 0.26%
wpDbQueries 55 +0% -1.82% 55 +0% -1.82% 0%

site-editor

Metric 509d969 trunk % Change
serverResponse 305.26 ms +4.75% -7.33% 288.39 ms +5.37% -3.67% 5.85%
firstPaint 187.23 ms +28.34% -10.71% 207.99 ms +10.12% -16.54% -9.98%
domContentLoaded 947.45 ms +1.09% -1.59% 952.08 ms +0.72% -1.48% -0.49%
loaded 948.52 ms +1.08% -1.62% 953.01 ms +0.69% -1.46% -0.47%
firstContentfulPaint 345.19 ms +7.64% -12.28% 384.7 ms +4.21% -30.07% -10.27%
firstBlock 3586.91 ms +0.25% -0.24% 3572.04 ms +1.08% -0.72% 0.42%
type 15.95 ms +3.45% -1.38% 15.57 ms +1.67% -3.28% 2.44%
navigate 96.57 ms +4.98% -9.46% 97.96 ms +2.24% -8.43% -1.42%
loadPatterns 1012.5 ms +20.53% -1.45% 977.5 ms +15.61% -5.13% 3.58%
loadPages 1023.61 ms +3.99% -1.34% 1038.02 ms +1.9% -2.96% -1.39%
wpTotal 297.15 ms +4.86% -7.52% 280.37 ms +5.58% -3.72% 5.98%
wpMemoryUsage 12.70 MB +0% -0% 12.66 MB +0% -0% 0.29%
wpDbQueries 44 +2.27% -0% 44 +2.27% -0% 0%

37c49c6 Run

🏁 Flaky tests

Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

Regression: updating a specific option will only change its value and will not corrupt others in /test/e2e/specs/editor/various/core-settings.spec.js, passed after 1 failed attempt.
Error: expect(received).toStrictEqual(expected) // deep equality

- Expected  - 1
+ Received  + 1

  Array [
    "rewrite_rules",
-   "SERIALIZED DATA",
+   "",
  ]
    at forEach (/home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/various/core-settings.spec.js:46:26)
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/various/core-settings.spec.js:43:35
refuses the drop and uploads nothing in /test/e2e/specs/editor/various/single-file-placeholder-drop.spec.js, passed after 1 failed attempt.
Error: expect(received).toHaveLength(expected)

Expected length: 0
Received length: 1
Received array:  [{"_links": {"about": [{"href": "http://localhost:8889/wp-json/wp/v2/types/attachment"}], "author": [{"embeddable": true, "href": "http://localhost:8889/wp-json/wp/v2/users/1"}], "collection": [{"href": "http://localhost:8889/wp-json/wp/v2/media"}], "curies": [{"href": "https://api.w.org/{rel}", "name": "wp", "templated": true}], "replies": [{"embeddable": true, "href": "http://localhost:8889/wp-json/wp/v2/comments?post=223"}], "self": [{"href": "http://localhost:8889/wp-json/wp/v2/media/223", "targetHints": {"allow": ["GET", "POST", "PUT", "PATCH", "DELETE"]}}], "wp:attached-to": [{"embeddable": true, "href": "http://localhost:8889/wp-json/wp/v2/posts/222", "id": 222, "post_type": "post"}]}, "alt_text": "", "author": 1, "caption": {"rendered": ""}, "class_list": ["post-223", "attachment", "type-attachment", "status-inherit", "hentry", "entry"], "comment_status": "open", "date": "2026-10-20T20:29:48", "date_gmt": "2026-10-20T20:29:48", "description": {"rendered": "<p class=\"attachment\"><a href='http://localhost:8889/wp-content/uploads/2026/10/12590a61-dddc-4c2a-bb08-82765e12cb8d.png'><img loading=\"lazy\" decoding=\"async\" width=\"10\" height=\"10\" src=\"http://localhost:8889/wp-content/uploads/2026/10/12590a61-dddc-4c2a-bb08-82765e12cb8d.png\" class=\"attachment-medium size-medium\" alt=\"\" style=\"width:100%;height:100%;max-width:10px;\" /></a></p>
"}, "featured_media": 0, "filename": "12590a61-dddc-4c2a-bb08-82765e12cb8d.png", "filesize": 80, "guid": {"rendered": "http://localhost:8889/wp-content/uploads/2026/10/12590a61-dddc-4c2a-bb08-82765e12cb8d.png"}, "id": 223, "link": "http://localhost:8889/?attachment_id=223", "media_details": {"file": "2026/10/12590a61-dddc-4c2a-bb08-82765e12cb8d.png", "filesize": 80, "height": 10, "image_meta": {"alt": "", "aperture": "0", "camera": "", "caption": "", "copyright": "", "created_timestamp": "0", "credit": "", "focal_length": "0", "iso": "0", "keywords": [], "orientation": "0", "shutter_speed": "0", "title": ""}, "sizes": {}, "width": 10}, "media_type": "image", "meta": [], "mime_type": "image/png", "modified": "2026-10-20T20:29:48", "modified_gmt": "2026-10-20T20:29:48", "ping_status": "closed", "post": 222, "slug": "12590a61-dddc-4c2a-bb08-82765e12cb8d", "source_url": "http://localhost:8889/wp-content/uploads/2026/10/12590a61-dddc-4c2a-bb08-82765e12cb8d.png", "status": "inherit", "template": "", "title": {"rendered": "12590a61-dddc-4c2a-bb08-82765e12cb8d"}, "type": "attachment"}]
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/various/single-file-placeholder-drop.spec.js:71:45
submenu converts to link automatically in /test/e2e/specs/editor/blocks/navigation.spec.js, passed after 1 failed attempt.
Error: apiRequestContext.fetch: socket hang up
Call log:
  - → POST http://localhost:8889/wp-json/wp/v2/navigation
    - user-agent: Playwright/1.63.0 (x64; ubuntu 24.04) node/24.20 CI/1
    - accept: */*
    - accept-encoding: gzip,deflate,br
    - X-WP-Nonce: 7fc4276d2d
    - content-type: application/json
    - content-length: 356
    - cookie: wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_23778236db82f19306f247e20a353a99=admin%7C1792700390%7CLf5UeVYoOACZgVdBuMzfDb6DH9DJXzd3SvDVPFQ6NdA%7Ca0bb159bbb9b912a5d488ae92cbafb30b6c7f9d3f9a63063010b39a30358ad11; wp-settings-time-1=1792527720

    at RequestUtils.rest (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/rest.ts:112:39)
    at RequestUtils.createRecord (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/records.ts:20:14)
    at RequestUtils.createNavigationMenu (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/menus.ts:63:14)
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/blocks/navigation.spec.js:226:23
three users concurrently edit a large post with diverse blocks in /test/e2e/specs/editor/collaboration/collaboration-stress.spec.ts, passed after 1 failed attempt.
TimeoutError: locator.waitFor: Timeout 17500ms exceeded.
Call log:
  - waiting for getByRole('button', { name: /Collaborators list/ }) to be visible

    at map (/home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/collaboration/fixtures/collaboration-utils.ts:193:7)
    at CollaborationUtils.waitForMutualDiscovery (/home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/collaboration/fixtures/collaboration-utils.ts:188:10)
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/collaboration/collaboration-stress.spec.ts:416:28

37c49c6 Run

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Patterns A collection of blocks that can be synced (previously reusable blocks) or unsynced [Package] Edit Site /packages/edit-site [Type] Performance Related to performance efforts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant