usability.cat
Issue Wiki

document.write() Usage

Rewriting the entire page while someone is reading it — that's what document.write() does. There are much better ways to update the DOM.

What is this?

Imagine someone is reading a book, and you rip out the page they are on and replace it with completely different text — mid-sentence. That is what document.write() does. It injects HTML directly into the document stream, and if called after the page has finished loading, it wipes the entire page and replaces it with whatever you wrote. Even when called during loading, it blocks the HTML parser, forcing the browser to stop everything while it processes the injected content.

Why it matters

  • For your visitors: document.write() blocks the HTML parser, which means the browser cannot continue rendering the page until the write operation completes. If the written content includes external scripts, the delay compounds. Visitors see a blank or partially rendered page longer than necessary. In the worst case (called after load), the entire page disappears and is replaced — a catastrophic experience.
  • For your business: Google's Lighthouse explicitly flags document.write() as a performance problem. Chrome actively intervenes on slow connections by blocking document.write() calls that inject external scripts, which means your code might silently break for visitors on mobile networks. Beyond performance, document.write() is a security risk — if the content being written comes from user input, it is an XSS vector.
  • The standard: Never use document.write(). Use document.createElement(), element.textContent, element.append(), or framework-level DOM updates instead. There is no modern use case where document.write() is the right answer.
Modern DOM manipulation
// Create and insert elements properly
const banner = document.createElement("div");
banner.textContent = "Welcome back!";
banner.className = "welcome-banner";
document.getElementById("app").append(banner);
document.write()
// Blocks parser, can wipe the page, potential XSS
document.write("<div class='banner'>Welcome back!</div>");

// Even worse: writing a script tag
document.write('<script src="https://ads.example.com/ad.js"><\/script>');

How to fix it

React / Next.js

In React, you should never need document.write() — React manages the DOM for you. But third-party scripts (especially ad scripts and legacy analytics) sometimes use it. Wrap them in Next.js Script component with lazy loading.

import Script from "next/script";

// If a third-party script uses document.write internally,
// load it lazily so it doesn't block rendering
<Script
  src="https://legacy-analytics.example.com/tracker.js"
  strategy="lazyOnload" // loads after page is fully interactive
/>;

// For dynamic content injection, use refs or state — never document.write
("use client");
import { useRef, useEffect } from "react";

function DynamicBanner({ message }: { message: string }) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (ref.current) {
      // Safe: using textContent, not innerHTML or document.write
      ref.current.textContent = message;
    }
  }, [message]);

  return <div ref={ref} className="banner" />;
}

If you are migrating legacy code that uses document.write():

// BEFORE: legacy pattern
document.write(`<div id="widget">${widgetHTML}</div>`);

// AFTER: modern equivalent
const container = document.createElement("div");
container.id = "widget";
container.innerHTML = DOMPurify.sanitize(widgetHTML); // sanitize if from external source
document.body.append(container);

Plain HTML

Replace every document.write() call with proper DOM methods.

<!-- BAD: document.write for loading scripts -->
<script>
  document.write('<script src="https://cdn.example.com/lib.js"><\/script>');
</script>

<!-- GOOD: dynamically create script element -->
<script>
  const script = document.createElement("script");
  script.src = "https://cdn.example.com/lib.js";
  script.async = true;
  document.head.appendChild(script);
</script>

<!-- BAD: document.write for dynamic content -->
<script>
  document.write("<p>Today is " + new Date().toLocaleDateString() + "</p>");
</script>

<!-- GOOD: update existing element -->
<p id="date-display"></p>
<script>
  document.getElementById("date-display").textContent =
    "Today is " + new Date().toLocaleDateString();
</script>
Medium impactsecurity~1 paw

document.write() is a relic from the 1990s web. The cat does not tolerate outdated patterns that hurt both performance and security. Modernize your DOM updates.

How the cat scores this

The scanner searches your JavaScript for calls to document.write() and document.writeln(). Each occurrence is flagged with its context — whether it is injecting scripts, HTML content, or dynamic values. Calls that inject external scripts receive a higher severity because they compound the performance impact. The scanner also checks whether the content being written includes any dynamic or user-controllable values, which would elevate the finding to a security issue (potential XSS).

Further reading

On this page