
Heavy third-party embeds can destroy your Webflow page's performance. Many third-party iframes start downloading resources the moment your page loads, creating massive network waterfalls, slow interaction times, and poor Lighthouse scores—even for something as simple as a calendar or map.
Click-to-load is a straightforward performance pattern: don't let the browser load the embed until the user explicitly requests it. Instead of an iframe rendering immediately, visitors see a fast placeholder with a clear "Click to load" action. This tutorial shows you how to implement this pattern in Webflow, verify it works using Chrome DevTools, and fix the three most common failure modes.

Before building anything, confirm you're gating the right embed and choosing the right pattern. Click-to-load works best when used intentionally, not everywhere.
A Webflow embed is usually "heavy" when it triggers any of these on initial load:
If you open DevTools → Network and see huge files from Google, video players, or booking platforms loading immediately, that's a prime candidate for click-to-load.
These patterns are not the same:
A simple rule of thumb: if the embed is visible immediately and not critical to see instantly, use click-to-load. If the embed is below the fold, consider adding loading="lazy" to the iframe instead (or in addition).
Here's a simple example of lazy-loading an iframe:
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
width="560"
height="315"
frameborder="0"
loading="lazy"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>Click-to-load improves initial speed, but it adds friction. Users need one extra click, and the embed loads slightly later (only after the click). If the embed is required to understand the page, click-to-load can feel annoying. Use it where it improves outcomes: faster first impression, smoother scrolling, and fewer initial requests.
This section walks you through the exact Webflow Designer steps. The key principle: the iframe must not have a real URL in src on initial load, otherwise the browser will load it immediately.

Setting a real height here prevents Cumulative Layout Shift. If you skip this, the placeholder and iframe can collapse to zero height or "jump" when loaded.

