Quick Fix with Thunder
Thunder Page Speed helps Shopify stores reduce the damage from non-critical scripts by improving resource timing, script loading, image delivery, and Core Web Vitals pressure. It is the fastest baseline fix before you spend developer time hunting old code across theme files, app embeds, and Google Tag Manager.
Why document.write Hurts Shopify Performance
document.write() is an old JavaScript method that writes markup directly into the page while the browser is parsing HTML. That made sense in older ad and analytics snippets, but it is hostile to modern storefront performance. When the browser encounters a blocking script that calls document.write(), it may need to pause parsing, fetch more JavaScript, inject new markup, and continue only after that work finishes.
Chrome's Lighthouse documentation warns that document.write() can delay visible page content badly on slow connections, and Chrome has blocked some problematic uses in certain network conditions. On Shopify, slow mobile shoppers are exactly the people you cannot afford to punish. If the legacy script appears before the hero, product title, price, or add-to-cart button, it can hurt Shopify LCP, INP, and Total Blocking Time.
The warning also overlaps with reduce JavaScript execution time, legacy JavaScript, and third-party JavaScript blocking Shopify. In practice, the fix is usually not one magic setting. You need to identify which snippet writes to the page, decide whether it is still needed, and replace it with a safer loading pattern.
Where document.write Comes From in Shopify Themes
Start with your theme code. Search theme.liquid, snippets, sections, and assets for document.write. Old themes sometimes use it for badges, trust seals, affiliate trackers, popups, social embeds, store locator widgets, or old browser fallbacks. If you see a script pasted before </head> or near the top of <body>, inspect it first.
Then check uninstalled app leftovers. Shopify apps can leave snippets, script tags, or theme app extension blocks behind. A store can uninstall the app but keep the legacy loader in the theme. That creates a double problem: the script hurts performance and no one owns it anymore. The cleanup workflow in remove unused CSS and JavaScript applies here.
Finally, inspect Google Tag Manager and marketing tags. Some ad vendors still provide old snippets that call document.write() to insert an iframe, tracking pixel, or script dependency. If GTM is part of the issue, use the dedicated guide on fixing GTM slowing down Shopify before changing revenue-critical tracking.
Manual Step-by-Step: Fix Avoid document.write on Shopify
1. Confirm the exact script source
Do not remove scripts by guessing. In PageSpeed Insights, expand the diagnostic and note the script URL. In Chrome DevTools, open the Sources panel or search all loaded files for document.write. If the URL belongs to your theme, fix the theme. If it belongs to an app or tag vendor, find the current async version of the snippet.
2. Replace static writes with Liquid or HTML
If the code writes markup that is always present, it should not be JavaScript at all. Render it in Liquid so the browser receives stable HTML from the server.
<!-- Avoid this old pattern -->
<script>
document.write('<div class="trust-badge">Free shipping over $75</div>');
</script>
<!-- Use normal theme markup instead -->
<div class="trust-badge">
Free shipping over $75
</div> This is safer for layout stability because the element exists from the first HTML response. It also makes the section easier to manage in Shopify's theme editor.
3. Replace dynamic writes with DOM insertion
If JavaScript really needs to create markup after load, use DOM APIs. They are explicit, easier to control, and do not interrupt HTML parsing the same way a parser-time write can.
const slot = document.querySelector('[data-promo-slot]');
if (slot) {
const badge = document.createElement('div');
badge.className = 'promo-badge';
badge.textContent = 'Today only: free shipping over $75';
slot.appendChild(badge);
} 4. Replace script writes with async script creation
Many legacy snippets use document.write() to load another script. Replace that with an async loader and place it after critical content or behind user intent when the script is not needed immediately.
function loadVendorScript(src) {
const script = document.createElement('script');
script.src = src;
script.async = true;
script.defer = true;
document.head.appendChild(script);
}
window.addEventListener('load', () => {
loadVendorScript('https://vendor.example.com/widget.js');
}); 5. Move non-critical vendors later
Review widgets, chat tools, heatmaps, surveys, popups, affiliate badges, and some personalization tools rarely need to run before the first viewport. Delay them until load, idle time, scroll, click, or section visibility. For videos and maps, use a facade instead of loading the full iframe immediately.
const schedule = window.requestIdleCallback || ((callback) => setTimeout(callback, 1200));
schedule(() => {
loadVendorScript('https://vendor.example.com/non-critical.js');
}, { timeout: 2500 }); 6. Test checkout and attribution before publishing
Be careful with consent, analytics, ads, subscription widgets, add-to-cart logic, and checkout handoff. A faster page that loses conversion tracking or breaks a product form is not a win. Test homepage, product page, collection page, cart drawer, checkout redirect, and all marketing events after the change.
Manual Fix vs Thunder Fix
| Problem | Manual fix | Thunder fix |
|---|---|---|
| Old theme snippet uses document.write | Replace it with Liquid, HTML, or DOM insertion. | Creates a faster baseline, but raw legacy code may still need removal. |
| Vendor script loads synchronously | Swap to the current async vendor snippet and test tracking. | Improves common non-critical script timing automatically. |
| Uninstalled app left code behind | Find and remove orphan snippets, assets, and script tags. | Helps reduce storefront resource pressure while you clean leftovers. |
| Embeds inject iframes early | Use a facade or load on interaction. | Pairs well with automated resource prioritization and script deferral. |
How to Verify the Warning Is Fixed
Retest the same URL in PageSpeed Insights and the free Shopify speed test. Check whether the warning disappears or the script source changes. Then record a mobile Performance trace and confirm the legacy script is no longer blocking early parsing or creating long tasks.
Watch the business flow too. Place a test order if needed. Confirm add-to-cart works, consent still fires, analytics events still appear, and ad pixels still respect privacy settings. For a complete cleanup path, follow the complete Shopify speed optimization guide, compare automation in the best Shopify speed apps guide, or use professional speed optimization for risky theme cleanup.
FAQ
What does avoid document.write mean on Shopify?
It means a script is using document.write() to inject markup or another script while the page is loading. On Shopify, this usually comes from old app snippets, ad tags, tracking pixels, affiliate scripts, or custom theme code.
Is document.write still a PageSpeed issue in 2026?
Yes for stores that still see the warning in older Lighthouse reports, PageSpeed tooling, or third-party audits. Chrome's Lighthouse documentation notes that the audit was removed from Lighthouse 13, but document.write() remains a risky legacy loading pattern.
Why is document.write bad for Shopify speed?
document.write() can block parsing, delay rendering, and force the browser to wait on external scripts. Chrome has historically intervened in some slow-network cases because document.write() can delay page display badly.
Can Thunder fix document.write warnings?
Thunder can improve common third-party script loading and storefront performance pressure automatically, but raw document.write() inside a theme or vendor snippet may need to be removed or replaced manually.
What should I use instead of document.write?
Use normal HTML for static content, Liquid for server-rendered Shopify markup, DOM methods for client-side injection, async script creation for third-party loaders, and app embed settings for vendor scripts where possible.