23

I'm a total React newbie and I guess there is something fundamental I don't quite understand here. A default Gatsby page looks like this. Is there a way to use a local .js file somewhat like this?

<script src="../script/script.js"></script>

What I would like to achieve is to have react ignore script.js but still have the client side use it. A default Gatsby page looks like this, is it possible to do somerthing like that there?

import React from "react"
import { Link } from "gatsby"

import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"

const IndexPage = () => (
  <Layout>
    <SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
  </Layout>
)

8 Answers 8

75

After several hours of frustration I finally stumbled upon discussion on GitHub that solved this for me. In Gatsby, there is a thing called static folder, for which one use case is including a small script outside of the bundled code.

Anyone else in the same situation, try proceeding as follows:

  1. Create a folder static to the root of your project.

  2. Put your script script.js in the folder static.

  3. Include the script in your react dom with react-helmet.

So in the case of the code I posted in my original question, for instance:

import React from "react"
import Helmet from "react-helmet"
import { withPrefix, Link } from "gatsby"

import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"

const IndexPage = () => (
  <Layout>
    <Helmet>
        <script src={withPrefix('script.js')} type="text/javascript" />
    </Helmet>
    <SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
    <h1>Hi people</h1>
    <p>Welcome to your new Gatsby site.</p>
    <p>Now go build something great.</p>
    <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
      <Image />
    </div>
    <Link to="/page-2/">Go to page 2</Link>
  </Layout>
)

Notice the imports

import Helmet from "react-helmet"
import { withPrefix, Link } from "gatsby"

and the script element.

<Helmet>
    <script src={withPrefix('script.js')} type="text/javascript" />
</Helmet>

This would have saved hours of my time, hopefully this does it for someone else.

Sign up to request clarification or add additional context in comments.

7 Comments

It worked for gatsby develop mode but not including the file correctly on gatsby build. Any suggestion please?
same here with me
Thanks a lot for this. Does anyone know if it is possible to include scripts outside of the <head> tag? I often prefer having a script at the end of the body rather than right at the top for improved loading speed (or does this hardly affect anything?)
@Mayron One solution if you want a script to always load upon navigation is to load it during the componentDidMount, for example see this: github.com/gatsbyjs/gatsby/issues/13136. You can also load them at the foot of the body by using gatsby-ssr but read this also: gatsbyjs.org/docs/using-client-side-only-packages
@Mayron Also see this answer: stackoverflow.com/questions/60066579/…
|
10

There are many ways to add scripts in GatsbyJS...

To execute a script on a specific page

  • create a stateless ScriptComponent.js file and place it inside your /src folder.

  • in your ScriptComponent.js use require() to execute the script inside useEffect() like this:

    const ScriptComponent = ({
     src, // if internal,put a path relative to this component
     onScriptLoad = () => {}, // cb
     appendToHead = false,
     timeoutDuration = 10, 
     defer = false,
     isExternal = false,
    }) => {
    
     useEffect(() => {
      setTimeout(() => {
       if (isExternal) {
        const script = document.createElement('script');
        script.src = src;
        script.onload = onScriptLoad;
    
         defer
          ? script.defer = true
          : script.async = true;
    
         appendToHead
          ? document.head.appendChild(script)
          : document.body.appendChild(script);
        } else { // for internal scripts
         // This runs the script
         const myScript = require(src);
        }
       }, timeoutDuration);
      }, []);
    
     return null;
    };
    
  • To run it on client-side, you could check the window object inside your script.js file if you didn't run it in useEffect:

     if(typeof window !== 'undefined' && window.document) {
        // Your script here...
     }
    
  • finally, go to the page you want to execute the script in it (e.g. /pages/myPage.js ), and add the component <ScriptComponent />

If you want to execute a script globally in (every component/page) you could use the html.js file.

  • first, you'll have to extract the file (in case you didn't) by running:

cp .cache/default-html.js src/html.js

  • inside your html.js file:
<script dangerouslySetInnerHTML= {{ __html:`
  // your script here...
  // or you could also reuse the same approach as in useEffect above
`}} />

