1

I'm using Gatsby 2.13.50 (CLI: 2.7.14). I'm trying to add Bootstrap 4 to my base template at /src/components/layout.js. It's used by /src/pages/index.js. Basically the directory structure as taught by most beginner tutorials.

I keep getting this error:

Uncaught TypeError: Cannot read property 'fn' of undefined
    at bootstrap.js:6
    at bootstrap.js:6
    at bootstrap.js:6

Several solutions online say that it's because bootstrap.js is loading before jquery.js and popper.js. They say placing jquery.js and popper.js first and bootstrap.js last would solve it. My results vary depending on how I bring them into the file.

First, the code in layout.js:

import React from "react"
import Header from '../components/header'
import '../styles/bootstrap-4.3.1-dist/css/bootstrap.css'
import { withPrefix } from "gatsby"

// If I import them like this, Gatsby cannot find the files.
// import '../styles/site_js/jquery.js'
// import '../styles/site_js/popper.js'
// import '../styles/site_js/bootstrap.js'

import { Helmet } from "react-helmet"

const Layout = (props) => {
    return (
        <div>

            <Header />
            <main role="main" className="container">
                {props.children}
            </main>

            // If I do this, the error happens all the time.
            {/* <Helmet>
                    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
                    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
                    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
                </Helmet> */}

            // If I do this, the error happens once every handful of times.
            <Helmet>
                <script src={withPrefix('site_js/jquery.js')} type="text/javascript" />
                <script src={withPrefix('site_js/popper.js')} type="text/javascript" />
                <script src={withPrefix('site_js/bootstrap.js')} type="text/javascript" />
            </Helmet>
        </div>
    )
}

export default Layout

I found three ways to bring the JavaScript files into layout.js.

The first one imports the files like this:

import '../styles/site_js/jquery.js'
import '../styles/site_js/popper.js'
import '../styles/site_js/bootstrap.js'

With the above, Gatsby fails to compile. It gives this error for all three files:

Module not found: Error: Can't resolve 'popper.js' in '/home/testjs/hayatdemo/src/styles/site_js'

I double-checked and the files exist at that file path. So, I don't know what's wrong.

The second loads the three files from external sources:

<Helmet>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</Helmet>

With this, Gatsby compiles, but the error appears every time I reload the page.

Finally, this solution says to add the files to /static in the root directory and call them with the help of withPrefix(). Like so:

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

This works for the most part but the error returns once every handful of reloads.

How do I solve this problem?

Olian04
  • 6,480
  • 2
  • 27
  • 54
nusantara
  • 1,109
  • 1
  • 15
  • 38
  • Are you sure the version of bootstrap you're using is compatible with the version of jQuery you're using? Because the error is most likely referring to the ["fn" property of jquery](https://api.jquery.com/jQuery.fn.extend/). So $ (or jQuery) didn't load correctly. – Olian04 Aug 07 '19 at 23:06
  • @Olian04 They should be. I downloaded them from Bootstrap's [BootstrapCDN](https://getbootstrap.com/docs/4.3/getting-started/download/#bootstrapcdn) section in its docs. Should I get them elsewhere? – nusantara Aug 07 '19 at 23:14
  • have you looked at the [gatsby bootstrap starter](https://www.gatsbyjs.org/starters/jaxx2104/gatsby-starter-bootstrap/)? maybe looking through their implementation will give you a hint. – Olian04 Aug 07 '19 at 23:18
  • @Olian04 I took a look following your suggestion. I can't figure out where the starter places the JS files. There's an `import emergence from 'emergence.js'` in /src/components/layout/index.js though. So, I guess `npm install bootstrap` might have worked better for me. I'll try this next time. For now I'm going with another solution I'll post in a bit. – nusantara Aug 08 '19 at 12:23

3 Answers3

1

I know JS files load in the order they are placed. But, just to make extra sure that they really, really, really do that, I decided to override Gatsby's default-html.js so that I could add jquery.js and popper.js to the <head> tag.

I don't know what default-html.js does. I just have a feeling it loads first and acts as some sort of basis for the whole project.

So, following Gatsby's docs on default-html.js, I first ran cp .cache/default-html.js src/html.js.

Now that it's named html.js in /src, I opened it up and added the two JS files to the <head> section:

import { withPrefix } from "gatsby"

<head>
    ...
    <script async src={withPrefix('site_js/jquery.js')} type="text/javascript" />
    <script async src={withPrefix('site_js/popper.js')} type="text/javascript" />
  </head>

To make things load just a bit fast, I added async to their <script> tags following this wee article.

Now, it works perfectly.

However, I think the trade-off is, because the JS files load first, it might affect loading time. The files are not big and it's not critical for my project so I think it's ok. Maybe if loading time is more critical in future I might try npm install bootstrap or some other solution.

nusantara
  • 1,109
  • 1
  • 15
  • 38
1

Best way to add Bootstrap and related dependency with gatsby is adding them in gatsby-browser.js like following:

Step 1: Run npm i bootstrap jquery popper.js

Step 2: In gatsby-browser.js add following imports

import "bootstrap/dist/css/bootstrap.min.css";
import "jquery/dist/jquery.min.js";
import "popper.js/dist/popper.min";
import "bootstrap/dist/js/bootstrap.min.js";

Note: if gatsby-browser.js is already present and contains ES5 code, then that needs to converted to ES6 first

Kushal Bhalaik
  • 3,349
  • 5
  • 23
  • 46
1

Script tags should be placed at the end of page.

Whereas Helmet and gatsby-plugin-load-script try to append script into the head (see link).

Basically, you just need a gatsby-ssr.js file like this, no need plugin at all.

Create gatsby-ssr.js

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
    />
  ])
}

image

ninhjs.dev
  • 7,203
  • 1
  • 49
  • 35