DEV Community

Cover image for How To Deal With document.write() in React and Next.js
Antonello Zanini for Writech

Posted on • Edited on

How To Deal With document.write() in React and Next.js

When developing a React or Next.js app, you might need to import external JavaScript files through the HTML <script></script>tag. This is especially true when dealing with ads.

The problem here is that those external scripts frequently use the 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, 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 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().
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

Google Lighthouse does not like it as well and will report the non-blocked document.write() statements with the following message:

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(). On the other hand, you may not have access to these external scripts and cannot change their code. In this case, 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

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:

<script src={"https://external-domain.com/ads.js"} />
Enter fullscreen mode Exit fullscreen mode

Let's now see how to avoid them.

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

npm install --save postscribe
Enter fullscreen mode Exit fullscreen mode

This way, the postscribe npm library will be added to your project's dependencies. Learn more about postscribe here.

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

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>
}
Enter fullscreen mode Exit fullscreen mode

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() hook. Learn more about React references here.

Now, you can use this component whenever you want, with the following JSX statement:

<ExternalAd />
Enter fullscreen mode Exit fullscreen mode

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:

import postscribe from "postscribe"
Enter fullscreen mode Exit fullscreen mode

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:

declare module "postscribe"
Enter fullscreen mode Exit fullscreen mode

This will help TypeScript load postscribe with no problem. Learn more about this workaround here.

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" appeared first on Writech.

Top comments (0)