How to include local javascript on a Gatsby page?
Asked Answered
C

8

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>
)
Checkoff answered 22/2, 2019 at 20:48 Comment(0)
C
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.

Checkoff answered 23/2, 2019 at 18:22 Comment(7)
It worked for gatsby develop mode but not including the file correctly on gatsby build. Any suggestion please?Francophile
same here with meCredendum
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?)Dissolvent
@Dissolvent 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-packagesLien
@Dissolvent Also see this answer: #60067079Lien
This worked fine. I wanted to include a logo from Altmetric. I tried to include the external script in html.js and using gatsby-plugin-load-script but none worked. This worked like a charm. Thanks!Firkin
This worked great in develop AND build environments. The only thing I added was a forward slash to '/script.js' and a defer property to make it load last. Thx and cheers 🍻Sudor
C
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
`}} />
Couthie answered 23/6, 2020 at 10:39 Comment(2)
Would this work well with an external js? IE. The public jqueryurl?Herisau
Feels like this is the React way of doing it.Avar
G
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

Georgiannageorgianne answered 26/11, 2020 at 3:44 Comment(0)
D
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();
}, []}
Disfrock answered 21/9, 2020 at 21:8 Comment(0)
S
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
      },
    },
Stockinet answered 13/11, 2020 at 8:12 Comment(0)
P
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/

Predicable answered 22/2, 2019 at 21:1 Comment(0)
W
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.

Walton answered 14/9, 2021 at 16:34 Comment(0)
E
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.

Elanorelapid answered 22/2, 2019 at 20:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.