How to include local javascript on a Gatsby page?
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:
Create a folder
static
to the root of your project.Put your script
script.js
in the folderstatic
.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.
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
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
userequire()
to execute the script insideuseEffect()
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 yourscript.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 `}} />