Including local JS and CSS files using Gatsbyjs

I am completely new to the gatsbyjs ecosystem, and at the same time I am learning a bit of reactjs. I recently bought an html template and was trying to use it as a UI in a gatsbyjs application. The template has many css and js, whether proprietary or costumized, which implies that there are no plugins in gatsbyjs to replace them. That said, the only option I have left is to add them directly as a link and scripts tags in the gatsbyjs application. Following some of the tutorials I saw that they suggest importing these files, the issue is that these files are not webpack friendly, resulting in multiple errors and the application does not compile.
Following this help page Gatsby Server Rendering APIs get to this code in the gatsby-ssr.js file:

const React = require("react")
exports.onRenderBody = ({ setBodyAttributes, setPostBodyComponents }) => {
  setBodyAttributes({
    "data-spy":"scroll",
    "data-offset":"73",
    "data-target":"#navbar-demos"
  })
  setPostBodyComponents([
    <script
      key="1"
      type="text/javascript"
      src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.slim.js"
    />,
    <script
      key="2"
      type="text/javascript"
      src="./src/revolution/js/extensions/revolution.extension.actions.min.js"
    />,
  ])
}

the problem is this file revolution.extension.actions.min.js is never exposed to the browser. when you open the Developer Tools appears to be there, but it definitely isn’t:

enter image description here

enter image description here

What do I have to do to render those .js files correctly and the browser can see them? That also applies for .css files

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

After a more extensive search, I found a couple of articles with a solution. This was the first approach:

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

const IndexPage = () => (
  <div>
    <Helmet>
        <script src={withPrefix('revolution/js/extensions/revolution.extension.actions.min.js')} type="text/javascript" />
    </Helmet>

  </div>
)
export default IndexPage

This is the second approach using the gatsby-ssr.js file:

const React = require("react")

exports.onRenderBody = ({setPostBodyComponents}) => {
          setPostBodyComponents([
            <script key="1" src={'js/plugins/plugins.js'} type="text/javascript" />,
            <script type="text/javascript" src={"js/beauty.custom.js"}/>
          ]);
  };

this way the scripts tags will be added at the end of the body instead of the head

I think the second one is the best one, but have to take in count that every time you change something in the gatsby-ssr.js file have to stop the gatsby develop and re-run it.

In the first case usingreact-helmet will hot-reload.

NOTE: In both approaches, files have to be in the static folder

Here are the links to where I found this approach:

How to include local javascript on a Gatsby page?

How do you insert an external script using gatsby-browser?

Solution 2

@ddieppa solution might not work if you demand your script must only be executed after the component/page is completely loaded, because:

  • React-Helmet always put your tag on top of the page inside the <head> tag, no matter where you put it.
  • Gatsby Server Rendering API – setPostBodyComponents via (gatsby-ssr.jsx/tsx) will append your <script> tag at the end of the body nicely, but watch out because your script file might actually executed before React completely finishes rendering your component. If your js file is referring to any specific object in the component, you might end up seeing a bunch of null pointer exception, undefined exception.

So if you want to make sure your script file get executed/loaded right after the component is completely rendered, you might need to use either React useEffect hook (if your React component is a functional component) or React componentDidMount (if your component is a class component)

The idea will be simple, after the component is fully rendered, we will append a <script> at the bottom of it.

With useEffect hook (for functional component)

(It’s the same for componentDidMount)

import React, { useEffect } from 'react'

const MyComponent = () => {

    useEffect(() => {
        const mainJsTag = document.createElement('script')
        // given the js/main.js file is put in the static folder
        mainJsTag.src = 'assets/js/main.js'
        document.body.appendChild(mainJsTag)
        console.log('Component Rendered!')
    });

    return (
        <main>
          <text>Pretty much the component's body here...</text>
        </main>
    )
}

export default MyComponent

(The snippet above assuming that you put your script file under the Gatsby static folder, see Gatsby – Using the Static Folder in case you don’t know about this concept in Gatsby)

You can put a bunch of console.log in both of your script file and in the useEffect/componentDidMount functions to confirm which one get executed first.

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply