Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render block preview on the server #55850

Closed
wants to merge 22 commits into from
Closed

Conversation

draganescu
Copy link
Contributor

@draganescu draganescu commented Nov 3, 2023

Attempt to fix #54999

This PR registers a custom endpoint that renders a block tree and returns the html. This endpoint is used in the BlockPattern component (of BlockPatternList) which uses BlockPreview.

BlockPreview is augmented to receive a new html prop which is passed down to ScaledBlockPreview (from AutoBlockPreview) and set to be the dangerouslySetInnerHTML of a div in the preview iframe.

This oprimisation makes all the instances of BlockPatternList be much snappier: the new page pattern selector, the pattern inserter or the template swap modal.

Known issues

  • the patterns that contain a query block do not render as expected because on the server they miss the context. It may be possible to setup the correct wp query for it to inherit.

Known limitations

  • some blocks render a placeholder on when used in the editor in an undefined context. For example the "Archive Title" block shows a "Tag: Name" placeholder when one creates a tag index template. When this block is rendered on the server it will need a global context to render a tag or it renders nothing.

  • In order to avoid having to update blocks, the solution here is to provide a global context for every context we preview in. We can "detect" the context in the editor and send it to the REST API endpoint. For instance, when previewing patterns in a template in the site editor we can send the template name. We can then build a WP_Query and a Post based on the most recently published content.

    • however if thre is no content then we still need to hard code some fallback content, for every possible preview context. This in the future can migrate to be provided by the theme via demo content.

To do:

  • the markup should come with the pattern list instead of being requested per pattern, and so drop the need to use api-fetch in the block editor package

  • the api endpoint should get a list of patterns, that is be specific to this not render generic blocks

  • rendering blocks on the server require that some of the global context never be undefined. We need to update render_block to always have the global context keys set, even if they are null.

Screen recording

pre-rendered-patterns.mp4

Copy link

github-actions bot commented Nov 3, 2023

This pull request has changed or added PHP files. Please confirm whether these changes need to be synced to WordPress Core, and therefore featured in the next release of WordPress.

If so, it is recommended to create a new Trac ticket and submit a pull request to the WordPress Core Github repository soon after this pull request is merged.

If you're unsure, you can always ask for help in the #core-editor channel in WordPress Slack.

Thank you! ❤️

View changed files
❔ lib/compat/wordpress-6.5/class-gutenberg-render-blocks-controller.php
❔ lib/compat/wordpress-6.5/rest-api.php
❔ lib/load.php

Copy link

github-actions bot commented Nov 3, 2023

Size Change: +3.9 kB (0%)

Total Size: 1.69 MB