Here's a working starter snippet you can adapt (replace the data-src URL with your real embed URL):
<!-- Webflow click-to-load iframe (does NOT load until click) -->
<iframe
src="about:blank"
data-src="https://calendly.com/your-calendly-link"
width="100%"
height="700"
frameborder="0"
scrolling="no"
title="Book a call"
brix-iframe-click-on-load="on"
brix-iframe-click-on-load-image="YOUR_WEBFLOW_ASSET_PLACEHOLDER_IMAGE_URL"
></iframe>
This guide covers iframe embeds only—if your provider doesn't offer an iframe option, you'll need a different implementation.
You already saw them in the snippet above. For this tutorial, they must be placed exactly on the iframe tag:
Both must appear on the iframe tag, not on the Embed wrapper or a parent element. If you skip them or move them, the helper script won't activate.
Here's how a fully attributed iframe looks inside your Embed HTML:
<iframe
src="about:blank"
data-src="https://calendly.com/your-calendly-link"
width="100%"
height="700"
frameborder="0"
scrolling="no"
brix-iframe-click-on-load="on"
brix-iframe-click-on-load-image="YOUR_WEBFLOW_ASSET_PLACEHOLDER_IMAGE_URL"
></iframe>
When this publishes, the helper script (described next) scans for brix-iframe-click-on-load="on", wraps the iframe in a clickable placeholder, and swaps src from about:blank to the real URL when the user clicks. This single change stops the iframe from loading until requested.
The helper script is what makes the pattern functional. It finds the BRIX-enabled iframes and creates placeholders dynamically.
In Webflow Designer:
Placing it in Footer Code ensures the script runs after the page content is present. This guarantees the iframe elements are in the DOM when the script executes.
<script>
/*!
* BRIX Templates Click-to-Load Iframes for Webflow
* ----------------------------------------------------------------------------
* Replaces heavy third-party iframe embeds with lightweight placeholders.
* Loads the real iframe only after user interaction.
*
* Usage: brix-iframe-click-on-load="on" brix-iframe-click-on-load-image="..."
* Author: BRIX Templates
*/
(function () {
function init() {
var iframes = document.querySelectorAll('iframe[brix-iframe-click-on-load="on"]');
if (!iframes.length) return;
iframes.forEach(function (iframe) {
var placeholderUrl = iframe.getAttribute('brix-iframe-click-on-load-image') || '';
var realSrc = iframe.getAttribute('data-src') || '';
var currentSrc = iframe.getAttribute('src') || '';
// Enforce correct setup:
// If the iframe ships with a real src, the browser may already have requested it.
// Don't pretend we can "fix" that after the fact—warn and exit.
if (!realSrc && currentSrc && currentSrc !== 'about:blank') {
console.warn('[Click-to-load] This iframe has a real src. Move it to data-src and use src="about:blank" for true click-to-load.', iframe);
return;
}
if (!realSrc) return;
// Hide iframe until activation
iframe.style.display = 'none';
// Create facade wrapper
var wrapper = document.createElement('div');
wrapper.style.position = 'relative';
wrapper.style.width = '100%';
wrapper.style.overflow = 'hidden';
wrapper.style.cursor = 'pointer';
wrapper.style.backgroundPosition = 'center';
wrapper.style.backgroundSize = 'cover';
wrapper.style.backgroundRepeat = 'no-repeat';
// Robust height handling
var h = iframe.getAttribute('height');
if (h && String(h).trim() !== '') {
wrapper.style.height = /^\d+$/.test(h) ? (h + 'px') : h;
} else {
wrapper.style.height = '100%';
wrapper.style.minHeight = '240px';
}
if (placeholderUrl) {
wrapper.style.backgroundImage = 'url("' + placeholderUrl + '")';
} else {
wrapper.style.background = '#f2f2f2';
}
// Button for accessibility + intent
var button = document.createElement('button');
button.type = 'button';
button.setAttribute('aria-label', 'Load embed');
button.textContent = 'Click to load';
button.style.position = 'absolute';
button.style.left = '50%';
button.style.top = '50%';
button.style.transform = 'translate(-50%, -50%)';
button.style.padding = '12px 16px';
button.style.border = '0';
button.style.borderRadius = '8px';
button.style.fontSize = '16px';
button.style.lineHeight = '1';
button.style.cursor = 'pointer';
wrapper.appendChild(button);
// Insert wrapper before iframe, then move iframe inside wrapper
var parent = iframe.parentNode;
parent.insertBefore(wrapper, iframe);
wrapper.appendChild(iframe);
function activate() {
iframe.setAttribute('src', realSrc);
iframe.style.display = 'block';
wrapper.style.backgroundImage = 'none';
button.remove();
wrapper.style.cursor = 'default';
}
button.addEventListener('click', function (e) {
e.preventDefault();
activate();
});
// Optional: click background to load too
wrapper.addEventListener('click', function (e) {
if (e.target === wrapper) activate();
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
This script uses plain JavaScript and does not rely on jQuery. It finds each iframe with the BRIX attribute, wraps it with a styled placeholder, and swaps src on click.
After publishing your Webflow site with the click-to-load iframe, use Chrome DevTools to confirm the pattern is working correctly. This verification step is critical because incorrect configurations often fail silently, and you'll only discover the problem by checking Network activity and element attributes.

After the hard reload, look at the Network panel and filter requests by the third-party provider's domain (e.g., calendly.com, google.com/maps, youtube.com, vimeo.com). If click-to-load is working correctly, you should not see requests to these domains before you interact with the placeholder.
If you immediately see provider requests appearing, the iframe is loading prematurely. Common causes:
Fix these issues before moving to the next step.
In DevTools, switch to the Elements tab. Find your iframe (you can right-click the placeholder on the page and select Inspect). Confirm:
If any of these are missing or incorrect, return to Webflow Designer, correct the Embed HTML, and republish.
With Network still open and Preserve log enabled, click the placeholder. You should immediately see new requests to the provider domain appearing in the Network panel. Switch back to Elements and inspect the iframe element again—the src attribute should now contain the real embed URL (matching what was in data-src).
If clicking does nothing:
Look at the placeholder before clicking. If you specified a brix-iframe-click-on-load-image, you should see that image as the background. If the placeholder is blank or shows a broken image icon, open the image URL directly in the browser to verify it's accessible.
Common fixes:
If the placeholder renders correctly and the iframe loads only after click, click-to-load is working as designed.
Once the pattern works for one iframe, you can scale it across your entire site.
Use the same Embed element setup for each heavy iframe:
Because the helper script scans for brix-iframe-click-on-load="on" globally, you don't need to duplicate script logic. Add the attributes once, and the script handles the rest automatically.
For CMS-driven pages with many embeds, click-to-load is often better than lazy-loading because you might have many embeds near the top (maps, directories, "locations" lists). Keep placeholder images lightweight and preferably hosted in Webflow Assets to avoid broken hotlinks. Use one global helper script (Section "Add the click-to-load helper script in Webflow custom code") so each new CMS item automatically gets the behavior.
Click-to-load is a pattern where a Webflow page shows a lightweight placeholder first, and only loads the real embed after the user clicks. Set the iframe src to about:blank, store the real URL in data-src, and use a script to swap the URL on click. Best for above-the-fold embeds you don't need to render immediately.
An iframe is a separate document that can load its own scripts, styles, fonts, and requests. When Webflow renders your page, the browser immediately starts fetching the iframe source and its dependencies, increasing blocking time and creating a huge Network waterfall. Using click-to-load can reduce those early requests for non-essential embeds.
Lazy-loading delays an iframe until it's near the viewport, while click-to-load delays the iframe until the user explicitly interacts. Click-to-load is better for above-the-fold embeds or when you want maximum control over initial performance. For below-the-fold embeds, try loading="lazy" first.
Use click-to-load when you want to guarantee an embed does not load until requested—especially for above-the-fold calendars, maps, or videos that aren't essential to see immediately. It's also strong for pages with multiple embeds, because even lazy-loading can trigger early loads if embeds are near the viewport. Start with your heaviest single embed first.
Set src to about:blank, store the real URL in data-src, and use a click handler to swap src from data-src after the user clicks. The helper script shown in this tutorial automatically creates a placeholder and performs the swap. Keep the script in Footer Code for consistent behavior.
Place brix-iframe-click-on-load="on" directly on the iframe tag inside your Embed element's HTML, not on the Embed wrapper or parent. After publishing, inspect in DevTools to confirm the attribute is on the iframe itself and that src is about:blank before clicking.
Add brix-iframe-click-on-load-image="YOUR_IMAGE_URL" on the iframe tag with a real, públicamente accesible image URL. Upload a compressed placeholder to Webflow Assets for reliability, and set a fixed height on the Embed element to prevent layout shift.
The iframe likely has a real URL in src instead of about:blank, or the BRIX attributes are on the wrong element. Ensure src is about:blank and the real URL is in data-src. Verify in Network that no provider requests fire until click.
Use DevTools → Network with Preserve log enabled. Hard reload and filter for the embed provider domain. You should not see provider requests before interaction. After clicking, requests should start immediately and the iframe src should change from about:blank to the real URL.
Yes—click-to-load delays embed content until user interaction, and crawlers may not execute that interaction. If the embed contains important indexable content, gating it can reduce what search engines see. Keep critical content as native Webflow text and treat embeds as enhancements. For purely functional embeds (booking, map, video), click-to-load is usually fine.
Click-to-load is one of the most practical ways to protect Webflow performance when you can't avoid heavy third-party embeds. You reduce initial network requests, keep the page responsive, and give users a clear, intentional interaction before loading expensive widgets.
Next steps: apply the pattern to your heaviest iframe embed first, then expand to multi-embed pages, modals, and CMS-driven lists. If you want a reusable, scalable solution across a large Webflow build, an experienced team can help you implement it cleanly and consistently. If you'd like hands-on assistance, our agency can help with Webflow performance optimization.
Install Reddit Pixel on Framer via GTM or Custom Code, track Lead conversions on thank-you pages, and verify firing with Pixel Helper.

Install Reddit Pixel on Webflow via GTM or Head code, track Lead conversions on thank-you pages, and verify firing with Pixel Helper.
Step-by-step Framer guide to install Pinterest tag site-wide via GTM, add Lead event on thank-you pages, and verify with Tag Helper.