Third-party embeds: script embed vs iframe embed. Which should you choose?
Today, I will explore the 2 main methods of embedding third-party content in a website: a script embed and an iframe embed. I will compare them across a few dimensions and outline some best practices. Let’s dive in!
What are third-party embeds?
Third-party embeds allow you to include many different types of external content such as code demos (Codepen, JS Fiddle, CodeSandbox) and hosted videos (YouTube, Vimeo) on your webpage. A third-party embed is any content displayed on your site that is not directly authored by you, and is served from third-party servers.
To embed the content, you need to copy a code snippet and include it on your webpage.
Sometimes, there is more than one embed option. The embed snippets vary from website to website but fall into 2 general categories: a script embed and an iframe embed.
The other embed options tend to be offering a platform-specific snippet – for example, Codepen offers a Wordpress Shortcode to use on Wordpress.
It is common practice for people to use a shortcode (used with templating languages such as Liquid) or a web component (a custom HTML element or a framework-specific component such as React) to encapsulate the embed code to avoid duplication and make maintenance easier.
For example, I built this website with Eleventy, and I added a nunjucks shortcode that enables me to embed a codepen with the following code:
It has 3 mandatory parameters: URL, title, and height.
What is the difference between an iframe embed and a script embed?
An iframe embed
An iframe embed is an iframe
element with various attributes populated.
For example, this is the snippet that Codepen produces for my codepen found at https://codepen.io/robjoeol/pen/gOEbPvZ:
At a minimium, the iframe
will have the src
attribute populated to specify the page to embed. A height
is usually included too. The default size of an iframe is 300 pixels by 150 pixels if the width
and height
are omitted.
I will discuss the other attributes in more detail later as they can influence how the iframe
will look and behave.
A script embed
A script embed involves running a third-party script. Generally, the script
creates a iframe
element, populates some attributes, and inserts it into the DOM of the page at the location of the script tag.
The purest example of this is JSFiddle. For example, if I want to the embed the demo with the URL of https://jsfiddle.net/robjoeol/6da74g2y/, its default embed snippet is: <script async src="//jsfiddle.net/robjoeol/6da74g2y/8/embed/"></script>
.
This is the result after the script has run:
The nice thing about the embed script provided by JSFiddle is that you can read it! It is not minified and obfuscated like most scripts these days! Here is what it looks like:
A common variation on this is that the embed snippet contains a block element such as a p
that acts a placeholder, along with the script
. The script transforms that p
into an iframe
.
For example, this is the snippet for Codepen’s “HTML (Recommended)” embed for the same codepen mentioned earlier:
The p
element has some data attributes that is used by the script, and some inline styles. The script replaces the p
element with the following markup:
The iframe
produced has some different attributes than the “Iframe” embed, and is wrapped in a div
. Since the script is minified, we cannot tell what it does exactly.
Why would you chose a script embed over an iframe embed?
Let’s explore why you would chose one over the other across a few dimensions. In both cases, it is worthwhile auditing the embed code snippet before you use it in production.
Aspect | Script embed | Iframe embed | Which is preferable? |
---|---|---|---|
Performance | The main perf benefit of this method is to make the loading of the iframe non-blocking (asynchronous). The iframe is loaded some time after the intial page load. | If you can lazy load the iframe , the performance is optimal. | Typically, an iframe with lazy loading is preferable because the resources are only loaded when the iframe is in view. There are iframe embeds like YouTube where a “facade” script such as lite-youtube-embed might be preferable to make the loading of the iframe user-initiated. |
UX | This method injects an iframe into the DOM. This can a cause a cumulative layout shift (CLS) if the correct space is not reserved for the iframe . The provider may not be doing this for you. | An iframe has an intrinsic size with minimum dimensions, it will not cause a cumulative layout shift. | The “iframe” embed is more foolproof because it never contributes to CLS. If you audit the behaviour of the script on a page, perhaps it does not cause a layout shift and delivers the same experience. |
Security/Privacy | Running a third-party script gives access to a page and an user via cookies. It is more prone to security and privacy concerns. | An iframe is inherently more secure as is an isolated environment (sandbox) that restricts access to the DOM. It is possible to break out of the sandbox if certain attributes are provided. | An iframe is inherently more secure than a script because it is an isolated environment. However, you still need to be vigilant on what permissions you are granting to an iframe . If you are negligent, an iframe can have the same access to a page as a script . |
Accessibility | I don’t know if there is any negative effect on accessibility when you inject an iframe into a page with a script, but I have spotted accessibility issues more often on injected iframes. | An iframe is easier to audit with accessibility tools because it is not an injected element. | I have noticed that some script embeds don’t provide an unique title for the injected iframe. An iframe is preferable because an issue with a missing or invalid title can be identified by auditing tools more easily. |
Syndication (Feeds) | - | - | Some feed readers strip out iframe and script elements from content. If you want to make your feed more interoperable, you can substitute the iframe for a link to the content when you generate your feed. A script embed may have a placeholder element with a link that can save you doing the work yourself. You can partly emulate this for iframe embeds with by using the scripting CSS media feature to provide some alternative markup when scripting is prohibited. |
Progressive enchancement | - | - | A script embed is more amenable to progressive enhancement than an iframe embed. Only some embed providers have a link to offer a base experience for all users. It is probably best to reach for an open source solution or write your own. |
There is not a clear winner. You should take it on a case-by-case for each provider. Let’s take a closer look at each of the dimensions.
Performance
Lazy loading an iframe
should perform better than a script embed most of the time. It will not load an iframe
that is not immediately required i.e. out of view.
In some cases, a “facade” script like lite-youtube-embed
may be preferable. A facade script provides a placeholder with a cover image and a button, it will load the iframe when the button is clicked. I discussed this scenario in a previous post on YouTube embeds.
The primary performance benefit of a script embed is that it makes the loading of the iframe non-blocking (asynchronous). This improves the rendering time of the page. This is usally done by adding async
or defer
attribute to the script
tag. The default embed that JSFiddle offers falls in this category.
The performance benefit of an async script is limited because the iframe will still be loaded and take up resources shortly after the page has loaded. Since every iframe
is a complete document environment, it requires significant computing resources. Lazy loading will reduce loading these heavier resources upfront.
I would audit the performance of embeds properly. Favour using the browser devtools or real user monitoring test tools. Lab tools such as Lighthouse can be misleading, especially if you are not familiar with them!
User Experience (UX)
A potential drawback of a script embed is that it may cause a layout shift. If the iframe
is inserted into the DOM, it can cause content to move. It is not good for user experience when what you are looking suddenly moves offscreen! You can catch this through the Cumulative Layout Shift (CLS) performance metric.
To prevent this, you need to reserve the right amount of space for the iframe
that will be inserted later. Some embed providers do this by having a div
element (or similar block element) with a declared height
, and the script creates an iframe
with the same height to replace that element.
An iframe
embed is not susceptible to causing CLS because it does not involve DOM manipulation and it has intrinsic dimensions. An iframe is 300 pixels by 150 pixels (W x H) by default.
Security/Privacy
Embedding a script on your website is an act of faith. You are likely to be giving it full access to do what it wants to your site. The script could include tracking or something malicious. It is a good idea to verify what it does before you use it!
The problem is that typically only a minified version of the embed script is available. You can’t verify what it does unless you have compiler-like reading ability! As I demonstrated earlier, JS Fiddle was one of the few providers I saw that has an unminified script that you can read through. In their case, they do what you would expect them to.
Security is generally lax with scripts embeds. Measures such as subresource integrity are rarely used. If you are not familiar with subresource integrity, it verifies that resources fetched are delivered without unexpected manipulation. This is done through the integrity
attribute that has a base64-encoded sha384 hash.
Also, it is realm of possibility to perform a malicious rug pull. The provider could completely update the script at the URL that you reference in the src
attribute of the script
.
An iframe
is inherently more secure than a script. Loading untrusted code in an iframe
provides a measure of separation between your website and the content you’d like to load. The framed content won’t have access to your page’s DOM or your browser’s cookies. It is more secure than a script by default.
However, you still need to be vigilant on what permissions you are granting to an iframe
. If you are negligent, an iframe
can have the same access to a page as a script
. If you want to restrict what the iframe
can do, then you can use the sandbox
attribute. An iframe that has an empty sandbox
attribute is fully sandboxed. It can do very little. Each of the restrictions can be lifted by adding a flag to the sandbox attribute’s value. For example, allow-scripts
allows the frame to run JavaScript.
One thing to watch out for is using both the allow-scripts
and allow-same-origin
flags. This is not recommended, because it effectively enables an embedded page to break out of all sandboxing and makes an iframe
similar to a script
. Some embeds such as Twitter’s Tweet embed requires allow-scripts
to run JavaScript and allow-same-origin
to access twitter.com’s cookies to enable posting a tweet (the form) as an authenticated user. You may want this behaviour sometimes, but you should be aware of the risk of doing this.
Another option is to write your own script that mirrors what the provider does.
Accessibility
People navigating with assistive technology such as a screen reader use the title
attribute of an iframe
to understand its content. The title’s value should concisely describe the embedded content:
Without this title, they have to navigate into the iframe
to determine what its embedded content is. This context shift can be confusing and time-consuming, especially if embeds contain interactive content like video or audio.
I noticed that some script embeds generate an iframe without an unique title such as Codepen (has been fixed since). I discussed that in a previous post.
It is harder to miss this issue with an iframe embed. You would hope audting tools would catch issues like this regardless. Also, I noticed that Google’s Lighthouse tool does not flag if there are non-unique iframe titles in their accessibility audit.
Syndication (Feeds)
Feed readers (RSS readers) often remove iframe
and script
tags from articles to protect users. This can limit the reading experience if there is just a blank space in the article where the embed is expected.
The W3C Feed Validator gives the following recommendation when it encounters an iframe
in a feed:
This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.
line 26, column 0: content should not contain iframe tag (13 occurrences) [help]
It will improve interoperability if you provide a fallback for an iframe
– provide a link (a
element) to the content instead. I don’t see many people do this frankly!
If a script embed has a placeholder element, it can provide a fallback. Codepen’s script embed has an alternative link if the script is blocked.
An iframe embed does not have that ability to provide a fallback. I noticed that Codepen tried to get around this for their iframe embed by putting a link inside the tags. Technically this is invalid HTML because the iframe
element does not permit content. However it may work in some feed readers.
You can provide a partial fallback for iframe embeds when scripting is prohibited (usual case in RSS readers) by using the scripting
CSS media feature to provide some alternative markup. Below is an example for a YouTube video:
I think that it is best to transform the content you provide for a RSS feed, replace iframes with links explicitly. My experience as a RSS consumer is that most developers just port the same content from the webpage to the feed.
A script embed that has a placeholder element can save you doing the work yourself if it is done correctly. In any case, you should audit this if you want to maximise the experience for the readers of RSS feed.
Progressive enhancement
Progressive enhancement is a design philosophy that puts emphasis on content first, allowing everyone to access the basic content and functionality of a web page. The idea is to create a base experience that will work for all users and then build on top of that, improving or enhancing the experience for users that can run all the required code.
For embeds, this usually amounts to:
- The base experience can be a link to the embedded content.
- Some CSS styles can make the link more appealing. A preview placeholder is preferable. If, for any reason, the CSS is not loaded or applied, you still have the base experience.
- Some JavaScript will replace the link with an
iframe
. This can be done when the page loads, or it can be triggered by an user clicking on a button in the preview placeholder. Again, if the user has JavaScript disabled or the script doesn’t run, the link will still be fully functional.
Alvaro Montoro did a nice write-up on creating a progressively enhanced codepen embed that goes through how you can execute the above steps.
A script embed is more amenable to progressive enhancement than an iframe embed. Only some embed providers expose a link to provide a base experience for all users. For this reason, often you are unlikely to use the provider’s default embed snippet.
There is a growing list of open source web components and solutions for embeds that provide a great progressive enchancement experience e.g. lite-youtube
and lite-vimeo-embed
.
Best practices for embeds
- As a general rule of thumb, I would not use the default snippet offered by providers. At the very least, audit the embed across the dimensions discussed in this article. You are more likely to reach for someone’s plugin or web component, review these too!
- If you are using a script embed:
- I would not use the script from a provider if you cannot review the source code. It is better for the integrity and security of your website to use a script that you have inspected or written yourself.
- The initial HTML should have a block element with a specified height that acts as a placeholder. This will prevent a layout shift when the
iframe
is inserted (in its place). - If you want to provide a base experience to support progressive enhancement, the initial HTML should include a link to the content.
- Ensure the script does not block rendering of the page. This can harm the performance of the page. Add the
async
ordefer
attribute to thescript
tag. - To optimize performance, consider using the facade pattern or island pattern for script embeds. This will defer loading assets until the embed is required or the user interacts with the embed. You can consider the
is-land
component for a general purpose lazy loading solution. - Review the resulting
iframe
after the script has run.
- If you are using an iframe embed:
- To improve performance, you will want to lazy load the embeds not initially needed. You can do this by adding
loading="lazy"
to openingiframe
tag. - It is preferable to set a
width
andheight
for the iframe. If omitted the default dimension is 300 pixels by 150 pixels (W x H). - Review the attributes:
- To improve performance, you will want to lazy load the embeds not initially needed. You can do this by adding
Conclusion
Third-party embeds can enrich your webpages with external content. They are regularly used to include videos and code demos in pages. I would urge you to be mindful of the impact that they can have on your website. They can affect the performance, user experience, integrity, and security of your website dramatically.
I recommend that you do not blindly use the default embed snippets provided. The cautionary tale is YouTube embeds that are terribly bloated and slow. There are open source solutions for embeds that deliver superior performance and user experience e.g. lite-youtube
for YouTube embeds. Often they use lazy loading or utilise the facade pattern to optimize performance. Also, do not be afraid to write your own script - that way you know what is happening!
When it comes to choosing a script embed or iframe embed, the predictable answer is “it depends”. You should take it on a case-by-case for each provider. I recommend that you audit the embed across the dimensions discussed in this article to seek out the optimal result.