Filename Size Change
build/block-editor/index.min.js 247 kB +39 B (0%)
build/edit-site/index.min.js 196 kB +3.86 kB (+2%)
build/editor/index.min.js 55.9 kB +2 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 964 B
build/annotations/index.min.js 2.71 kB
build/api-fetch/index.min.js 2.29 kB
build/autop/index.min.js 2.11 kB
build/blob/index.min.js 590 B
build/block-directory/index.min.js 7.25 kB
build/block-directory/style-rtl.css 1.04 kB
build/block-directory/style.css 1.04 kB
build/block-editor/content-rtl.css 4.31 kB
build/block-editor/content.css 4.31 kB
build/block-editor/default-editor-styles-rtl.css 403 B
build/block-editor/default-editor-styles.css 403 B
build/block-editor/style-rtl.css 15.3 kB
build/block-editor/style.css 15.3 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 138 B
build/block-library/blocks/audio/theme.css 138 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/block/editor-rtl.css 305 B
build/block-library/blocks/block/editor.css 305 B
build/block-library/blocks/button/editor-rtl.css 419 B
build/block-library/blocks/button/editor.css 417 B
build/block-library/blocks/button/style-rtl.css 633 B
build/block-library/blocks/button/style.css 632 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 113 B
build/block-library/blocks/categories/editor.css 112 B
build/block-library/blocks/categories/style-rtl.css 124 B
build/block-library/blocks/categories/style.css 124 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 421 B
build/block-library/blocks/columns/style.css 421 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 647 B
build/block-library/blocks/cover/editor.css 650 B
build/block-library/blocks/cover/style-rtl.css 1.7 kB
build/block-library/blocks/cover/style.css 1.69 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/style-rtl.css 98 B
build/block-library/blocks/details/style.css 98 B
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 138 B
build/block-library/blocks/embed/theme.css 138 B
build/block-library/blocks/file/editor-rtl.css 316 B
build/block-library/blocks/file/editor.css 316 B
build/block-library/blocks/file/style-rtl.css 280 B
build/block-library/blocks/file/style.css 281 B
build/block-library/blocks/file/view.min.js 322 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 B
build/block-library/blocks/form-input/editor-rtl.css 229 B
build/block-library/blocks/form-input/editor.css 228 B
build/block-library/blocks/form-input/style-rtl.css 343 B
build/block-library/blocks/form-input/style.css 343 B
build/block-library/blocks/form-submission-notification/editor-rtl.css 343 B
build/block-library/blocks/form-submission-notification/editor.css 342 B
build/block-library/blocks/form-submit-button/style-rtl.css 69 B
build/block-library/blocks/form-submit-button/style.css 69 B
build/block-library/blocks/form/view.min.js 452 B
build/block-library/blocks/freeform/editor-rtl.css 2.61 kB
build/block-library/blocks/freeform/editor.css 2.61 kB
build/block-library/blocks/gallery/editor-rtl.css 957 B
build/block-library/blocks/gallery/editor.css 962 B
build/block-library/blocks/gallery/style-rtl.css 1.75 kB
build/block-library/blocks/gallery/style.css 1.75 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 654 B
build/block-library/blocks/group/editor.css 654 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 189 B
build/block-library/blocks/heading/style.css 189 B
build/block-library/blocks/html/editor-rtl.css 340 B
build/block-library/blocks/html/editor.css 341 B
build/block-library/blocks/image/editor-rtl.css 834 B
build/block-library/blocks/image/editor.css 833 B
build/block-library/blocks/image/style-rtl.css 1.61 kB
build/block-library/blocks/image/style.css 1.6 kB
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/image/view.min.js 2.02 kB
build/block-library/blocks/latest-comments/style-rtl.css 357 B
build/block-library/blocks/latest-comments/style.css 357 B
build/block-library/blocks/latest-posts/editor-rtl.css 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/style-rtl.css 478 B
build/block-library/blocks/latest-posts/style.css 478 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 505 B
build/block-library/blocks/media-text/style.css 503 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 671 B
build/block-library/blocks/navigation-link/editor.css 672 B
build/block-library/blocks/navigation-link/style-rtl.css 103 B
build/block-library/blocks/navigation-link/style.css 103 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation/editor-rtl.css 2.26 kB
build/block-library/blocks/navigation/editor.css 2.26 kB
build/block-library/blocks/navigation/style-rtl.css 2.27 kB
build/block-library/blocks/navigation/style.css 2.26 kB
build/block-library/blocks/navigation/view.min.js 1.02 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 401 B
build/block-library/blocks/page-list/editor.css 401 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 235 B
build/block-library/blocks/paragraph/editor.css 235 B
build/block-library/blocks/paragraph/style-rtl.css 335 B
build/block-library/blocks/paragraph/style.css 335 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 508 B
build/block-library/blocks/post-comments-form/style.css 508 B
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/style-rtl.css 141 B
build/block-library/blocks/post-excerpt/style.css 141 B
build/block-library/blocks/post-featured-image/editor-rtl.css 666 B
build/block-library/blocks/post-featured-image/editor.css 662 B
build/block-library/blocks/post-featured-image/style-rtl.css 345 B
build/block-library/blocks/post-featured-image/style.css 345 B
build/block-library/blocks/post-navigation-link/style-rtl.css 215 B
build/block-library/blocks/post-navigation-link/style.css 214 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 409 B
build/block-library/blocks/post-template/style.css 408 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-time-to-read/style-rtl.css 69 B
build/block-library/blocks/post-time-to-read/style.css 69 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 125 B
build/block-library/blocks/preformatted/style.css 125 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 335 B
build/block-library/blocks/pullquote/style.css 335 B
build/block-library/blocks/pullquote/theme-rtl.css 168 B
build/block-library/blocks/pullquote/theme.css 168 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 288 B
build/block-library/blocks/query-pagination/style.css 284 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 486 B
build/block-library/blocks/query/editor.css 486 B
build/block-library/blocks/query/style-rtl.css 312 B
build/block-library/blocks/query/style.css 308 B
build/block-library/blocks/query/view.min.js 647 B
build/block-library/blocks/quote/style-rtl.css 237 B
build/block-library/blocks/quote/style.css 237 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 140 B
build/block-library/blocks/read-more/style.css 140 B
build/block-library/blocks/rss/editor-rtl.css 149 B
build/block-library/blocks/rss/editor.css 149 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 184 B
build/block-library/blocks/search/editor.css 184 B
build/block-library/blocks/search/style-rtl.css 613 B
build/block-library/blocks/search/style.css 613 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/search/view.min.js 475 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/style-rtl.css 234 B
build/block-library/blocks/separator/style.css 234 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 329 B
build/block-library/blocks/shortcode/editor.css 329 B
build/block-library/blocks/site-logo/editor-rtl.css 760 B
build/block-library/blocks/site-logo/editor.css 760 B
build/block-library/blocks/site-logo/style-rtl.css 204 B
build/block-library/blocks/site-logo/style.css 204 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 116 B
build/block-library/blocks/site-title/editor.css 116 B
build/block-library/blocks/site-title/style-rtl.css 57 B
build/block-library/blocks/site-title/style.css 57 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-links/editor-rtl.css 682 B
build/block-library/blocks/social-links/editor.css 681 B
build/block-library/blocks/social-links/style-rtl.css 1.49 kB
build/block-library/blocks/social-links/style.css 1.49 kB
build/block-library/blocks/spacer/editor-rtl.css 359 B
build/block-library/blocks/spacer/editor.css 359 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 399 B
build/block-library/blocks/table/editor.css 399 B
build/block-library/blocks/table/style-rtl.css 646 B
build/block-library/blocks/table/style.css 645 B
build/block-library/blocks/table/theme-rtl.css 157 B
build/block-library/blocks/table/theme.css 157 B
build/block-library/blocks/tag-cloud/style-rtl.css 251 B
build/block-library/blocks/tag-cloud/style.css 253 B
build/block-library/blocks/template-part/editor-rtl.css 403 B
build/block-library/blocks/template-part/editor.css 403 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/term-description/style-rtl.css 111 B
build/block-library/blocks/term-description/style.css 111 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/style-rtl.css 191 B
build/block-library/blocks/video/style.css 191 B
build/block-library/blocks/video/theme-rtl.css 139 B
build/block-library/blocks/video/theme.css 139 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.11 kB
build/block-library/common.css 1.11 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 12.3 kB
build/block-library/editor.css 12.3 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 214 kB
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14.7 kB
build/block-library/style.css 14.7 kB
build/block-library/theme-rtl.css 700 B
build/block-library/theme.css 705 B
build/block-serialization-default-parser/index.min.js 1.13 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/blocks/index.min.js 51.6 kB
build/commands/index.min.js 15.5 kB
build/commands/style-rtl.css 947 B
build/commands/style.css 942 B
build/components/index.min.js 235 kB
build/components/style-rtl.css 12.1 kB
build/components/style.css 12.1 kB
build/compose/index.min.js 12.8 kB
build/core-commands/index.min.js 2.73 kB
build/core-data/index.min.js 72.7 kB
build/customize-widgets/index.min.js 12.1 kB
build/customize-widgets/style-rtl.css 1.36 kB
build/customize-widgets/style.css 1.36 kB
build/data-controls/index.min.js 651 B
build/data/index.min.js 8.94 kB
build/date/index.min.js 17.9 kB
build/deprecated/index.min.js 462 B
build/dom-ready/index.min.js 336 B
build/dom/index.min.js 4.68 kB
build/edit-post/classic-rtl.css 571 B
build/edit-post/classic.css 571 B
build/edit-post/index.min.js 30.7 kB
build/edit-post/style-rtl.css 6.81 kB
build/edit-post/style.css 6.8 kB
build/edit-site/style-rtl.css 14.8 kB
build/edit-site/style.css 14.8 kB
build/edit-widgets/index.min.js 17.3 kB
build/edit-widgets/style-rtl.css 4.71 kB
build/edit-widgets/style.css 4.71 kB
build/editor/style-rtl.css 4.68 kB
build/editor/style.css 4.68 kB
build/element/index.min.js 4.87 kB
build/escape-html/index.min.js 548 B
build/format-library/index.min.js 7.76 kB
build/format-library/style-rtl.css 577 B
build/format-library/style.css 577 B
build/hooks/index.min.js 1.57 kB
build/html-entities/index.min.js 454 B
build/i18n/index.min.js 3.61 kB
build/interactivity/file.min.js 442 B
build/interactivity/image.min.js 2.15 kB
build/interactivity/index.min.js 12.5 kB
build/interactivity/navigation.min.js 1.14 kB
build/interactivity/query.min.js 791 B
build/interactivity/search.min.js 610 B
build/is-shallow-equal/index.min.js 535 B
build/keyboard-shortcuts/index.min.js 1.76 kB
build/keycodes/index.min.js 1.49 kB
build/list-reusable-blocks/index.min.js 2.11 kB
build/list-reusable-blocks/style-rtl.css 865 B
build/list-reusable-blocks/style.css 865 B
build/media-utils/index.min.js 2.92 kB
build/modules/importmap-polyfill.min.js 12.2 kB
build/notices/index.min.js 964 B
build/nux/index.min.js 2.01 kB
build/nux/style-rtl.css 775 B
build/nux/style.css 771 B
build/patterns/index.min.js 5.31 kB
build/patterns/style-rtl.css 564 B
build/patterns/style.css 564 B
build/plugins/index.min.js 1.81 kB
build/preferences-persistence/index.min.js 1.85 kB
build/preferences/index.min.js 1.26 kB
build/primitives/index.min.js 994 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 994 B
build/react-i18n/index.min.js 631 B
build/react-refresh-entry/index.min.js 9.46 kB
build/react-refresh-runtime/index.min.js 6.78 kB
build/redux-routine/index.min.js 2.71 kB
build/reusable-blocks/index.min.js 2.74 kB
build/reusable-blocks/style-rtl.css 265 B
build/reusable-blocks/style.css 265 B
build/rich-text/index.min.js 10.4 kB
build/router/index.min.js 1.79 kB
build/server-side-render/index.min.js 1.96 kB
build/shortcode/index.min.js 1.4 kB
build/style-engine/index.min.js 2.06 kB
build/token-list/index.min.js 587 B
build/url/index.min.js 3.83 kB
build/vendors/inert-polyfill.min.js 2.48 kB
build/vendors/react-dom.min.js 41.8 kB
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 967 B
build/warning/index.min.js 259 B
build/widgets/index.min.js 7.22 kB
build/widgets/style-rtl.css 1.18 kB
build/widgets/style.css 1.18 kB
build/wordcount/index.min.js 1.03 kB

