Search code examples
javascripthtmlreactjsgatsbytracking

Gatsby: How to inject raw html inside head tag


In a Gatsby.js project, is there any way to inject raw html inside the <head></head> tag of every page? I'm receiving a string of html for tracking (inline and external script tags, link tags and meta tags) that I just need to dump into the head tag.

Edit: Here is an example of what the html looks like that I'll receive (due to restrictions of the environment I'm working in, I'm not able to edit the html string):

<script src="//sometracking.com/script.js" async></script>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','TAGID');</script>
<!-- End Google Tag Manager -->
<link rel="stylesheet" type="text/css" href="https://somefont.com/stylesheet.css" />
<link href="~/another/stylesheet.css" type="text/css" rel="stylesheet" />
<link href="~/more/styles.css" type="text/css" rel="stylesheet" />
<script language="javascript" type="text/javascript" src="~/vendor/javascript.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="format-detection" content="telephone=no">
<meta name="apple-itunes-app" content="app-id=123456789">
<meta name="google-play-app" content="app-id=com.blah.blah">
<link rel="apple-touch-icon" sizes="180x180" href="~/icon.png" />
..
  • I've tried using react-helmet (requires either a: properties passed to the meta prop or b: the script/meta/link tags to be JSX as children)

  • I've tried using the onPreRenderHTML API within gatsby-ssr.js. (similar problem where it expects jsx instead of a string)

      // THIS RENDERS THE HTML AS TEXT IN THE BODY TAG
    
      const headScripts = `<script type="text/javascript">alert("tracking stuff");</script>`;
      exports.onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
          replaceHeadComponents([...getHeadComponents(), headScripts]);
      };
    
  • Plugins like gatsby-plugin-google-tagmanager are not an option as the tracking html comes in as one big string

Any insight would be greatly appreciated!


Solution

  • I solved this by creating a custom html.js file (Gatsby documentation here) and using html-react-parser.

    First I copied .cache/default-html.js to src/html.js in order to modify the root html document that Gatsby uses. Then I used the html-react-parser package to place the tracking html that is given to me into the head.

    My modifications to the html.js file are indicated by the ADDED comments below

    import React from "react";
    import PropTypes from "prop-types";
    import parse from "html-react-parser"; // ADDED
    import getTrackingHtml = from './myCustomUtils'; // ADDED
    
    export default function HTML(props) {
        const trackingHtml = getTrackingHtml(); // ADDED
    
        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}
                    {parse(trackingHtml)} {/* ADDED */}
                </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,
    };
    
    

    (Note: "Anything you render in the html.js component will not be made “live” in the client like other components" -Gatsby Documentation)