Embedding clutch.co widget to Gatsby.js website
Asked Answered
M

2

6

I'm trying to add a clucth.co widget to a Gatsby site, but it does not render. I've tried using react Helmet for the <script> part, but it still does not work.

Hopefully I'm missing something simple here, but looking at other solutions I can't find anything that works.

For reference: https://clutch.co/content/add-review-widget-your-website

<script type="text/javascript" src="https://widget.clutch.co/static/js/widget.js"></script>
    <div className="clutch-widget" data-url="https://widget.clutch.co" data-widget-type="7" data-height="65" data-clutchcompany-id="XXXXXXX"></div>
Mercy answered 19/8, 2021 at 11:18 Comment(1)
good day - can you please help me: what is a clutch widget!?Troglodyte
D
4

You have multiple ways of inserting a third-party script in Gatsby. The problem you'll face in all of them is that you need to await that your div:

<div className="clutch-widget" data-url="https://widget.clutch.co" data-widget-type="7" data-height="65" data-clutchcompany-id="XXXXXXX"></div>

Needs to be rendered your script won't be able to load.

Using Script component (2022 update)

Since the release of the Script Gatsby component (powered by Partytown) it's much easier adding third-party scripts. Just:

import React from "react"
import { Script } from "gatsby"

function YourPage() {
  return <Script src="https://my-example-script" />
}

export default YourPage

Using Helmet:

You said you already tried but it should. You may need to try the drop-in support that adds the gatsby-plugin-react-helmet. Then:

<Layout>
  <SEO title="Live" />
  <Helmet>
    <script src="https://tlk.io/embed.js" type="text/javascript"/>
  </Helmet>
</Layout>

Check the compatibility issues when used with hooks.

Using onRenderBody API from gatsby-ssr.js:

Gatsby exposes a setHeadComponents function in the onRenderBodyAPI that you can take advantage from:

import React from "react"

export const onRenderBody = ({ setHeadComponents }, pluginOptions) => {
  setHeadComponents([
    <script key="tracking"
      src="https://widget.clutch.co/static/js/widget.js
      type="text/javascript"
      async
    />,
  ])
}

This snippet above will insert the <script> in the <head> tag of the compiled HTML.

Here you have another approach using dangerouslySetInnerHTML:

setHeadComponents([
    <script dangerouslySetInnerHTML={{whateveryouneedtoset}}>
])

Extracted from Unable to Inject 3rd Party Scripts in Gatsby

Modifying directly the html.js:

You can customize even more the output of the resultant HTML by modifying the html.js, the boilerplate that uses Gatsby to build your entire site.

Run:

cp .cache/default-html.js src/html.js

Or alternatively, copy the default-html.js from .cache folder into /src and rename it to html.js. When compiling, if the html.js is present, Gatsby will take it to build your site based on that skeleton.

You'll have something like:

import React from "react"
import PropTypes from "prop-types"

export default function HTML(props) {
  return (
    <html {...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"
        />
        {props.headComponents}
      </head>
      <body {...props.bodyAttributes}>
        {props.preBodyComponents}
        <div
          key={`body`}
          id="___gatsby"
          dangerouslySetInnerHTML={{ __html: props.body }}
        />
        {props.postBodyComponents}
      </body>
    </html>
  )
}

HTML.propTypes = {
  htmlAttributes: PropTypes.object,
  headComponents: PropTypes.array,
  bodyAttributes: PropTypes.object,
  preBodyComponents: PropTypes.array,
  body: PropTypes.string,
  postBodyComponents: PropTypes.array,
}

There you can add your <script> directly:

  <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"
    />
    <script type="text/javascript" src="https://widget.clutch.co/static/js/widget.js"></script>
    {props.headComponents}
  </head>

Using gatsby-plugin-load-script:

Just install and use the plugin:

{
  resolve: 'gatsby-plugin-load-script',
  options: {
    src: 'https://widget.clutch.co/static/js/widget.js',
  },
},

Hacking the gatsby-browser.js API:

If none of the above fits you, you can still use one of gatsby-browser.js APIs (onClientEntry) to manually add script given a source URL:

const addScript = url => {
  const script = document.createElement("script")
  script.src = url
  script.async = true
  document.body.appendChild(script)
}

export const onClientEntry = () => {
  window.onload = () => {
    addScript("https://widget.clutch.co/static/js/widget.js")
  }
}
Declamatory answered 19/8, 2021 at 11:50 Comment(4)
Great solution and explanation! However it turned out is was something else quite simple, the script element does not render on dev environment with npm start, however when built and deployed it will render.Mercy
@Mercy does widget work when you navigate through pages? I have widget on home page, loads pretty much okay but when I go to another page and then back to home (all internal links) then widget is not shown.Heaven
@Heaven without seeing it it's impossible to know what's going on. I'd say (it seems) it's a old-fashioned script that interacts with the DOM directly (using listeners, document or other global objects. This breaks React's hydration that uses a virtual DOM (vDOM). One (dirty) bypassing solution is to force the script loading in every needed page or using some global wrappers that Gatsby SSR offers. The ideal solution would be adapt/using a React-based approachDeclamatory
@FerranBuireu yes, I did something like that, thanksHeaven
S
3

In order to not have the clutch widget disappear on route changes, I ended up running the Init and Destroy methods from window.CLUTCHCO myself in useEffect.

  React.useEffect(() => {
    // add widget to end of body and run it
    const script = document.createElement("script")
    script.type = "text/javascript"
    script.src = "https://widget.clutch.co/static/js/widget.js"
    script.async = true
    document.body.appendChild(script)
    // run script
    script.onload = () => {
      // @ts-expect-error Apparently we have to manually do this!! 🗑️
      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
      window.CLUTCHCO.Init()
    }
    return () => {
      // @ts-expect-error Apparently we have to manually do this!! 🗑️
      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
      window.CLUTCHCO.Destroy()
      document.body.removeChild(script)
    }
  }, [])
Saros answered 25/7, 2022 at 19:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.