-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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 <BlockPreview>
statically
#54999
Comments
Yep, I've encountered this with shadow DOM. I think |
FYI to @WordPress/performance folks and cc @ellatrix who worked on iframing. |
One thing that could help make the previews faster: instead of mounting a full "live" React UI, use the server-side rendering approach to render a static markup and then insert the markup into the preview iframe. The full live React UI is able to react to changes: it subscribes to In #55337 we were investigating and optimizing the number of subscriptions to |
I did do an exploration on statically rendering and the impact was instantly visible - basically no UI lag. My exploration did not put the markup in an iframe and missed some CSS - but the effect was so big that no matter the additions the path looks very promsing. |
I don't like using shadow DOM because, like you said, relative CSS units are messed up. A solution I shared before somewhere could be reusing the same iframe for all previews, take a picture of then content, and then load all previews as images. What do you think? Any downsides? Could still take a while to generate a picture if you're waiting for resources to load. |
I'm not sure if this could help Block editor iframe: cache object URL |
Could be worth trying |
In #55850 the rendering on the server and then setting the html in the iframe of the previews makes the experience much snappier. It seems the removal of The |
I don't think there's a web API for taking screenshots of DOM, probably for good reasons too (security?). In addition, generating the images and loading them might just well be slower and more computational-heavy than rendering the HTML directly (which is what the browsers do best). Images are also often bigger in terms of size and not performant for caching. |
I had a week of pause around this work, will resume. |
Just noting that I stumbled upon this issue after realizing |
The PR advancing this issue (#55850.), has gone through a lot of work, all trying to land this feature. At this point the bits that hold it back are:
A quick reminder of the challenges in server side block rendering:
|
I know this was long time ago and forgive me if I forget the reasoning completely 😅. Could we just render a block-provided empty content if we encounter such cases? For instance, the Image block renders nothing on the server if it doesn't have a I think this would solve the empty state issue, which I think might be the 80% missing cases here. For the rest I think we'd still want to explore the HTML API in the future. The proposed solution comes from the assumption that rendering statically for 99% of the patterns with a few fallbacks is still better than rendering all the patterns in editors. |
Blocks already have the |
I think the problem with the If we support adding the full markup in the |
What problem does this address?
Currently, the
<BlockPreview>
component internally renders a full-blown block editor inside an iframe. That's a lot of overhead for a UI-only component that doesn't allow user interactions. It's especially true for UIs or pages that render multiple<BlockPreview>
s at the same time. For instance, the "patterns" tab in the inserter, and the "Patterns" page in the site editor.To mitigate this, we often use workarounds like
useAsyncList
to split the rendering of the previews into multiple time slices. However, the browser still needs to perform the rendering and all the scripting for unnecessary work. The user might feel a bit more responsive, but the content is still delayed, thus hurting metrics like LCP or CLS. "Slicing" the work also raises questions regarding accessibility for screen readers which need to know when new content is appearing.What is your proposed solution?
I have some ideas that could be done separately and they fix different problems:
Removing the block editor
Instead of rendering a block editor for a given
blocks
instance, we can simply render the HTML usingdangerouslySetInnerHTML
. This saves us a lot of overhead for rendering a full editor for each preview.We can get the HTML string from backend. Take patterns for instance, we can call something like
$html = apply_filters('the_content', $pattern->content);
to render the content on the server side. Most APIs already do this for other reasons, so we just need to expose them.There might be some edge cases that we can't represent without an editor though. For instance, a preview that renders an empty image block will show a placeholder in the editor but nothing with HTML string. IMHO this is acceptable as these are merely "previews" and don't have to match the editor UI exactly. We can audit the comparisons for different patterns in a separate issue to determine if the result is acceptable.
Removing the iframe
The iframe was needed to encapsulate styles within the preview. However, it also adds the overhead of attaching a document and all its memory along with it. Some popular browser extensions also inject their scripts into every frame on a page, which could make it significantly slower for some users.
The solution is to use Shadow DOM to prevent the style from "leaking" out. Using Shadow DOM also means that we will no longer be limited by the
<iframe>
's layout, and we can use simpler CSS likewidth: 100%
to style our previews.The only gotcha I can think of right now is that some viewport-specific units will not behave correctly. If the preview internally uses
100vh
somewhere, for instance, the value will still be calculated based on the browser's viewport, as opposed to the preview's sizes. One possible solution is to make the preview "full size" of the window viewport, then "scale" it down to appropriate sizes. It still has some quirks like unexpected results when resizing though. If this wouldn't work for some previews then we might have to settle on iframes in the end.Sharing the styles
Currently, each preview requests the same set of common styles and scripts for each iframe. Even though those requests are often cached by the browser, they still need to make the HTTP requests, parse the responses, construct the stylesheets, and then finally apply them on each iframe. It's especially noticeable when a developer works on the page with devtools and "Disable cache" enabled, which is a common practice.
We can leverage Constructable Stylesheets to share those reusable stylesheets for each preview (and even the editor canvas itself). It works for both Shadow DOM and iframes so we can take advantage of this technique whatever the above solutions we settle on. The best part of this trick is that theoretically it has near-zero breaking changes and we can start experimenting with that today.
Thoughts? Other ideas? Concerns? Perhaps we can start by recording the performance metrics for the previews in some critical paths, so that we can compare them and know which change is impactful for the trade-offs presented.
The text was updated successfully, but these errors were encountered: