`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.
| 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 |
// 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);// Bad
document.write('<div class="banner">Sale!</div>');
// Good
document.getElementById('banner-slot')
.insertAdjacentHTML('beforeend', '<div class="banner">Sale!</div>');// 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);// 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>'
);
});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.
On 2G or Lite Mode, Chrome blocks the parser-blocking, cross-origin script that was injected by `document.write()`. Your script never runs.
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.
Yes for parsing (it does not erase the document), but not for XSS. Always sanitize untrusted strings or use `textContent` for plain text.
Yes. It blocks parsing and rendering, hurting LCP and INP. Removing it is one of the highest-leverage fixes for a slow page.
Some libraries (e.g. Postscribe) emulate it for legacy snippets, but they add their own parser overhead. Replace the source snippet instead.
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.
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.
`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()`.