Gatsby: Adding third-party js script to html.js

Created on 14 Apr 2018  Â·  15Comments  Â·  Source: gatsbyjs/gatsby

I'm trying to add a third-party js script to a Gatsby project. The script is for embedding a widget into a particular section on one of my pages. It is not available on npm or anything like that.

<script src="https://somewidgetcompany.com/widget.min.js"></script>

I'm struggling to figure out the best method for including this resource so that I can access / initialize the widget at the appropriate place, as well as set it's attributes:

<a class="bit-widget-initializer" data-product-name="Some Product" data-auto-style="false" data-text-color="#000" data-link-color="#000" data-popup-background-color="#02004A" data-background-color="" data-display-limit="10" data-link-text-color="#FFF" data-separator-color="#000"></a>

Usually I would be able to easily slap it at the bottom of the index.html and call it a day. Upon digging into it, there doesn't seem to be documentation that provides a clean / easy solution for this. I've looked at the Customizing HTML.js page but the example only shows custom inline javascript via dangerouslySetInnerHTML. Seems like a lot of work around for something so simple.

How have others been handling this? I'd love to see a couple different approaches if anyone is willing to share. Thanks

Most helpful comment

I have a very similar issue, with a slightly different outcome.

Client wishes to use the Instagram embed code, and is pasting that into an embedded block in Contentful. I am handling that fine, and have the embed code outputting to the page as needed, but, the code does not run when the page is navigated to, I have to do a hard refresh of the page to get the embedded code to run.

Is there a way to work around this while still giving the client the simplicity of copy/pasting the code in Contentful?

All 15 comments

You can just add your script tag as you've written it above in your html.js file. A simple example of adding something like a script tag would be helpful for that docs page on customizing the html.js page — would you like to add that in a PR?

I'll start a PR shortly for this. I think it would be helpful for some who are starting out, to include a real world example like the one above. I'll also cover some basic gotcha moments like needing to be sure to change things like <a class="widget-initializer"> to <a className="widget-initializer">.

On a side note: I think at first there was a caching issue during my initial attempt to add the script, so it might be important to document that as a consideration for now on the customize html.js docs page?

Thanks!

What kind of caching issue?

Be aware that scripts that init widgets like this don't play well with react and SPAs. It will work on initial page load - but when you navigate to new page (using gatsby-link) widgets on new page might not get initialized. Do you init your widgets manually in js or that script will automatically do that?

That is really great to know @pieh! I'm newer to react so its a good heads up. In my particular case the script included automatically handles the initializing of the widget. I haven't run into a widget like this before now. Knowing about this pitfall certainly has me thinking about how to resolve it differently if there is that type of link issue.

@KyleAMathews I'll try to recreate the exact issue. It was basically one of those instances where I added the script as it should be added and restarted the server. The script wasn't added. So I deleted the cache and restarted the server again and the script showed up as expected.

Yeah, good point @pieh. Checkout the source go gatsby-plugin-twitter for a good example of how to overcome this https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-twitter/src/gatsby-browser.js

Heck yea, tyvm. Appreciate it!

Hi guys, just wanted to reopen this issue as I'm having a similar problem, tried the above method of putting the script just before the closing body tag in html.js, but to no avail. I'm using a custom.js file to collate all my custom scripts like adding and removing of classes on clicks/scroll animations, custom mobile menu, and GSAP animations and all of that other stuff which is usually easy to be done on normal, static html files.

When I placed the <script src="js/custom.js/> in the html.js file, then I gatsby develop and preview it on the browser, the custom.js file had some preloaded html boilerplate code, but my original code isn't there.

I've been trying to solve this issue for a week or two now since I can only work on this during the weekends. I'm new to Gatsby and React so I don't really understand how it loads custom files, so any help/explanation on that would help!

Hi guys, just wanted to reopen this issue as I'm having a similar problem, tried the above method of putting the script just before the closing body tag in html.js, but to no avail. I'm using a custom.js file to collate all my custom scripts like adding and removing of classes on clicks/scroll animations, custom mobile menu, and GSAP animations and all of that other stuff which is usually easy to be done on normal, static html files.

When I placed the <script src="js/custom.js/> in the html.js file, then I gatsby develop and preview it on the browser, the custom.js file had some preloaded html boilerplate code, but my original code isn't there.

I've been trying to solve this issue for a week or two now since I can only work on this during the weekends. I'm new to Gatsby and React so I don't really understand how it loads custom files, so any help/explanation on that would help!

I believe in your case it's because of the file path link. When the site compiles, it may not have a path like 'js/custom.js'. In order to reference this file, you have to put it in the /static folder. Then set the path you want for static/public files.

So for me, I create the folder "static" in root, then in there I put "js/my.js". When the site compiles, this file will be available at the URL "/js/my.js". Or if you set a path, it would be "path/js/my.js".

Unless you use the static folder to hold the files, you can't really reference it as you do there.