2 Comments

Would this work well with an external js? IE. The public jqueryurl?
Feels like this is the React way of doing it.
9

Just create gatsby-ssr.js file on root folder

and add the following pattern for your scripts folder

import React from 'react'

export const onRenderBody = ({ setPostBodyComponents }) => {
  setPostBodyComponents([
    <script
      key="https://code.jquery.com/jquery-3.2.1.slim.min.js"
      src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
      integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
      crossOrigin="anonymous"
      defer
    />,
    <script
      key="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
      src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
      integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
      crossOrigin="anonymous"
      defer
    />,
    <script
      key="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
      src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
      integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
      crossOrigin="anonymous"
      defer
    />
  ])
}

Then, you at the end of dom you'll see the links to scripts enter image description here

Comments

2

If you'd like to use a Gatsby plugin, which to me is no different from using an external library like Helmet (plugins are npm packages after all), you could use gatsby-plugin-load-script.

You can provide either the url to the src attribute or a local path. If you're going to store your JS in a local file such as some-minified-js.min.js - make sure to store in the static directory at the root of your project.

Once you do this, you can access via the global object:

global.<object or func name here>

For example, I was trying to include a very small JS library via a minified file, so I stored the file in /static/my-minified-library.min.js and then:

  1. Installed the plugin: npm i --save gatsby-plugin-load-script
  2. Added this to my gatsby-config.js
plugins: [
    {
      resolve: "gatsby-plugin-load-script",
      options: {
        src: "/my-minified-library.min.js",
      },
    },
  ],
  1. Accessed in my react component like so:
useEffect(() => {
  const x = new global.MyImportedLibraryObject();
}, []}

Comments

2

You can do this very easily with the Gatsby plugin "gatsby-plugin-load-script."

Simply do this:

  1. Create a folder named static at the root of your gatsby app
  2. Place your script in it
  3. Add the following configuration in gatsby-config.js
    {
      resolve: 'gatsby-plugin-load-script',
      options: {
        src: '/test-script.js', // Change to the script filename
      },
    },

Comments

1

Gatsby uses html.js in the src folder. Not index.html like most react projects.

Example html.js file:

import React from "react"
import PropTypes from "prop-types"

export default class HTML extends React.Component {
  render() {
    return (
      <html {...this.props.htmlAttributes}>
        <head>
          <meta charSet="utf-8" />
          <meta httpEquiv="x-ua-compatible" content="ie=edge" />
          <meta
            name="viewport"
            content="width=device-width, initial-scale=1, shrink-to-fit=no"
          />
          {this.props.headComponents}
        </head>
        <body {...this.props.bodyAttributes}>
          {this.props.preBodyComponents}
          <div
            key={`body`}
            id="___gatsby"
            dangerouslySetInnerHTML={{ __html: this.props.body }}
          />
          {this.props.postBodyComponents}
        </body>
      </html>
    )
  }
}

HTML.propTypes = {
  htmlAttributes: PropTypes.object,
  headComponents: PropTypes.array,
  bodyAttributes: PropTypes.object,
  preBodyComponents: PropTypes.array,
  body: PropTypes.string,
  postBodyComponents: PropTypes.array,
}

For adding custom Javascript using dangerouslySetInnerHTML inside src/html.js:

<script
  dangerouslySetInnerHTML={{
    __html: `
            var name = 'world';
            console.log('Hello ' + name);
        `,
  }}
/>

You can try adding your js there but, note that your js may not work as expected. You can always look into react-helmet for more dynamic apps and adding scripts to <head>.

Gatsby Documentation: https://www.gatsbyjs.org/docs/custom-html/

Comments

1

I'm not sure if anyone still needs this answer, but here it goes:

The answer by Elliot Marques is excellent. If you need it for a local file, upload the script to Github and use a service like JSDelivr. It saves a lot of time and stress.

Comments

0

React works with dynamic DOM. But for rendering it by browser, your web server should send a static index page, where React will be included as another script tag.

So, take a look on your index.html page, which you can find in public folder. There you could insert your script tag in the header section, for example.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.