Overview
- Proposed module name: oEmbed optimizer
- Proposed module slug: oembed-optimizer
- Proposed module owner GitHub username(s): adamsilverstein
About the module
Purpose
Optimize loading of oEmbeds by ensuring they are lazy loaded and that scripts are only loaded as the embed enters the viewport.
Scope
Adjust core's loading of oEmbeds to make them lazier.
Rationale
oEmbeds are embedded visual blocks and on WordPress sites, they are typically inserted by pasting a URL, for example a tweet or youtube video into the block editor. This URL is transformed by the editor into an embed block.
oEmbeds represent an unpredictable contributor to performance. Since they by nature include third party resources, they tend to compete for resources with the main page thread. While an oEmbed could be simple, they can also be incredibly impactful - loading on additional resources dynamically in unpredictable ways.
oEmbed Stats
I gathered some stats about oEmbed usage in WordPress by querying httparchive:
- Around 1% of WordPress pages in httparchive contain at least one oEmbed (query)
- Of WP sites using an oEmbed, ~70% have a YouTube oEmbed and ~10% have a Twitter(X) embed (query)
- Some of the top oEmbeds are among the top INP offenders in WordPress (query)
How oEmbeds work
oEmbeds are typically implemented either as an iframe or using a script tag that expands a block of content.
Example: YouTube oEmbed which is implemented as an iframe:
<figure class="wp-block-embed">
<div class="wp-block-embed__wrapper">
<iframe title="TITLE" width="840" height="473" src="https://www.youtube.com/embed/YT_LINK?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen>
</iframe>
</div>
</figure>
Of note for iframe oEmbeds:
- WordPress core will already add the loading=”lazy” attribute to iframes that meet the proper heuristics. However, it might be worth using more aggressive lazy loading for oEmbeds which are less likely to be LCP elements.
- oEmbed providers can also include the lazy loading attribute in their iframe markup.
Example: Twitter (X) oEmbed which is implemented with a script loader:
<figure class="wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter">
<div class="wp-block-embed__wrapper">
<blockquote class="twitter-tweet" data-width="474" data-dnt="true">
<p lang="en" dir="ltr">TWEET_TITLE
<a href="https://t.co/TWEET_LINK">https://t.co/TWEET_LINK</a>
</p>
<a href="LINK_TO_ORIGINAL">DATE</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</div></figure>
Notes: A couple of things to note about the Twitter oEmbed (and similar oEmbeds that use a loader script):
A minimal version of the tweet itself still loads and works even before the JavaScript loads or if JavaScript is disabled
-
The https://platform.twitter.com/widgets.js script is generic and is loaded for every Twitter oEmbed. Its job is to expand fill its container div content with a full visual representation, including an image preview:
-
In the Twitter example, the widget.js script replaces the oEmbeds parent div (wp-block-embed__wrapper) content entirely with an iframe that provides the visual representation:

- Loaders are typically loaded async, however it still may be advantageous to delay loading these scripts until the embed approaches the viewport. This prevents any resource contention until the user actually scrolls near to the embed.
WordPress post embeds
WordPress itself provides an oEmbed capability, and pasting a post URL into the editor results in a WordPress embed - a mini version of the linked post embedded into the post. These embeds are a bit of a special case and while implemented as an iframe, they are hidden until a signal is received from the inner frame, making them a poor match for lazy loading
Optimizing oEmbeds
In my PR I propose optimizing oEmbeds using two techniques:
For oEmbeds delivered as iframes:
- ensure the loading=lazy attribute is always present. My hypothesis is this will result in improved metrics at the median.
For oEmbeds built using script tags:
- move the script src to a data attribute
- add a page level interaction observer that resets src (or injects the script tag anew) before the user scrolls to the oEmbed
To Do
WordPress oEmebds need special treatment as mentioned above
The scroll tolerance should be bandwidth based (higher speed connections get a smaller preload window)
Overview
About the module
Purpose
Optimize loading of oEmbeds by ensuring they are lazy loaded and that scripts are only loaded as the embed enters the viewport.
Scope
Adjust core's loading of oEmbeds to make them lazier.
Rationale
oEmbeds are embedded visual blocks and on WordPress sites, they are typically inserted by pasting a URL, for example a tweet or youtube video into the block editor. This URL is transformed by the editor into an embed block.
oEmbeds represent an unpredictable contributor to performance. Since they by nature include third party resources, they tend to compete for resources with the main page thread. While an oEmbed could be simple, they can also be incredibly impactful - loading on additional resources dynamically in unpredictable ways.
oEmbed Stats
I gathered some stats about oEmbed usage in WordPress by querying httparchive:
How oEmbeds work
oEmbeds are typically implemented either as an iframe or using a script tag that expands a block of content.
Example: YouTube oEmbed which is implemented as an iframe:
Of note for iframe oEmbeds:
Example: Twitter (X) oEmbed which is implemented with a script loader:
Notes: A couple of things to note about the Twitter oEmbed (and similar oEmbeds that use a loader script):
A minimal version of the tweet itself still loads and works even before the JavaScript loads or if JavaScript is disabled
The https://platform.twitter.com/widgets.js script is generic and is loaded for every Twitter oEmbed. Its job is to expand fill its container div content with a full visual representation, including an image preview:
In the Twitter example, the widget.js script replaces the oEmbeds parent div (
wp-block-embed__wrapper) content entirely with an iframe that provides the visual representation:WordPress post embeds
WordPress itself provides an oEmbed capability, and pasting a post URL into the editor results in a WordPress embed - a mini version of the linked post embedded into the post. These embeds are a bit of a special case and while implemented as an iframe, they are hidden until a signal is received from the inner frame, making them a poor match for lazy loading
Optimizing oEmbeds
In my PR I propose optimizing oEmbeds using two techniques:
For oEmbeds delivered as iframes:
For oEmbeds built using script tags:
To Do
WordPress oEmebds need special treatment as mentioned above
The scroll tolerance should be bandwidth based (higher speed connections get a smaller preload window)