Hi guys, just wanted to reopen this issue as I'm having a similar problem, tried the above method of putting the script just before the closing body tag in html.js, but to no avail. I'm using a custom.js file to collate all my custom scripts like adding and removing of classes on clicks/scroll animations, custom mobile menu, and GSAP animations and all of that other stuff which is usually easy to be done on normal, static html files.
When I placed the <script src="js/custom.js/> in the html.js file, then I gatsby develop and preview it on the browser, the custom.js file had some preloaded html boilerplate code, but my original code isn't there.
I've been trying to solve this issue for a week or two now since I can only work on this during the weekends. I'm new to Gatsby and React so I don't really understand how it loads custom files, so any help/explanation on that would help!

I believe in your case it's because of the file path link. When the site compiles, it may not have a path like 'js/custom.js'. In order to reference this file, you have to put it in the /static folder. Then set the path you want for static/public files.

So for me, I create the folder "static" in root, then in there I put "js/my.js". When the site compiles, this file will be available at the URL "/js/my.js". Or if you set a path, it would be "path/js/my.js".

Unless you use the static folder to hold the files, you can't really reference it as you do there.

Also had this problem trying to add file path link to html.js and you saved me, thank's a lot! This solution would be a great addition to the docs.

Yeah, good point @pieh. Checkout the source go gatsby-plugin-twitter for a good example of how to overcome this https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-twitter/src/gatsby-browser.js

You can now use withPrefix to reference static assets from any page as well:

https://www.gatsbyjs.org/docs/static-folder/#referencing-your-static-asset

I have a very similar issue, with a slightly different outcome.

Client wishes to use the Instagram embed code, and is pasting that into an embedded block in Contentful. I am handling that fine, and have the embed code outputting to the page as needed, but, the code does not run when the page is navigated to, I have to do a hard refresh of the page to get the embedded code to run.

Is there a way to work around this while still giving the client the simplicity of copy/pasting the code in Contentful?

Having the same issue.
Trying to embed Iubenda's script tag inside a specific page in it's body.

Still no luck!

Hope this isn't too off-topic but I was trying to do _similar_ and Google led me here.

In my case I was trying to add a Twitcker Twitter ticker. They give you a snippet for traditional HTML sites that you just paste into your page wherever you want it to appear, consisting of a div and an injection script. The injection script then injects the Twitcker javascript itself (as an external src) into the head. I think this is because it's asynchronous for best performance (as it should be) so needs to be loaded after the DOM has been rendered and the ticker div is present. This is fine but of course wouldn't play well with a Gatsby site / React app if you just pasted it into a page somewhere.

I'm still new to Gatsby and React myself and still getting my head around it, so I may be thinking about things in completely the wrong way, but thought "if we're going to work with React we should probably work _with_ React, not _around_ it" which is what editing html.js feels like to me.

So I reimplemented the "pasteable code" (div and injection script) as a React component. I did this with the help of the React tutorials (in particular the State and Lifecycle tutorial)

Therefore I ended up with this components/twitcker.js:

import React from 'react'

class Twitcker extends React.Component {
  constructor(props) {
    super(props);
    // "if" block because document object isn't available during build
    // - point 1 here https://www.gatsbyjs.org/docs/debugging-html-builds/
    if (typeof document !== `undefined`) {
      this.ticker = document.createElement("script");
      this.ticker.async = true;
      this.ticker.type = "text/javascript"
      this.ticker.src = "https://embed.twitcker.com/ticker/USERNAME_HERE.js?speed=8&count=7&background=6cbadc&tweet=ffffff&container=own-container&own-container=twitcker";
    }
  }

  componentDidMount() {
    document.head.appendChild(this.ticker);
  }

  componentWillUnmount() {
    document.head.removeChild(this.ticker);
  }

  render() {
    return(
      <div id="twitcker" style={{height: 30}}></div>
    )
  }
}

export default Twitcker

Then it can be put into any page in the same way as any other component. On construction it prepares the script element like the original snippet would've done but it doesn't add it into the head until the component has rendered (componentDidMount) which achieves the same function as the original. Per best practice it then, tidies up after itself (componentWillUnmount) which removes it from the head on pages that don't need it.

I added a height style to the output div so that the rest of the content doesn't move down when the ticker loads (this I think would upset Lighthouse - as it stands, even with the ticker I'm still on 100/100 for everything - either way it'd be irritating for the user).

I wonder if this kind of approach could be used for other similar use cases? But please take this "advice" with a pinch of salt, as I am new to it all (it might actually be really bad advice) - any feedback from the experts is appreciated.

A more generic version of this component should move speed, count, background colour etc into props and establish defaults, but leaving it hardcoded was fine for my purposes. Better still would be to reimplement the whole twitcker in React but I'm not sure how feasible that is since their main JS is dynamically generated, plus I wouldn't want to tread on the author's toes too much.

I have a very similar issue, with a slightly different outcome.

Client wishes to use the Instagram embed code, and is pasting that into an embedded block in Contentful. I am handling that fine, and have the embed code outputting to the page as needed, but, the code does not run when the page is navigated to, I have to do a hard refresh of the page to get the embedded code to run.

Is there a way to work around this while still giving the client the simplicity of copy/pasting the code in Contentful?

Same here, the script works only after doing a hard refresh. Are there any fixes/workarounds for this yet?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  Â·  3Comments

brandonmp picture brandonmp  Â·  3Comments

jimfilippou picture jimfilippou  Â·  3Comments

3CordGuy picture 3CordGuy  Â·  3Comments

andykais picture andykais  Â·  3Comments