Conditionally add and remove a script tag in React

Last updated : August 19, 2022

Loading unnecessary resources can negatively impact the application's performance. Particularly in web applications, we should load only the resources it uses.

This article explains how to load an external script based on a condition. The resource can be any Javascript file, CSS, or JSON.

Loading and unloading script on a button click

In this example, I load and unload the CSS file provided by the w3schools website.

import { useState } from "react"
export const App = () => {
  const [stylesLoaded, setStylesLoaded] = useState<boolean>(false)
  var link = document.createElement("link");
  link.type = "text/css"
  link.rel = "stylesheet"
  link.href = "https://www.w3schools.com/w3css/4/w3.css"
  link.id = "w3schools"
  const handleStyles = () => {
    if(stylesLoaded){
      var externalResource = document.getElementById("w3schools");
      externalResource?.parentNode?.removeChild(externalResource);
    }
    else{
      document.head.appendChild(link)
    }
    setStylesLoaded(prev => !prev)
  }
  return(
    <>
      <div className="w3-container">
        <div className="w3-panel w3-pale-green">
          <p>This message is supposed to have a green background</p>
        </div>
        <button onClick={handleStyles}>{!stylesLoaded ? "Load Styles" : "Unload Styles" }</button>
      </div>
    </>
  )
}
export default App

Removing script when the component unmounts

The above example is only for illustration purposes. When I navigate away from the component, I must remove the script I loaded conditionally in real-world applications. Otherwise, the script will remain in the DOM until the browser session ends or refreshes.

import { useEffect, useState } from "react"
export const App = () => {
  const [stylesLoaded, setStylesLoaded] = useState<boolean>(false)
  const handleStyles = () => {
    setStylesLoaded(prev => !prev)
  }
  useEffect (() => {
    var link = document.createElement("link");
    link.type = "text/css"
    link.rel = "stylesheet"
    link.href = "https://www.w3schools.com/w3css/4/w3.css"
    link.id = "w3schools"
    if(stylesLoaded){
      document.head.appendChild(link)
    }
    else{
      var externalResource = document.getElementById("w3schools");
      externalResource?.parentNode?.removeChild(externalResource);
    }
    return () => { stylesLoaded && document.head.removeChild(link); }
  },[stylesLoaded])
  return(
    <>
      <div className="w3-container">
        <div className="w3-panel w3-pale-green">
          <p>This message is supposed to have a green background</p>
        </div>
        <button onClick={handleStyles}>{!stylesLoaded ? "Load Styles" : "Unload Styles" }</button>
      </div>
    </>
  )
}
export default App

Conditional script loading hook

That brings us to the practical re-usability of the functionality I have developed so far. Packaging this code in a React hook is a scalable way to reuse the functionality.

interface ExternalResource {
  type: "css" | "javascript"
  href: string
  id: string
}
export const useExternalResources = (props: ExternalResource): Function[] => {
  var link: HTMLLinkElement | HTMLScriptElement
      const loadResource = (): void => {
        document.head.appendChild(link)
  }
      const unLoadResource = (): void => {
        var externalResource = document.getElementById(props.id);
        externalResource?.parentNode?.removeChild(externalResource);
      }
      if(props.type === "css"){
        link = document.createElement("link");
        link.type = "text/css"
        link.rel = "stylesheet"
        link.href = props.href
      }
      else{
        link = document.createElement("script");
        link.type = "text/javascript"
        link.src = props.href
      }
      link.id = props.id
      return [loadResource, unLoadResource]
}
import { useState } from "react"
import { useExternalResources } from "./components/useCookies"
export const App = () => {
  const [stylesLoaded, setStylesLoaded] = useState<boolean>(false)
  const [loadStyles, unloadStyles] = useExternalResources({type:"css", href:"https://www.w3schools.com/w3css/4/w3.css", id:"w3school"})
  const handleStyles = () => {
    !stylesLoaded ? loadStyles() : unloadStyles()
    setStylesLoaded(prev => !prev)
  }
  return(
    <>
      <div className="w3-container">
        <div className="w3-panel w3-pale-green">
          <p>This message is supposed to have a green background</p>
        </div>
        <button onClick={handleStyles}>{!stylesLoaded ? "Load Styles" : "Unload Styles" }</button>
      </div>
    </>
  )
}
export default App
L Raney
By: L Raney
Lance is a software engineer with over 15 years of experience in full-stack software development.
Read more...

Comments are disabled

No Comments