Document: write() method

Deprecated: This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.

Warning: Use of the document.write() method is strongly discouraged. Avoid using it, and where possible replace it in existing code.

As the HTML spec itself warns:

This method has very idiosyncratic behavior. In some cases, this method can affect the state of the HTML parser while the parser is running, resulting in a DOM that does not correspond to the source of the document (e.g., if the string written is the string "<plaintext>" or "<!--"). In other cases, the call can clear the current page first, as if document.open() had been called. In yet more cases, the method is simply ignored, or throws an exception. Users agents are explicitly allowed to avoid executing script elements inserted via this method. And to make matters even worse, the exact behavior of this method can in some cases be dependent on network latency, which can lead to failures that are very hard to debug. For all these reasons, use of this method is strongly discouraged.

Warning: This method parses its input as HTML, writing the result into the DOM. APIs like this are known as injection sinks, and are potentially a vector for cross-site scripting (XSS) attacks, if the input originally came from an attacker.

You can mitigate this risk by always passing TrustedHTML objects instead of strings and enforcing trusted types. See Security considerations for more information.

The write() method of the Document interface writes text in one or more TrustedHTML or string parameters to a document stream opened by document.open().

Syntax

js
write(markup)
write(markup, markup2)
write(markup, markup2, /* …, */ markupN)

Parameters

markup, …, markupN

TrustedHTML objects or strings containing the markup to be written to the document.

Return value

None (undefined).

Exceptions

InvalidStateError DOMException

The method was called on an XML document, or called when the parser is currently executing a custom element constructor.

TypeError

A string is passed as one of the parameters when Trusted Types are enforced and no default policy has been defined for creating TrustedHTML objects.

Description

document.write() parses the markup text in the objects passed as parameters into the open document's object model (DOM), in the order that the parameters are specified.

Because document.write() writes to the document stream, calling document.write() on a closed (loaded) document (without first calling document.open()) automatically calls document.open(), which will clear the document.

The exception is that if the document.write() call is embedded within an inline HTML <script> tag, then it will not automatically call document.open():

html
<script>
  document.write("<h1>Main title</h1>");
</script>

document.write() (and document.writeln) cannot be used with XML or XHTML, and attempting to do so will throw an InvalidStateError exception. This is the case if opening a local file with a .xhtml file extension or for any document served with an application/xhtml+xml MIME type. More information is available in the W3C XHTML FAQ.

Using document.write() in deferred or asynchronous scripts will be ignored and you'll get a message like "A call to document.write() from an asynchronously-loaded external script was ignored" in the error console.

In Edge only, calling document.write() more than once in an <iframe> causes the error "SCRIPT70: Permission denied".

Security considerations

The method is a possible vector for cross-site scripting (XSS) attacks, where potentially unsafe strings provided by a user are injected into the DOM without first being sanitized. While the method may block <script> elements from executing when they are injected in some browsers (see Intervening against document.write() for Chrome), it is susceptible to many other ways that attackers can craft HTML to run malicious JavaScript.

You can mitigate these issues by always passing TrustedHTML objects instead of strings, and enforcing trusted types using the require-trusted-types-for CSP directive. This ensures that the input is passed through a transformation function, which has the chance to sanitize the input to remove potentially dangerous markup (such as <script> elements and event handler attributes), before it is injected.

Examples

Writing TrustedHTML

This example uses the Trusted Types API to sanitize HTML strings of <script> elements before they are written to a document.

The example initially displays some default text and a button. When the button is clicked, the current document is opened, three strings of HTML are converted to TrustedHTML instances and written into the document, and the document is then closed. This replaces the document in the example frame, including the original HTML for the button and the JavaScript that made the update!

HTML

html
<p>Some original document content.</p>
<button id="replace" type="button">Replace document content</button>

JavaScript

First we use the Window.trustedTypes property to access the global TrustedTypePolicyFactory, and use its createPolicy() method to define a policy called "docPolicy".

The new policy defines a transformation function createHTML() for creating the TrustedHTML objects that we will pass to the write() method. This method can do anything it likes with the input string: the trusted types API just requires that you pass the input through a policy transformation function, not that the transformation function does anything in particular.

You'd use the method to sanitize the input by removing potentially unsafe features such as <script> tags or event handler attributes. Sanitization is hard to get right, so this process typically uses a reputable third-party library such as DOMPurify.

For the purposes of demonstration, here we implement a rudimentary "sanitizer" that replaces < symbols in script opening and closing tags with the &lt; character.

js
const policy = trustedTypes.createPolicy("docPolicy", {
  createHTML(string) {
    return string
      .replace("<script", "&lt;script")
      .replace("</script", "&lt;/script");
  },
});

We can then use the TrustedTypePolicy.createHTML() method on the returned policy to create TrustedHTML objects from our original input strings. These are then passed to the write() function when the user clicks the button.

js
const oneInput = "<h1>Out with the old</h1>";
const twoInput = "<p>in with the new!</p>";
const threeInput = "<script>alert('evil afoot')<" + "/script>";
const replace = document.querySelector("#replace");

replace.addEventListener("click", () => {
  document.open();
  document.write(
    policy.createHTML(oneInput),
    policy.createHTML(twoInput),
    policy.createHTML(threeInput),
  );
  document.close();
});

Results

Press the button and note that the HTML elements that we trust (in this example) are injected, but the untrusted <script> element is now rendered as plain text.

Writing strings