compressed-size-action

@draganescu draganescu changed the title try/prerender patterns Render patterns on the server Nov 3, 2023
Copy link

github-actions bot commented Nov 3, 2023

Flaky tests detected in a4e7b28.
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.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7126692771
📝 Reported issues:

@MaggieCabrera MaggieCabrera added the [Type] Performance Related to performance efforts label Nov 3, 2023
@draganescu draganescu added the [Feature] Patterns A collection of blocks that can be synced (previously reusable blocks) or unsynced label Nov 3, 2023
@draganescu draganescu marked this pull request as ready for review November 3, 2023 16:25
Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start. This has a lot of potential.

@@ -18,6 +18,9 @@ import {
import { useInstanceId } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import { Icon, symbol } from '@wordpress/icons';
// ignore restricted import ESLint error for apiFetch
// eslint-disable-next-line no-restricted-imports
import apiFetch from '@wordpress/api-fetch';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The block editor can't depend on apiFetch as it's a generic JS package. What we should do here is to provide a block editor setting that is called renderPreview or something that is basically a component that takes a list of blocks to render their preview. And if this setting is not provided, we should keep the existing preview behavior (third-party editors...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I've commented as well that this is a known issue. The eslint disable is for demo purposes.

If the endpoint gets a list of patterns and returns a list of rendered html then the patterns that are passed to this component list can include their rendered html and this fetch would go away. Why would we need an editor setting?

Copy link
Contributor

@youknowriad youknowriad Nov 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component is about rendering previews regardless of whether they come from pattern, the example of a block type, variations or any random block list. Adding the rendered html to patterns won't solve all the use-cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The block preview component optionally gets the HTML. It is being passed this HTML depending on the context. In this case only it comes from block patterns list which gets it from whomever wants to render a pattern list. So yes, what I am saying is I only want to solve the pattern preview case but the low level block preview will be ready for future situations. I don't understand what the editor setting would be for, it's probably obvious :D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would a BlockPreview component get HTML, it's not block preview anymore, it's just an iframe in that case and there's no need to use this component.

For me, we should solve all the use-cases at once because they're just as problematic in terms of performance.

I don't understand what the editor setting would be for, it's probably obvious :D

This is some pseudo code, to explain the idea.

const BlockPreview = ( blocks ) => {
   const html = useSelect( select => select( 'core/data' ).getHtmlForBlocks( blocks ), [ blocks ] );
   return <Iframe>{html ?? <Spinner />}</Iframe>
}

<BlockEditorProvider settings={ ...settings, BlockPreview } />

and the BlockPreview component could do something like:

const { BlockPreview } = useSelect( select => select( 'core/block-editor' ).getSettings().BlockPreview, [] );

return BlockPreview ? <BlockPreview blocks={blocks} /> : <CurrentBlockPreviewCode blocks={blocks} />

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not against two components to render the previous, but in that case, It's not even a StaticBlockPreview it's just Iframe

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's still some hot-wiring to make an <Iframe> <StaticBlockPreview>. The injected styles and scaled size as mentioned by @draganescu for instance, but also like pointer-events: none and other stuff that only makes sense in the "preview" context. We probably will end up abstracting them into a different component, but the name should be more explicit than <Iframe>.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as @kevin940726 describes the helper components that block preview needs will have to be abstracted and provided by a 3rd package if we want to give editor implementations the ability to implement their own previewer - like this PR demonstrates for the site editor.

I don't think we should modify the patterns API - previewing is very special - again as this PR demonstrates - in the sense that we need to build some context for the rendering to be as close to a real preview as possible - which complicates the process enough to warrant a special endpoint. I also agree with @youknowriad that if should have a blanket solution for all previews: patterns, blocks, block styles.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previewing is very special - again as this PR demonstrates - in the sense that we need to build some context for the rendering to be as close to a real preview as possible - which complicates the process enough to warrant a special endpoint.

Could you elaborate what specifically is the special handling we need to do in a separate API request, as opposed to maybe a PHP function call instead? There's nothing stopping us from providing both the endpoint and patching the patterns API too. I guess the main concern I have here is that a round-trip time for each preview depends on network condition and might just be slower for some users (high-end device but with slow internet). Not to mention that we're not doing batching and we'll soon reach the cap of maximum concurrent requests the browser can make at a time. We're also not doing error management and retrying which make this a little bit more complicated. I think it'll be easier if we can just move them all to the patterns API so that it can also be cached correctly too (any reason we're using POST for this endpoint that can't be cached by the browser?).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your observations are correct. There's nothing stopping us from providing both the endpoint and patching the patterns API too, indeed.

what specifically is the special handling we need to do in a separate API request, as opposed to maybe a PHP function call instead

It's not specific handling it's specific logic that we require to execute to create a server context for rendering properly. We could have shared procedures (a function) between patterns and other custom endpoints, sure.

any reason we're using POST for this endpoint

We send this big blob of blocks. If this were part of the GET patterns this would also not be required.

lib/init.php Outdated Show resolved Hide resolved
__html: html,
} }
></div>
{ /* <MemoizedBlockList renderAppender={ false } /> */ }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some loader is probably needed while fetching the preview.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally it should not be needed at this point - the html should exist via the props this component gets.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not necessarily saying but the preview function should be async, whenever that thing is in flight, we should probably show some kind of visual indicator.

