Adding script tag to React/JSX


I have a relatively straightforward issue of trying to add inline scripting to a React component. What I have so far:

'use strict';

import '../../styles/pages/people.scss';

import React, { Component } from 'react';
import DocumentTitle from 'react-document-title';

import { prefix } from '../../core/util';

export default class extends Component {
    render() {
        return (
            <DocumentTitle title="People">
                <article className={[prefix('people'), prefix('people', 'index')].join(' ')}>
                    <h1 className="tk-brandon-grotesque">People</h1>

                    <script src="https://use.typekit.net/foobar.js"></script>
                    <script dangerouslySetInnerHTML={{__html: 'try{Typekit.load({ async: true });}catch(e){}'}}></script>
                </article>
            </DocumentTitle>
        );
    }
};

I have also tried:

<script src="https://use.typekit.net/foobar.js"></script>
<script>try{Typekit.load({ async: true });}catch(e){}</script>

Neither approach seems to execute the desired script. I'm guessing it's a simple thing I'm missing. Can anybody help out?

PS: Ignore the foobar, I have a real id actually in use that I didn't feel like sharing.

Edit: Things change fast and this is outdated - see update


Do you want to fetch and execute the script again and again, every time this component is rendered, or just once when this component is mounted into the DOM?

Perhaps try something like this:

componentDidMount () {
    const script = document.createElement("script");

    script.src = "https://use.typekit.net/foobar.js";
    script.async = true;

    document.body.appendChild(script);
}

However, this is only really helpful if the script you want to load isn't available as a module/package. First, I would always:

  • Look for the package on npm
  • Download and install the package in my project (npm install typekit)
  • import the package where I need it (import Typekit from 'typekit';)

This is likely how you installed the packages react and react-document-title from your example, and there is a Typekit package available on npm.


Update:

Now that we have hooks, a better approach might be to use useEffect like so:

useEffect(() => {
  const script = document.createElement('script');

  script.src = "https://use.typekit.net/foobar.js";
  script.async = true;

  document.body.appendChild(script);

  return () => {
    document.body.removeChild(script);
  }
}, []);

Which makes it a great candidate for a custom hook (eg: hooks/useScript.js):

import { useEffect } from 'react';

const useScript = url => {
  useEffect(() => {
    const script = document.createElement('script');

    script.src = url;
    script.async = true;

    document.body.appendChild(script);

    return () => {
      document.body.removeChild(script);
    }
  }, [url]);
};

export default useScript;

Which can be used like so:

import useScript from 'hooks/useScript';

const MyComponent = props => {
  useScript('https://use.typekit.net/foobar.js');

  // rest of your component
}

Further to the answers above you can do this:

import React from 'react';

export default class Test extends React.Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    const s = document.createElement('script');
    s.type = 'text/javascript';
    s.async = true;
    s.innerHTML = "document.write('This is output by document.write()!')";
    this.instance.appendChild(s);
  }

  render() {
    return <div ref={el => (this.instance = el)} />;
  }
}

The div is bound to this and the script is injected into it.

Demo can be found on codesandbox.io


My favorite way is to use React Helmet – it's a component that allows for easy manipulation of the document head in a way you're probably already used to.

e.g.

import React from "react";
import {Helmet} from "react-helmet";

class Application extends React.Component {
  render () {
    return (
        <div className="application">
            <Helmet>
                <script src="https://use.typekit.net/foobar.js"></script>
                <script>try{Typekit.load({ async: true });}catch(e){}</script>
            </Helmet>
            ...
        </div>
    );
  }
};

https://github.com/nfl/react-helmet


If you need to have <script> block in SSR (server-side rendering), an approach with componentDidMount will not work.

You can use react-safe library instead. The code in React will be:

import Safe from "react-safe"

// in render 
<Safe.script src="https://use.typekit.net/foobar.js"></Safe.script>
<Safe.script>{
  `try{Typekit.load({ async: true });}catch(e){}`
}
</Safe.script>

The answer Alex Mcmillan provided helped me the most but didn't quite work for a more complex script tag.

I slightly tweaked his answer to come up with a solution for a long tag with various functions that was additionally already setting "src".

(For my use case the script needed to live in head which is reflected here as well):

  componentWillMount () {
      const script = document.createElement("script");

      const scriptText = document.createTextNode("complex script with functions i.e. everything that would go inside the script tags");

      script.appendChild(scriptText);
      document.head.appendChild(script);
  }

I created a React component for this specific case: https://github.com/coreyleelarson/react-typekit

Just need to pass in your Typekit Kit ID as a prop and you're good to go.

import React from 'react';
import Typekit from 'react-typekit';

const HtmlLayout = () => (
  <html>
    <body>
      <h1>My Example React Component</h1>
      <Typekit kitId="abc123" />
    </body>
  </html>
);

export default HtmlLayout;

There is a very nice workaround using Range.createContextualFragment.

/**
 * Like React's dangerouslySetInnerHTML, but also with JS evaluation.
 * Usage:
 *   <div ref={setDangerousHtml.bind(null, html)}/>
 */
function setDangerousHtml(html, el) {
    if(el === null) return;
    const range = document.createRange();
    range.selectNodeContents(el);
    range.deleteContents();
    el.appendChild(range.createContextualFragment(html));
}

