For web designers and developers, embedding our work within a webpage is common practice, and iframes are a popular choice. But a static iframe viewport can downplay the very feature you might be most proud of - your website's responsive design.
This blog post will guide you through implementing dynamic viewports in iframes, similar to how I did in my portfolio. This technique will allow you to effectively communicate the adaptability of your website across various screen sizes.
React Solution
I'll be using React for this example, but you can adapt the concepts to any other framework or vanilla JavaScript.
We'll create a ResponsiveIframe
component accepting two props:
src
: The URL of the website you want to display in the iframe.viewportWidth
: The width of the viewport in pixels.- Addintional props can be added to customize the iframe further.
interface ResponsiveIframeProps {
src: string
viewportWidth: number
}
const ResponsiveIframe = ({ src, viewportWidth }: ResponsiveIframeProps) => {
return (
<iframe
src={src}
style={{
width: viewportWidth,
}}
/>
)
}
Using this component in its current state would display the iframe with a fixed width, likely overflowing the page. We want the iframe to fill the width and height of its container.
We'll achieve this using the CSS scale property. We'll calculate the scale factor based on the parent container's width and the viewport width, applying it to the iframe.
First, we need a reference to the iframe element. In React, we can use the useRef
hook. In vanilla JavaScript, use document.querySelector
or document.getElementById
.
const iframeRef = useRef<HTMLIFrameElement>(null)
return (
<iframe
ref={iframe}
src={src}
style={{
width: viewportWidth,
}}
/>
)
With a reference to the iframe, we can calculate the scale factor and apply it. The useCallback
hook (not strictly necessary) helps prevent creating a new function on every render.
const resize = useCallback(() => {
const iframe = iframeRef.current
const wrapper = iframe?.parentElement
// Avoid errors when the iframe isn't rendered yet
if (!iframe || !wrapper) return
// Get the width of the wrapper and the iframe
const wrapperWidth = wrapper.offsetWidth
const iframeWidth = iframe.offsetWidth
// Scale is the ratio between the wrapper width and the iframe width
const scale = wrapperWidth / iframeWidth
iframe.style.transform = `scale(${scale})`
// Set the iframe's height to match the parent's height while applying the responsive scale
const wrapperHeight = wrapper.offsetHeight
const height = wrapperHeight / scale
iframe.style.height = `${height}px`
}, [])
We need to call the resize
function whenever the window is resized and on initial render. The useEffect
hook achieves this.
useEffect(() => {
resize()
window.addEventListener('resize', resize)
return () => window.removeEventListener('resize', resize)
}, [resize])
That's it! Use the ResponsiveIframe
component to showcase your work responsively.
Full Code
interface ResponsiveIframeProps {
src: string
width: number
}
const ResponsiveIframe = ({ src, width }: ResponsiveIframeProps) => {
const iframeRef = useRef<HTMLIFrameElement>(null)
const resize = useCallback(() => {
const iframe = iframeRef.current
const wrapper = iframe?.parentElement
if (!iframe || !wrapper) return
const wrapperWidth = wrapper.offsetWidth
const iframeWidth = iframe.offsetWidth
const scale = wrapperWidth / iframeWidth
iframe.style.transform = `scale(${scale})`
const wrapperHeight = wrapper.offsetHeight
const height = wrapperHeight / scale
iframe.style.height = `${height}px`
}, [])
useEffect(() => {
resize()
window.addEventListener('resize', resize)
return () => window.removeEventListener('resize', resize)
}, [resize])
return (
<iframe
ref={iframeRef}
src={src}
style={{ width, transformOrigin: 'top left' }}
/>
)
}
Usage:
const [viewportWidth, setViewportWidth] = useState<number>(1220)
return (
<div>
<ResponsiveIframe src="https://baraa.app" width={viewportWidth} />
<div className="buttons">
<button onClick={() => setViewportWidth(375)}>Mobile</button>
<button onClick={() => setViewportWidth(768)}>Tablet</button>
<button onClick={() => setViewportWidth(1220)}>Desktop</button>
</div>
</div>
)
Vanilla JavaScript Solution
If you're not using React, you can achieve the same result using vanilla JavaScript. Here's how you can do it:
<div class="iframe-wrapper">
<iframe id="iframe" src="https://baraa.app"></iframe>
</div>
<script>
const iframe = document.getElementById('iframe')
const resize = () => {
const wrapper = iframe.parentElement
if (!wrapper) return
const wrapperWidth = wrapper.offsetWidth
const iframeWidth = iframe.offsetWidth
const scale = wrapperWidth / iframeWidth
iframe.style.transform = `scale(${scale})`
const wrapperHeight = wrapper.offsetHeight
const height = wrapperHeight / scale
iframe.style.height = `${height}px`
}
resize()
window.addEventListener('resize', resize)
</script>