Editor’s note: This article was last updated on 28 December 2023 to explore integrating MUI with React iframes for styling, and handling events in React iframes.
When building webpages, developers often need to include resources from other webpages. Some common examples that you may recognize from browsing the web include the share button from X ( formerly Twitter), the like button on Facebook, and the map display from Google Maps.
A popular way to retrieve this type of data is with an iframe. Short for inline frame, an iframe is essentially a frame within a frame. Using the <iframe/>
tag, you can easily embed external content from other sources directly into your webpage. Developers also use iframes to isolate certain resources from the same webpage, for example, encapsulating components in iframes by rendering them.
In this tutorial, we’ll explore iframes in React by looking at these two different use cases. First, we’ll cover some background information about how iframes work and how we should use them. Let’s get started!
When a resource is rendered in an iframe, it functions independently of the parent component where it is embedded. Therefore, neither the parent component’s CSS styling nor its JavaScript will have any effect on the iframe.
In React, developers use iframes to create either a sandboxed component or an application that is isolated from its parent component. In an iframe, when a piece of content is embedded from an external source, it is completely controlled by the source instead of the website it is embedded in.
For this reason, it’s important to embed content only from trusted sources. Keep in mind also that iframes use up additional memory. If the content is too large, it can slow down your webpage load time, so you should use iframes carefully.
First, let’s learn how to embed pages from external sources, which is probably the more common use case of iframes. Nowadays, you rarely see a web app that doesn’t have any content loaded from an external source.
For example, consider how many YouTube videos you find on webpages, Instagram posts you see outside of the app, Facebook comment sections on blogs, and even ads on webpages. Each of these elements is embedded into the website, which can range in complexity from a single line of code to an entire code section.
We can use the following line of code to add an X post button to a React app:
<iframe src="https://platform.twitter.com/widgets/tweet_button.html" ></iframe>
We’ll use the code snippet above in the following code to generate a post button like the one seen in the following screenshot. When a user clicks the post button, the selected content will open in a new post on their X homepage:
function App() { return ( <div className="App"> <h3>Iframes in React</h3> <iframe src="https://platform.twitter.com/widgets/tweet_button.html"></iframe> </div> ); } export default App;
Now, let’s review some useful attributes of the iframe tag, which will allow you to modify and customize your iframes. For one, src
is used to set the address of the webpage that you want to embed. For example, we can use the src
tag to embed a YouTube video as follows:
<iframe src="https://www.youtube.com/embed/uXWycyeTeCs" ></iframe>
srcdoc
is used to set an inline HTML to embed. Note that the srcdoc
attribute will override the src
attribute if both are present. In the code snippet below, we’ll override the src
command for a YouTube video with the srcdoc
command, which uses a hello message as a placeholder:
<iframe src="https://www.youtube.com/embed/uXWycyeTeCs" srcDoc='<p>Hello from Iframe</p>' ></iframe>
We’ll use height
and width
attributes to set the dimensions of our iframe. The default unit is pixels, but you can use other units as well. In the code snippet below, we’ll set the dimensions of an iframe that displays a YouTube video, as seen in the following screenshot:
<iframe src="https://www.youtube.com/embed/uXWycyeTeCs" width={1000} height={500} ></iframe>
The allow
attribute sets the features available to the <iframe>
based on the origin of the request, for example, accessing autoplay, microphone, and more.
In the screenshot below, we set allow
for our YouTube video with the following values: accelerometer
, autoplay
, clipboard-write
, encrypted-media
, gyroscope
, and picture-in-picture full
:
<iframe src="https://www.youtube.com/embed/uXWycyeTeCs" width={1000} height={500} allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture full"></iframe>
The title
attribute is used to set a description for the content in the iframe. While the title
attribute has no effect on the UI of the iframe, it is helpful for accessibility, providing valuable input to screen readers. In the code snippet below, we’re adding a title
to our YouTube video:
<iframe src="https://www.youtube.com/embed/uXWycyeTeCs" width={1000} height={500} title='A youtube video on React hooks'></iframe>
Next, we’ll use the name
attribute to set the iframe name and use it to reference the element in JavaScript. Similarly, you can also set the name
attribute as the value of the target
attribute of an a
or form
element or the value of the formtarget
attribute of an input
or button
element.
To set restrictions on the content of the iframe, we use the Sandbox
attribute. As mentioned earlier in the tutorial, we can’t control the content sent from an external source, however, we can restrict what we accept in the iframe using Sandbox
. To apply all restrictions, leave the value of the attribute empty. Or, you can add flags to relax the restrictions.
For example, allow-modals
will allow the external page to open modals, and allow-scripts
will allow the resource to run scripts:
<iframe src="https://www.youtube.com/embed/uXWycyeTeCs" width={1000} height={500} sandbox='allow-scripts allow-modal' ></iframe>
Properly using the Sandbox
attribute and its flags can greatly improve your app’s security, especially if the resource you’re embedding is from a third party.
To set how the browser should load the iframe content, we’ll use the loading
attribute; loading
takes either eager
or lazy
. When set to eager
, the iframe is loaded immediately, even if it is outside the visible viewport, which is the default value. lazy
delays loading until it reaches a calculated distance from the viewport, as defined by the browser:
<iframe src="https://www.youtube.com/embed/uXWycyeTeCs" width={1000} height={500} sandbox='allow-scripts allow-modal' loading='eager'></iframe>
When you don’t need to control the content that is being rendered in an iframe, the method we just covered is a good strategy. If you want to show your page visitors a video on YouTube or a post on Instagram, you can simply add the URL to the src
attribute.
But what if you are building a complex app, for example, a CodeSandbox that allows users to build standalone apps on the same platform, or a chatbot that gets triggered when a user clicks a button? In both examples, you are in control of the content, but you also want to isolate them from the rest of the app.
In this section, we’ll explore rendering a React app or component in an iframe. This is a good strategy when you want to cut CSS excesses or use a full-fledged app in another app without any interferences, especially when you want the content of the iframe to share state with its parent.
Let’s try to render our iframe as a direct child of the iframe:
function App() { return ( <div className="App"> <p>Iframes in React</p> <iframe> <MyComponent /> </iframe> </div> ); } export default App;
However, when we run the code above, we get nothing:
You can only use the src
attribute to set the URL that we want to render. Because we are trying to render a component in the same app, the src
attribute won’t work.
Alternatively, we could use the srcdoc
attribute, which takes in an inline HTML to embed. However, we’re then trying to render an entire app or component, which uses extensive and verbose code. We need to find a way to render the component in the iframe body instead of as a child of it. For that, we’ll use a portal.
According to the React documentation, portals allow us to render children into a DOM node that exists outside of the parent component’s DOM hierarchy. Basically, portals let us render children wherever we want to.
You can create a portal with the following command:
ReactDOM.createPortal(children, domNode, key?)
In this case, the children
parameter can be a piece of JSX, a React Fragment, a string, a number, or an array of these. The domNode
parameter is the DOM location or node to which the portal should be rendered. The optional key
parameter is a unique string or number React will use as the portal’s key.
With a React portal, we can choose where to place a DOM node in the DOM hierarchy. To do so, we’ll first establish a reference to an existing and mounted DOM node. In this case, it would be in the contentWindow
of a given <iframe>
. Then, we’ll create a portal with it. The portal’s contents are also considered children of the parent’s virtual DOM.
Let’s say we have the following file called MyComponent.js
:
import React from "react"; function MyComponent() { return ( <div> <p style={{ color: "red" }}>Testing to see if my component renders!</p> </div> ); } export default MyComponent;
Now, let’s create a file called CustomIframe.js
and write the following code:
import React, { useState } from 'react' import { createPortal } from 'react-dom' const CustomIframe = ({ children, ...props }) => { const [contentRef, setContentRef] = useState(null) const mountNode = contentRef?.contentWindow?.document?.body return ( <iframe {...props} ref={setContentRef}> {mountNode && createPortal(children, mountNode)} </iframe> ) } export default CustomIframe;
We created a ref
with the useState()
Hook, therefore, once the state is updated, the component will re-render. We also got access to the iframe document body, and then created a portal to render the children passed to iframe
in its body instead:
import "./App.css"; import CustomIframe from "./CustomIframe"; import MyComponent from "./MyComponent"; function App() { return ( <CustomIframe title="A custom made iframe"> <MyComponent /> </CustomIframe> ); } export default App;
You can pass any React app or component as a child of CustomIframe
, and it will work just fine! The React app or component will become encapsulated, meaning you can develop and maintain it independently.
You can also achieve the same encapsulation as above using a library called React <Frame /> component. To install it, run the following command:
npm install --save react-frame-component
Encapsulate your component as follows:
import Frame from 'react-frame-component'; function App() { return ( <div className='App'> <p>Iframes in React</p> <Frame > <MyComponent /> </Frame> </div> ); } export default App;
As explained above, the content of an iframe is a complete document with its markup and static assets like JavaScript, images, videos, fonts, and styling. The default Material UI (MUI) settings will not apply styling to React components rendered inside an iframe out of the box. An iframe is an isolated environment, therefore you need to integrate MUI with the iframes in your React project.
By default, MUI uses emotion as its styling engine under the hood, and the generated styles are injected into the parent frame’s head
element. However, the styles aren’t inserted into the iframe out of the box because the iframe’s document is different.
You can use the @emotion/cache
package with the <CacheProvider/>
component to get emotion to work within embedded contexts like iframes. The @emotion/cache
package is not installed by default. You need to install it from the npm package registry like so:
npm i @emotion/cache
After installing @emotion/cache
, you can add the changes below to the CustomIframe
component wrapper we created while learning how to use the React portal to render a component in an iframe:
import React, { useState } from "react"; import { createPortal } from "react-dom"; import { CacheProvider } from "@emotion/react"; import createCache from "@emotion/cache"; const CustomIframe = ({ children, ...props }) => { const [contentRef, setContentRef] = useState(null); const cache = createCache({ key: "css", container: contentRef?.contentWindow?.document?.head, prepend: true, }); const mountNode = contentRef?.contentWindow?.document?.body; return ( <CacheProvider value={cache}> <iframe {...props} ref={setContentRef}> {mountNode && createPortal(children, mountNode)} </iframe> </CacheProvider> ); }; export default CustomIframe;
In the code above, we wrapped the iframe inside the <CacheProvider/>
component. The generated styles will be inserted into the iframe’s head
element. You can also insert them into a DOM node other than the head
element.
As explained above, communication between an iframe and its parent frame is subject to the same origin restriction because of security concerns. Therefore, most features are restricted if the iframe is from a different origin than the parent frame.
However, the parent frame can still communicate with an embedded iframe via events using the window.postMessage()
API, though they may not be from the same origin.
For the same-origin frames, you can access the iframe from its parent. Similarly, you can also access the parent frame from the iframe.
An iframe is a document with assets like JavaScript, CSS, images, and audio. It will take a bit of time to load. Therefore, it’s good practice to render a loading indicator while the iframe is still loading to provide a good user experience.
A fully loaded iframe will emit the onLoad
event. You can update the loading state in the onLoad
event handler to remove the loading indicator when the iframe has finished loading:
function App() { const [loadingIframe, setLoadingIframe] = useState(true); return ( <> <iframe src="https://www.youtube.com/embed/uXWycyeTeCs" onLoad={() => setLoadingIframe(false)} ></iframe> {loadingIframe ? <p> Loading iframe</p> : null} </> ); }
As hinted above, the content of an iframe is a complete document with markup and other static assets like images, styles, and JavaScript. These assets use bandwidth, take time to load, and use memory after loading. Therefore, it is necessary to optimize the iframe page load.
If your iframe renders a video, you can optimize it by rendering a simple thumbnail or placeholder image and loading the iframe after the user clicks it. It ensures faster page load, and the user only downloads and watches the video if it is necessary to do so.
Furthermore, you can optimize the iframe load by setting the iframe’s loading
attribute to lazy
. If you load all the iframe resources as soon as the page loads, you pay the ultimate penalty of loading off-screen content that the user may not see or interact with:
<iframe src="https://www.youtube.com/embed/uXWycyeTeCs" width={1000} height={500} loading="lazy" ></iframe>;
Doing so will defer loading off-screen iframes until the user scrolls near them. As a result, it increases page load speed, saves bandwidth, and reduces your site’s memory usage.
Omitting the loading
attribute is equivalent to setting its value to eager
. The browser eagerly loads the iframe as soon as the page loads, irrespective of whether the user will see or interact with it.
In this tutorial, we explored iframes in React in two different use cases. For one, we learned how to embed external content from a webpage into a web application with an iframe, separating the JavaScript and CSS of the parent and child elements. Secondly, we learned how to isolate certain parts of our app in an iframe.
iframes are a useful element that is essential for developers to learn. I hope you enjoyed this tutorial!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
3 Replies to "Best practices for React iframes"
Hi, this is great and got me where I needed to be. HOWEVER, the imported .css/.scss styles for the iframe child components are being generated “outside” the iframe. Is there a fix for this?
Did you find a workaround for this behavior? I attached the link HTML element to the iframe head with href attribute that leads to the .css in the public folder. But I suppose this is not the best to achieve this…
I want to replace the video with a image, and when the user clicks the image, a floating youtube player comes up on the screen. How can we achieve this?