This works for arbitrary HTML and also retains context information such as document.currentScript.


You can use npm postscribe to load script in react component

postscribe('#mydiv', '<script src="https://use.typekit.net/foobar.js"></script>')

for multiple scripts, use this

var loadScript = function(src) {
  var tag = document.createElement('script');
  tag.async = false;
  tag.src = src;
  document.getElementsByTagName('body').appendChild(tag);
}
loadScript('//cdnjs.com/some/library.js')
loadScript('//cdnjs.com/some/other/library.js')

Tried all of the proposed solutions but none work. I am trying to embed this to a component but it's not loading. this is a banner from bannersnack.com.

<script type="text/javascript">
var bannersnack_embed = {"hash":"bxpien9pr","width":1190,"height":300,"t":1559889975,"userId":39254375,"responsive":true,"type":"html5"};
</script>
<script type="text/javascript" src="//cdn.bannersnack.com/iframe/embed.js"></script>

componentDidMount() {
  const head = document.querySelector("head");
  const script = document.createElement("script");
  script.setAttribute(
    "src",
    "https://assets.calendly.com/assets/external/widget.js"
  );
  head.appendChild(script);
}

You can find best answer at the following link:

https://cleverbeagle.com/blog/articles/tutorial-how-to-load-third-party-scripts-dynamically-in-javascript

const loadDynamicScript = (callback) => {
const existingScript = document.getElementById('scriptId');

if (!existingScript) {
    const script = document.createElement('script');
    script.src = 'url'; // URL for the third-party library being loaded.
    script.id = 'libraryName'; // e.g., googleMaps or stripe
    document.body.appendChild(script);

    script.onload = () => {
      if (callback) callback();
    };
  }

  if (existingScript && callback) callback();
};

According to Alex McMillan's solution, I have the following adaptation.
My own environment: React 16.8+, next v9+

// add a custom component named Script
// hooks/Script.js

import { useEffect } from 'react'

const useScript = (url, async) => {
  useEffect(() => {
    const script = document.createElement('script')

    script.src = url
    script.async = (typeof async === 'undefined' ? true : async )

    document.body.appendChild(script)

    return () => {
      document.body.removeChild(script)
    }
  }, [url])
}

export default function Script({ src, async=true}) {

  useScript(src, async)

  return null  // Return null is necessary for the moment.
}

// Use the custom compoennt, just import it and substitute the old lower case <script> tag with the custom camel case <Script> tag would suffice.
// index.js

import Script from "../hooks/Script";

<Fragment>
  {/* Google Map */}
  <div ref={el => this.el = el} className="gmap"></div>

  {/* Old html script */}
  {/*<script type="text/javascript" src="http://maps.google.com/maps/api/js"></script>*/}

  {/* new custom Script component */}
  <Script src='http://maps.google.com/maps/api/js' async={false} />
</Fragment>

A bit late to the party but I decided to create my own one after looking at @Alex Macmillan answers and that was by passing two extra parameters; the position in which to place the scripts such as or and setting up the async to true/false, here it is:

import { useEffect } from 'react';

const useScript = (url, position, async) => {
  useEffect(() => {
    const placement = document.querySelector(position);
    const script = document.createElement('script');

    script.src = url;
    script.async = typeof async === 'undefined' ? true : async;

    placement.appendChild(script);

    return () => {
      placement.removeChild(script);
    };
  }, [url]);
};

export default useScript;

The way to call it is exactly the same as shown in the accepted answer of this post but with two extra(again) parameters:

// First string is your URL
// Second string can be head or body
// Third parameter is true or false.
useScript("string", "string", bool);

Solution depends on scenario. Like in my case, I had to load a calendly embed inside a react component.

Calendly looks for a div and reads from it's data-url attribute and loads an iframe inside the said div.

It is all good when you first load the page: first, div with data-url is rendered. Then calendly script is added to body. Browser downloads and evaluates it and we all go home happy.

Problem comes when you navigate away and then come back into the page. This time the script is still in body and browser doesn't re-download & re-evaluate it.

Fix:

  1. On componentWillUnmount find and remove the script element. Then on re mount, repeat the above steps.
  2. Enter $.getScript. It is a nifty jquery helper that takes a script URI and a success callback. Once the script it loaded, it evaluates it and fires your success callback. All I have to do is in my componentDidMount $.getScript(url). My render method already has the calendly div. And it works smooth.

You can also use react helmet

import React from "react";
import {Helmet} from "react-helmet";

class Application extends React.Component {
  render () {
    return (
        <div className="application">
            <Helmet>
                <meta charSet="utf-8" />
                <title>My Title</title>
                <link rel="canonical" href="http://example.com/example" />
                <script src="/path/to/resource.js" type="text/javascript" />
            </Helmet>
            ...
        </div>
    );
  }
};

Helmet takes plain HTML tags and outputs plain HTML tags. It's dead simple, and React beginner friendly.


To import JS files in react project use this commange

  1. Move the JS file to the project.
  2. import the js into the page using the following command

It's simple and easy.

import  '../assets/js/jquery.min';
import  '../assets/js/popper.min';
import  '../assets/js/bootstrap.min';

In my case, I would like to import these JS files in my react project.