Why You Should Avoid document.write(): Complete Guide (2026)
What Is document.write()?
`document.write()` is a legacy DOM API that synchronously injects an HTML string into the document while the parser is still running. Calling it after parsing has finished triggers an implicit `document.open()`, which erases the entire current document and replaces it with the new content. Chrome has warned about it in the Console since 2016 and intervenes (blocks the call) in some 2G/3G scenarios. The standard automated Best Practices audit "Avoids `document.write()`" fails on any page that uses it.
Key Facts (TL;DR)
- Calling `document.write()` after the page has loaded erases the document and starts a new one.
- During parsing, `document.write()` blocks HTML parsing and rendering until the inserted script (if any) finishes loading and executing.
- Chrome intervenes against `document.write()` of a script element on slow connections (2G and Lite Mode), per the 2016 Chrome 53 intervention.
- The Best Practices automated audit `no-document-write` fails any page that calls it (excluding async injection).
- Common offenders: legacy ad tags, old A/B testing snippets, the original analytics.js/ga.js fallbacks, and pre-deprecation Google Optimize loaders.
- Modern replacements are `document.createElement` + `appendChild`, `insertAdjacentHTML`, `innerHTML` (sanitized), and `fetch()` for dynamic content; for scripts use `<script async>` or `<script defer>`.
document.write() Patterns and Their Modern Replacements
| Pattern Using document.write | Modern Replacement | Why It's Better |
|---|---|---|
| document.write('<script src="vendor.js"></script>') | <script src="vendor.js" async></script> or dynamic insertion | Non-blocking, no parser stall, no Chrome intervention |
| document.write('<p>Hello</p>') | el.insertAdjacentHTML('beforeend', '<p>Hello</p>') | Targets a node, never erases the document |
| document.write('<div>' + userInput + '</div>') | const div = document.createElement('div'); div.textContent = userInput; | Auto-escapes user input, prevents XSS |
| document.write(template) with templated HTML | container.innerHTML = sanitize(template) | Bound to a container, idempotent, sanitizable |
| Loading remote HTML inline via document.write | fetch(url).then(r => r.text()).then(html => container.innerHTML = sanitize(html)) | Async, cancellable, observable, CSP-friendly |
| Conditional content swap (A/B test) | CSS class toggle on the body before paint, or server-side variant | No flicker, no layout shift, no parser block |
How to Find document.write() in Your Site
- Run an automated audit and check the Best Practices result "Avoids `document.write()`."
- Search your codebase: `git grep -nE "document\\.write\\b"`.
- Open Chrome DevTools Console and reload; intervention messages appear as "A Parser-blocking, cross site... script... was invoked via document.write."
- Check third-party tags listed in the Network tab; legacy ad and analytics scripts are the biggest source.
- For our companion guides, see fixing console errors and DevTools Issues panel warnings.
How to Replace document.write(): Code Examples
1. Replace a Synchronous Script Injection
// Bad: blocks parser, fails the Best Practices audit, intervened on slow networks
document.write('<script src="https://cdn.example.com/widget.js"></script>');
// Good: non-blocking, runs after parsing
const s = document.createElement('script');
s.src = 'https://cdn.example.com/widget.js';
s.async = true;
document.head.appendChild(s);2. Replace HTML Insertion
// Bad
document.write('<div class="banner">Sale!</div>');
// Good
document.getElementById('banner-slot')
.insertAdjacentHTML('beforeend', '<div class="banner">Sale!</div>');3. Replace User-Generated Content
// Bad: XSS hole
document.write('<p>Welcome ' + name + '</p>');
// Good: textContent escapes everything
const p = document.createElement('p');
p.textContent = 'Welcome ' + name;
document.body.appendChild(p);Common Mistakes (Bad vs Good)
// Bad: "fixing" by wrapping in a setTimeout — STILL erases the page
setTimeout(() => document.write('<p>Late content</p>'), 1000);
// Good: target a known container after DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('content').insertAdjacentHTML(
'beforeend',
'<p>Late content</p>'
);
});How to Test That document.write() Is Gone
- Run an automated audit limited to `no-document-write` (e.g. via a CI audit runner). The audit should pass.
- `git grep -nE "document\\.write\\b"` should return only test fixtures or comments.
- Throttle the Network tab to Slow 3G and reload; no intervention messages should appear in the Console.
- Run a Playwright test that asserts `page.on('console', msg => ...)` does not include the string "document.write."
FAQ
Is document.write() removed from browsers?
No. It is still in the HTML spec for backwards compatibility, but Chrome intervenes on slow networks and the automated Best Practices audit fails the page. Treat it as deprecated for new code.
What does Chrome's "intervention" do?
On 2G or Lite Mode, Chrome blocks the parser-blocking, cross-origin script that was injected by `document.write()`. Your script never runs.
Can I keep document.write() for ads?
Most major ad platforms (Google Ad Manager, Prebid) ship modern async tags. Update to the current vendor snippet; legacy `document.write` ad tags are progressively blocked.
Is innerHTML safer than document.write?
Yes for parsing (it does not erase the document), but not for XSS. Always sanitize untrusted strings or use `textContent` for plain text.
Does document.write() hurt Core Web Vitals?
Yes. It blocks parsing and rendering, hurting LCP and INP. Removing it is one of the highest-leverage fixes for a slow page.
Can I polyfill document.write()?
Some libraries (e.g. Postscribe) emulate it for legacy snippets, but they add their own parser overhead. Replace the source snippet instead.
Is document.write() ever safe?
Only inside the same parsing pass it was issued in (e.g. an inline script in the head writing into the document being parsed). Anywhere else it is dangerous.
Does this affect AI search engines like ChatGPT and Perplexity?
Yes. Content injected via `document.write()` often renders after initial DOM construction, sometimes after AI crawler timeouts, so the ChatGPT crawler, PerplexityBot, and Google AI Overviews can miss it entirely. AI search engines also preferentially cite well-ranked pages, and the parser-blocking behavior hurts Core Web Vitals, indirectly lowering your citation odds.
Conclusion
`document.write()` is a parser-era API that blocks rendering, erases the document if called late, opens XSS holes, and fails the automated Best Practices audit. Replace script injections with `<script async>` or dynamic `createElement` insertion, replace HTML insertion with `insertAdjacentHTML`, escape user input with `textContent`, and load remote HTML with `fetch()`. After the change, the `no-document-write` audit passes and slow-network users actually see your content. Run a Greadme deep scan to find every page on your site still using `document.write()`.