Copy link
Contributor Author

@draganescu draganescu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored this to match the architecture suggestion from @youknowriad.
I think the path is good but we have some challenges:

  • rendering the shape of block list that the editor works with is unclear how to on the server - serialize to the rescue!
  • there is considerable duplication between the editor implementation and the abstract block editor in terms of how the preview works

lib/init.php Outdated Show resolved Hide resolved
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is duplicated from the block editor package,

@draganescu draganescu changed the title Render patterns on the server Render block preview on the server Nov 9, 2023
} );
}

function Iframe( {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component is exposed from block-editor, why did you have to reimplement it?

>
<EditorStyles styles={ editorStyles } />
{ contentResizeListener }
<div
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this div the only thing that is different compared to the default block preview? If that's the case, why not just make the blockPreview setting return just this part and not the whole thing and just reuse the BlockPreview except this part.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll review this - I think initially I thought there would be more simplification. I'll see if there is more if not I'll go with your observations. 🙏

@draganescu draganescu force-pushed the try/prerender-patterns branch 2 times, most recently from da1558b to 34967f0 Compare November 22, 2023 16:15
@draganescu draganescu self-assigned this Nov 23, 2023
@draganescu
Copy link
Contributor Author

I've hit a snag here: some patterns include image blocks with no image, and they should show a placeholder. But when rendering these blocks on the server we do not show placeholders, instead we show nothing. So previews for patterns or other things that are supposed to show placeholders show up empty.

One way I can think of is to set a context in the preview render endpoint that tells the image block to render the placeholder. I'll look into this path.

@jsnajdr
Copy link
Member

jsnajdr commented Nov 27, 2023

some patterns include image blocks with no image, and they should show a placeholder. But when rendering these blocks on the server we do not show placeholders, instead we show nothing.

Maybe it was a mistake to start using this "if certain attributes are not filled in, render nothing at all" logic in the first place. I've been recently very surprised by a similar behavior with a Social Icon block that doesn't have an URL set. You create some site layout, planning to fill details later, and decide to preview it, and some parts are outright missing.

@draganescu
Copy link
Contributor Author

The snag is more complicated than it appears. The snag is that blocks who are set empty will render whitespace on the server, but will render placeholders or examples in the editor. That means that server preview is an inferior experience, even a broken one sometimes (e.g. a TT4 pattern that has only image blocks in it on two colums, renders nothing on the server for preview, it looks broken).

I thought it's be something approachable but it's not so easy. To illustrate, one of the things we came up with was to use the example data to render previews. Sounds like a decent idea, most core blocks if not all have examples so we could use that.

While true that most examples are in the client side init, it's easy to move the example to block.json and make that available for server side rendering. However that is not enough.

Case in point the image block:

  • moving the example from the client side init to block.json
  • then using a render_block_data filter to merge the example attributes with the current block attributes
  • has no effect on the render
    ... because the image block renders a static render and works with the HTML directly, not with the url attribute.

So this is still stuck at: how to preview empty blocks on the server in a way in which they show up something, an example or a placeholder, without having to implement a custom solution for every block?

@ellatrix
Copy link
Member

Have the recent performance improvements made any noticeable difference? It should speed up everything inside BlockList.

@draganescu
Copy link
Contributor Author

Have the recent performance improvements made any noticeable difference? It should speed up everything inside BlockList.

@ellatrix you can test this. To me on on a fast computer TT4 trying to swap template using the menu in the template editor's inspector, template tab, dot menu, replace template, it is still noticeably laggy and stutters.

I think no matter how much we optimise the performance of the editor, instancing an inline editor per item previewed will be problematic - maybe a setup will show 98 patterns in that modal, what is the upper limit our optimisations still underperform?

Also rendering serverside, and fixing server side rendering of previews, opens up new possibilities around themes providing nice demo content that can be easily overriden without polluting the templates with hardcoded content, so there is side value in that too if this PR lands.

However, to advance here, we need to do at least a couple of things, if I am not mistaken:

  1. move the examples from the client side init to block json for all core blocks
  2. come up with a standard process based on the Tag Parser tool, a process which happes before serverside rendering and which sources attributes from HTML if the attributes have to be sourced from HTML, so we don't have to rely on client side rendering.

If we can achieve 1 and 2 we can then easily come up with a rendering "context" which renders blocks using example data on the server,

@kevin940726
Copy link
Member

The limit is close to 0ms 😄

FWIW, performance is only one aspect of the story here. An editor also uses a massive amount of memory to power all the editing experience. I think either way rendering previews statically can still have lots of potential!

setHTML( dataHTML );
};
getHTML().catch( ( error ) => {
return error;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's happening with this error? It seems to be swallowed right?

Should we instead set some local error state (as you do with the setHTML) and then use this to display an error message instead of the preview? Or you could revert to rendering client side.

Comment on lines +263 to +264
// Updates all blocks to use their example data, if they have it.
function modify_block_attributes_before_render( $block ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll need to explain why this is being done 🙏

@draganescu
Copy link
Contributor Author

In this commit I exemplify with bad code the idea of always using values from attributes when rendering dynamicly.

This is a convention based system. Since the dynamic render function allows the developer to write the rendering in any way possible, we can't enforce a system in which the data pipeline is fixed. So we need a sort of convention which, if you follow, you get benefits.

In this case if the block binding API, and hence partially synced patterns and block previews, work on the convention that the data always comes from attributes, it's a good incentive to embrace the convention.

In c177616:

  • the featured image block is modified
  • so that the data always comes from attributes
  • which means there is an attribute called featured_image, which does nothing in the editor
  • but it is used on the server to set the featured image from the result of get_the_post_thumbnail
  • via the same ol render_block_data filter

This way we don't have to do any HTML acrobatics. We only need to override the attribute. I think this should be attempted, the idea that to change rendered value at the system level only attribute data should be updated, not markup. Markup acrobatics should be reserved for on the spot, block specific handlers, which should not be managed by system level featured (block bindings, pattern overrides/synced patterns or block preview).

Extra notes:

  • in render_block_data we don't seem to have block context yet, that may be an issue
  • I think this idea is bound (oops) to happen anyway - block bindings use biding an attribute's value to a data source, so currently the featured image block does not have a featured image attribute so it can't be bound, which makes it odd - we want all core blocks to support binding.
  • I added a check for $_GET['block_preview'] so that preview example override happens only when that is in the URL of the rendered page, easier to test for me

@scruffian
Copy link
Contributor

I tried moving the basic feature to a different PR here: #57977

@draganescu
Copy link
Contributor Author

Close in favor of #57977 - this other one is a more streamlined implementation.

@draganescu draganescu closed this Feb 1, 2024
@scruffian scruffian deleted the try/prerender-patterns branch February 2, 2024 15:47
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 [Type] Performance Related to performance efforts
Projects
Development

Successfully merging this pull request may close these issues.

Render <BlockPreview> statically
8 participants