This is the same as the preceding example, except that trusted types are not used or enforced. We're writing unsanitized strings, which may provide a path for XSS attacks.

The example initially displays some default text and a button. When the button is clicked, the current document is opened, three strings of HTML are written into the document, and the document is then closed. This replaces the document in the example frame, including the original HTML for the button and the JavaScript that made the update.

HTML

html
<p>Some original document content.</p>
<button id="replace" type="button">Replace document content</button>

JavaScript

js
const replace = document.querySelector("#replace");

const oneInput = "<h1>Out with the old</h1>";
const twoInput = "<p>in with the new!</p>";
const threeInput = "<script>alert('evil afoot')<" + "/script>";

replace.addEventListener("click", () => {
  document.open();
  document.write(oneInput, twoInput, threeInput);
  document.close();
});

Results

Press the button and note that all the HTML elements are injected. This includes the <script> element, which in a real application might have executed harmful code.

Specifications

Specification
HTML
# dom-document-write-dev

Browser compatibility

desktop mobile
Chrome
Edge
Firefox
Opera
Safari
Chrome Android
Firefox for Android
Opera Android
Safari on iOS
Samsung Internet
WebView Android
WebView on iOS
write
Deprecated
Chrome – Partial support
Chrome 1 – 44 (Release date: 2008-12-11)
footnote Partial support
footnote Only supported for HTMLDocument, not all Document objects.
Chrome – Full support
Chrome 45 (Release date: 2015-09-01)
footnote Full support
Edge – Full support
Edge 12 (Release date: 2015-07-29)
footnote Full support
Firefox – Partial support
Firefox 1 – 68 (Release date: 2004-11-09)
footnote Partial support
footnote Only supported for HTMLDocument, not all Document objects.
Firefox – Full support
Firefox 69 (Release date: 2019-09-03)
footnote Full support
Opera – Full support
Opera 3 (Release date: 1997-12-01)
footnote Full support
Safari – Partial support
Safari 1 – 10.1 (Release date: 2003-06-23)
footnote Partial support
footnote Only supported for HTMLDocument, not all Document objects.
Safari – Full support
Safari 11 (Release date: 2017-09-19)
footnote Full support
Chrome Android – Partial support
Chrome Android 18 – 44 (Release date: 2012-06-27)
footnote Partial support
footnote Only supported for HTMLDocument, not all Document objects.
Chrome Android – Full support
Chrome Android 45 (Release date: 2015-09-01)
footnote Full support
Firefox for Android – Partial support
Firefox for Android 4 – 68 (Release date: 2011-03-29)
footnote Partial support
footnote Only supported for HTMLDocument, not all Document objects.
Firefox for Android – Full support
Firefox for Android 79 (Release date: 2020-07-28)
footnote Full support
Opera Android – Full support
Opera Android 10.1 (Release date: 2010-11-09)
footnote Full support
Safari on iOS – Partial support
Safari on iOS 1 – 10.3 (Release date: 2007-06-29)
footnote Partial support
footnote Only supported for HTMLDocument, not all Document objects.
Safari on iOS – Full support
Safari on iOS 11 (Release date: 2017-09-19)
footnote Full support
Samsung Internet – Partial support
Samsung Internet 1 – 4.2 (Release date: 2013-04-27)
footnote Partial support
footnote Only supported for HTMLDocument, not all Document objects.
Samsung Internet – Full support
Samsung Internet 5 (Release date: 2016-12-15)
footnote Full support
WebView Android – Partial support
WebView Android 4.4 – 44 (Release date: 2013-12-09)
footnote Partial support
footnote Only supported for HTMLDocument, not all Document objects.
WebView Android – Full support
WebView Android 45 (Release date: 2015-09-01)
footnote Full support
WebView on iOS – Partial support
WebView on iOS 1 – 10.3 (Release date: 2007-06-29)
footnote Partial support
footnote Only supported for HTMLDocument, not all Document objects.
WebView on iOS – Full support
WebView on iOS 11 (Release date: 2017-09-19)
footnote Full support
Requires TrustedHTML instance when trusted types are enforced
Deprecated
Chrome – Full support
Chrome 86 (Release date: 2020-10-20)
footnote Full support
Edge – Full support
Edge 86 (Release date: 2020-10-09)
footnote Full support
Firefox – Full support
Firefox 148 (Release date: 2026-02-24)
footnote Full support
Opera – Full support
Opera 72 (Release date: 2020-10-21)
footnote Full support
Safari – Full support
Safari 26 (Release date: 2025-09-15)
footnote Full support
Chrome Android – Full support
Chrome Android 86 (Release date: 2020-10-20)
footnote Full support
Firefox for Android – Full support
Firefox for Android 148 (Release date: 2026-02-24)
footnote Full support
Opera Android – Full support
Opera Android 61 (Release date: 2020-12-07)
footnote Full support
Safari on iOS – Full support
Safari on iOS 26 (Release date: 2025-09-15)
footnote Full support
Samsung Internet – Full support
Samsung Internet 14 (Release date: 2021-04-17)
footnote Full support
WebView Android – Full support
WebView Android 86 (Release date: 2020-10-20)
footnote Full support
WebView on iOS – Full support
WebView on iOS 26 (Release date: 2025-09-15)
footnote Full support

Legend

Tip: you can click/tap on a cell for more information.

Full support
Full support
Partial support
Partial support
Deprecated. Not for use in new websites.
Has more compatibility info.

See also