How To Deal With document.write() in React and Next.js
Tags: javascript, nextjs, react, softwaredevelopment
Published: 2024-01-19 21:20:05

When developing a [React](https://reactjs.org/) or [Next.js](https://nextjs.org/) app, you might need to import external JavaScript files through the HTML [`<script></script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)`tag. This is especially true when dealing with ads.`

The problem here is that those external scripts frequently use the [`document.write()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/write) statement. This does not only come with restrictions but its usage is discouraged and reported with a warning in the browser console. In some cases, it may even be blocked by the browser for speed reasons, making the imported external script broken.

Since you typically cannot change the code of these external scripts, this can easily become a headache. Luckily, there is a solution!

Let's see how the PostScribe library can help solve the issue in React or Next.js, in both JavaScript and TypeScript.

## Why document.write() Should Be Avoided

In [this 2016 article](https://developers.google.com/web/updates/2016/08/removing-document-write), Google delved into why `document.write()` represents an issue for users on slow connections. In detail, this statement can slow down the loading time of a web page by dozens of seconds. This happens because before the browser can render a web page, it needs to generate the entire [DOM](https://en.wikipedia.org/wiki/Document_Object_Model) tree. And if the HTML parser encounters a script, it has to stop and launch it before continuing.

The problem is that if the script dynamically injects another script with `document.write()`, the parser is forced to wait for the new external resource to be downloaded. This can lead to one or more network roundtrips and delay the rendering time of the web page.

This is such a problem that modern browsers can choose to prevent `document.write()` from being executed based on the user's connection speed. In other words, you may not rely on it entirely.

When used and not blocked, the Chrome DevTools Console will warn you with the following message:

```
[Violation] Avoid using document.write().
```

Likewise, this is what the Firefox DevTools Console prints when using it:

```
An unbalanced tree was written using document.write() causingdata from the network to be reparsed.
```

[Google Lighthouse](https://developers.google.com/web/tools/lighthouse/) does not like it as well and will report the non-blocked `document.write()` statements with the following message:

![](https://writech.run/wp-content/uploads/2023/08/1CyE-4oiDTz2mFDpye9t7YA.png)

The solution to `document.write()` is virtually easy to find. You should simply replace any `document.write()` statements with explicit DOM manipulations statements, such as [`.appendChild()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild). On the other hand, you may not have access to these external scripts and cannot change their code. In this case, [PostScribe](https://github.com/krux/postscribe) is the solution.

## Addressing the `document.write()` Issues

> Remote scripts, especially ads, block the page from doing anything else while they load. They contribute a large % to load times which affects your bottom line. _Asynchronous_ ads do not block the page and can be delivered after core content.

> Why is it so hard to deliver ads asynchronously? Because they may contain calls to `document.write`, which expects to be handled synchronously. PostScribe lets you deliver a synchronous ad asynchronously without modifying the ad code. — [PostScribe's official GitHub page](https://github.com/krux/postscribe)

Let's assume that `https://external-domain.com/ads.js` is your external script file using `document.write()`. This will take care of loading an ad in the `<div id="#adv"></div>` HTML element.

Adding `<div id="#adv"></div>`to your DOM and importing the external script with the following line would lead to the aforementioned problems:

```javascript
<script src={"https://external-domain.com/ads.js"} />
```

Let's now see how to avoid them.

First of all, you need to install PostScribe with the following command:

```shell
npm install --save postscribe
```

This way, the [`postscribe`](https://www.npmjs.com/package/postscribe) npm library will be added to your project's dependencies. Learn more about `postscribe` [here](https://github.com/krux/postscribe).

Now, you can define an `ExternalAd` React component loading the external script asynchronously with `postscribe` as follows:

```javascript
import React, { useEffect, useRef } from "react"
import postscribe from "postscribe"

export default function ExternalAd() {
  const advRef = useRef(null)

  useEffect(() => {
    if (advRef) {
      postscribe(
        advRef.current,
        "https://external-domain.com/ads.js",
      )
    }
  }, [advRef])

  return <div id="adv" ref={advRef}></div>
}
```

This component will load the `https://external-domain.com/ads.js` external script inside the `<div id="#adv"></div>` DOM element only when the `<div id="adv"></div>` element is loaded. This is because it uses its React reference as a flag inside the [`useEffect()`](https://reactjs.org/docs/hooks-effect.html) hook. Learn more about React references [here](https://reactjs.org/docs/refs-and-the-dom.html).

Now, you can use this component whenever you want, with the following [JSX](https://reactjs.org/docs/introducing-jsx.html) statement:

```javascript
<ExternalAd />
```

Since the external resource is now loaded asynchronously thanks to `postscribe`, the browser should not block it anymore, although it may still complain about `document.write()`.

## Making PostScribe Work in TypeScript

The following import statement will not work on TypeScript:

```javascript
import postscribe from "postscribe"
```

This is because PostScribe seems not to support TypeScript at the time of writing. Fortunately, tackling this issue is very simple. All you need to do is create a `postscribe.d.ts` file as follows:

```javascript
declare module "postscribe"
```

This will help TypeScript load `postscribe` with no problem. Learn more about this workaround [here](https://stackoverflow.com/a/50516783).

## Conclusion

In this article, we learned why `document.write()` represents a problem for users on slow connections, why it should be use used as a result, and how to prevent modern browsers from blocking it when loading external resources. All of this is possible thanks to PostScribe, which allows you to load synchronous resources asynchronously and solve the issues coming with `document.write()` without having to modify the code of the external resources, which is typically not an option.

Thanks for reading! I hope that you found this article helpful.
  
***
_The post "[How To Deal With document.write() in React and Next.js](https://writech.run/blog/how-to-deal-with-document-write-in-react-and-next-js-31f0c1215e43/)" appeared first on [Writech](https://writech.run)._