Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gatsby-plugin-react-helmet orders the components too late in <head> #22206

Open
Ciantic opened this issue Mar 12, 2020 · 41 comments
Open

gatsby-plugin-react-helmet orders the components too late in <head> #22206

Ciantic opened this issue Mar 12, 2020 · 41 comments

Comments

@Ciantic
Copy link

@Ciantic Ciantic commented Mar 12, 2020

Description

When I use gatsby-plugin-react-helmet I notice that the <Helmet /> components come way too late in the head tag. I have noticed that on occasion for instance Facebook does not always parse the og tags if they come too late in the head.

Steps to reproduce

Just use <Helmet />, and notice in the static output files there is a lot of CSS related stuff etc. above the helmet tag in the <head>.

Expected result

Helmet tags should be prioritised when doing the <head /> tag.

Workaround

Currently there is easy workaround, but I consider that gatsby's default is broken. We should not need workaround for this.

gatsby-ssr.js

var React = require("react");

// Hack, to reorder the helmet components as first in <head> tag
exports.onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
    /**
     * @type {any[]} headComponents
     */
    const headComponents = getHeadComponents();

    headComponents.sort((a, b) => {
        if (a.props && a.props["data-react-helmet"]) {
            return 0;
        }
        return 1;
    });
    replaceHeadComponents(headComponents);
};
@Ciantic Ciantic added the type: bug label Mar 12, 2020
@pieh
Copy link
Contributor

@pieh pieh commented Mar 12, 2020

Do you have any "sources" about facebook skipping trying to extract og tags if they are not early enough?

I'm not sure but ordering of this stuff shouldn't matter as long as it's there.

@pieh
Copy link
Contributor

@pieh pieh commented Mar 12, 2020

In https://html.spec.whatwg.org/multipage/semantics.html#the-meta-element I don't see any notion about ordering. Only ordering restrictions relate to charset meta - https://html.spec.whatwg.org/multipage/semantics.html#charset :

The element containing the character encoding declaration must be serialized completely within the first 1024 bytes of the document.

@pieh
Copy link
Contributor

@pieh pieh commented Mar 12, 2020

So if facebook don't pick up those tags if they are later on in - it seems like it's facebook that doesn't follow spec?

@Ciantic
Copy link
Author

@Ciantic Ciantic commented Mar 12, 2020

No I don't have sources, this is a few years ago, I had the problem in entirely different stack. I watched the FB bot crawling and downloading only n-amount of bytes from the top, and if the og tags was not there it didn't parse them.

I also have Content-Security-Policy, and it's odd if it comes after the style files.

@Ciantic
Copy link
Author

@Ciantic Ciantic commented Mar 13, 2020

I gave the workaround in my main post already. If this is not actionable at the moment I will close this.

@ttstauss
Copy link

@ttstauss ttstauss commented Apr 24, 2020

I ran into the same issue. I tried checking the meta/og tags via several tools (e.g. Open Graph Check, Meta Tags, and Social Share Preview) and none of them were able to pick them up.

Once I mad a tweak similar to @Ciantic's I was able to retrieve them successfully.

I beleive this was mainly caused by a rather large style tag being placed before the meta tags.

My solution requried a bit more to get it working. I had to implement the following in my gatsby-ssr.js (you may be able to get away with just using the onRenderbody api, but I used the onPreRenderHTML api as well to move them completely to the top):

const React = require("react")
const { Helmet } = require("react-helmet")

exports.onRenderBody = (
  { setHeadComponents, setHtmlAttributes, setBodyAttributes },
  pluginOptions
) => {
  const helmet = Helmet.renderStatic()
  setHtmlAttributes(helmet.htmlAttributes.toComponent())
  setBodyAttributes(helmet.bodyAttributes.toComponent())
  setHeadComponents([
    helmet.title.toComponent(),
    helmet.link.toComponent(),
    helmet.meta.toComponent(),
    helmet.noscript.toComponent(),
    helmet.script.toComponent(),
    helmet.style.toComponent(),
  ])
}

exports.onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
  const headComponents = getHeadComponents()

  headComponents.sort((x, y) => {
    if (x.props && x.props["data-react-helmet"]) {
      return -1
    } else if (y.props && y.props["data-react-helmet"]) {
      return 1
    }
    return 0
  })

  replaceHeadComponents(headComponents)
}
@Dres90
Copy link

@Dres90 Dres90 commented May 29, 2020

Thank you @ttstauss your fix worked perfectly.
This is an issue with whatsapp link sharing as well and other platforms that render previews. Hopefully it will be addressed in the main Gatsby release.

@Ciantic Ciantic reopened this May 30, 2020
@Ciantic
Copy link
Author

@Ciantic Ciantic commented May 30, 2020

If this issue exists, then I will reopen this.

I think that Gatsby should handle the good ordering by default, and these hacks are just adding noise to us all trying to maintain configurations which should be in Gatsby itself.

@jun-gh
Copy link

@jun-gh jun-gh commented Jun 2, 2020

What's the directory of gatsby-ssr.js?

@ttstauss
Copy link

@ttstauss ttstauss commented Jun 2, 2020

What's the directory of gatsby-ssr.js?

@jun-gh, gatsby-ssr.js goes in the root of your project (https://www.gatsbyjs.org/docs/ssr-apis/)

@jun-gh
Copy link

@jun-gh jun-gh commented Jun 2, 2020

What's the directory of gatsby-ssr.js?

@jun-gh, gatsby-ssr.js goes in the root of your project (https://www.gatsbyjs.org/docs/ssr-apis/)

I tried adding this on my root directory, but still Helmet tags are still under several style tags.

@github-actions
Copy link

@github-actions github-actions bot commented Jun 23, 2020

Hiya!

This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here.
If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!
As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks for being a part of the Gatsby community! 💪💜

@github-actions github-actions bot added the stale? label Jun 23, 2020
@Ciantic
Copy link
Author

@Ciantic Ciantic commented Jun 23, 2020

I don't have means to add labels. I wish one could just give reaction to that bot to not close it. Short of that, I have to comment.

@LekoArts
Copy link
Contributor

@LekoArts LekoArts commented Jun 23, 2020

We are happy to review a PR changing the behavior of gatsby-plugin-react-helmet however we won't work on this and thus the bot asking for activity is correct. We don't consider this being broken but a feature request.

@Ciantic Ciantic closed this Jun 23, 2020
@Ciantic
Copy link
Author

@Ciantic Ciantic commented Jun 23, 2020

I closed this, since that's what the bot would have done, if this is not actionable.

I would have thought that if the default gives a broken behavior as commented by others, it would be a bug. But others bug is someone's feature as the saying goes.

Edit: How can you read mr. @ttstauss and @Dres90 comments as anything else than a bug? The default behavior leads to OG meta tags not being identified correctly, isn't that a bug?

@nhc
Copy link
Collaborator

@nhc nhc commented Jun 29, 2020

For the record I also had to implement this fix and I think ordering of head elements should be a feature. I want that level of control right out of the plugin

@EricEisaman
Copy link

@EricEisaman EricEisaman commented Jun 30, 2020

Thank you @ttstauss . Your fix worked for me.

@OWMC
Copy link

@OWMC OWMC commented Jul 3, 2020

  • one that this should be a feature. @ttstauss 's fix worked for me.
@BagchiMB
Copy link

@BagchiMB BagchiMB commented Jul 7, 2020

@jun-gh I am also facing the same problem meta tags are still below the style tags, did you got a workaroud ?

@evanmrose
Copy link

@evanmrose evanmrose commented Jul 8, 2020

Also facing this issue

@jun-gh
Copy link

@jun-gh jun-gh commented Jul 8, 2020

@github-actions
Copy link

@github-actions github-actions bot commented Jul 28, 2020

Hiya!

This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here.
If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!
As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks for being a part of the Gatsby community! 💪💜

@github-actions github-actions bot added the stale? label Jul 28, 2020
@RodrigoPrior
Copy link

@RodrigoPrior RodrigoPrior commented Jul 28, 2020

This should not be closed automatically....and I think this is not a feature request...header rendering control is part of template building in most of the web frameworks and it should be reviewed as it is a requirement for seo and social.

@github-actions github-actions bot removed the stale? label Jul 29, 2020
@RodrigoPrior
Copy link

@RodrigoPrior RodrigoPrior commented Aug 10, 2020

I have also tested all the proposed solutions here and none of it works with the latest gatsby.

@nhc
Copy link
Collaborator

@nhc nhc commented Aug 12, 2020

I have also tested all the proposed solutions here and none of it works with the latest gatsby.

I am running

"gatsby": "^2.21.28",
"gatsby-plugin-react-helmet": "^3.3.10",
"react-helmet": "^6.1.0",

And none of the fixes above work. They used to work, but something recently has broken the hack described above.

@kenjinp
Copy link

@kenjinp kenjinp commented Aug 14, 2020

I am also experiencing this issue, but I cannot rely on SSR solutions, so sadly I cannot try the suggested work-arounds.

@AdamQuadmon
Copy link

@AdamQuadmon AdamQuadmon commented Aug 19, 2020

I tried everything, from purgecss to moving og:meta as top as I can then lowering resolution to 600x600 and file size to 210Kb

still no thumb, please help us!

@kenjinp
Copy link

@kenjinp kenjinp commented Aug 20, 2020

I was ignorant about the fact that gatsby-ssr.js can be used to manipulate static builds. In fact, I've tried @ttstauss 's solution and it appears to work fine!

@AdamQuadmon
Copy link

@AdamQuadmon AdamQuadmon commented Aug 21, 2020

Actually, it was working with @ttstauss 's solution. It didn't shows up for WhatsApp cache issues I guess.

@MorrisonCole
Copy link

@MorrisonCole MorrisonCole commented Sep 4, 2020

Dropping in to share an example production case (+ sources) where this is happening: https://search.google.com/test/rich-results?id=7VKg0YQtEoYGPlBlTD60RQ

Google reports 'Page not eligible for rich results known by this test' but viewing the rendered HTML shows that the tags are indeed present.

My personal website has a tonne of styles injected by the Gatsby MUI plugin. Until recently it was OK, but recently I added an inlined SVG and after that, it looks like parsers have just given up looking for the meta tags that follow it.

Obviously I can work around this as others have, but FWIW it does seem to be an easy problem to run into. My particular case is a little more complicated than the above since I'm injecting a lot of styles/metadata on the client-side at the moment.

@NLeRoy917
Copy link

@NLeRoy917 NLeRoy917 commented Sep 9, 2020

Im having this issue and @ttstauss 's work around doesn't seem to work for <meta> tags. But it does pull the <title> tag to the top!

@ilyador
Copy link

@ilyador ilyador commented Sep 11, 2020

I was trying @ttstauss 's solution, but for some reason, gatsby-browser.js and gatsby-ssr.js ignore onRenderBody and onPreRenderHTML. I also use wrapRootElement in the same file and it works fine.

import React from 'react'
import Helmet from 'react-helmet'
import Wrapper from './src/templates/pages-layout'

export const wrapRootElement = ({ element }) => <Wrapper>{element}</Wrapper> // works

export const onRenderBody = (
  { setHeadComponents, setHtmlAttributes, setBodyAttributes },
  pluginOptions
) => {
  console.log('onRenderBody')  // doesn't work
  ...
}

export const onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
  console.log('onPreRenderHTML') // doesn't work
  ...
}

Any ideas why?

@github-actions
Copy link

@github-actions github-actions bot commented Oct 2, 2020

Hiya!

This issue has gone quiet. Spooky quiet. 👻

We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 20 days since the last update here.
If we missed this issue or if you want to keep it open, please reply here.
As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request. Check out gatsby.dev/contribute for more information about opening PRs, triaging issues, and contributing!

Thanks for being a part of the Gatsby community! 💪💜

@github-actions github-actions bot added the stale? label Oct 2, 2020
@dougestey
Copy link

@dougestey dougestey commented Oct 9, 2020

Open graph tags not being pulled in from a Gatsby site to Facebook/etc is a bug.

@github-actions github-actions bot removed the stale? label Oct 10, 2020
@nhc
Copy link
Collaborator

@nhc nhc commented Oct 10, 2020

There is a bug here and it needs investigating. I will try and pull together a decent test case over the next week.

At one time the solution at the top worked fine, but as versions have progressed, the re-ordering has stopped completely.

So for example in my case

exports.onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
  const headComponents = getHeadComponents()

  headComponents.sort((x, y) => {
   console.log(x.props["data-react-helmet"]) // <---- THIS IS ALWAYS AN EMPTY ARRAY
  })

  replaceHeadComponents(headComponents)
}

So the ordering just doesnt work. I don't know if another plugin like gatsby-plugin-manifest is interfering somehow, but I tried a basic install and the above was always just empty. I know this isnt a proper bug report, but it might help someone.

@meugeniatr
Copy link

@meugeniatr meugeniatr commented Oct 12, 2020

Hi! I'm having the same issue as you guys. I have a huge style tag rendered first on the HTML when my intention is to put meta tags and hreflang on top of the head, since SEO best practices (and it's even noted on Google documentation) follows this guideline. I am working with Gatsby and still did not get something to work this way.

I will keep you updated about this fix, which is extremely delicate and basic for it to not have been handled by the plugin this far. Definitely NOT a feature.

@rodrigo-arias
Copy link

@rodrigo-arias rodrigo-arias commented Oct 16, 2020

Same problem here, the proposed workarounds did not work in my case. In my opinion this is a bug.

@ttstauss
Copy link

@ttstauss ttstauss commented Oct 16, 2020

Something definitely changed. @nhc touched on it above. When looking at the headComponents (using getHeadComponents()), I see significantly fewer components than there were before, including a bunch of empty arrays. The only "data-react-helmet" component I see is the title which happens to be inside an array (so now the sorting would need to be done after the array is flattened I believe).

My initial testing shows only a select number of components are generated from getHeadComponents() at the time it's called, many of the other tags (including the large style tag, helmet tags, and other tags) are being placed before the small number of components we have access to sort. In some cases, I can't event get the small number of components we have access to sorted anymore.

Gatsby docs indicates the getHeadComponents returns the current headComponents array. Gatsby docs also states the following for replaceHeadeComponents: WARNING if multiple plugins implement this API it’s the last plugin that “wins”. Not sure if the issue is related to when getHeadComponents is called or if there's another plugin accessing the API before/after.

Either way, I can confirm the previous hack does not work as it did before. I'm currently investigating the SEO implications this is potentially causing, but I do know various previews continue to be affected.

@ttstauss
Copy link

@ttstauss ttstauss commented Oct 16, 2020

So far I've tested the following on two separate websites with success (I'm early in my testing so I can't guarantee that'll work for everyone):

export const onRenderBody = ({
  setHeadComponents,
  setHtmlAttributes,
  setBodyAttributes,
}) => {
  const helmet = Helmet.renderStatic()
  setHtmlAttributes(helmet.htmlAttributes.toComponent())
  setBodyAttributes(helmet.bodyAttributes.toComponent())
  setHeadComponents([
    helmet.title.toComponent(),
    helmet.base.toComponent(),
    helmet.meta.toComponent(),
    helmet.link.toComponent(),
    helmet.noscript.toComponent(),
    helmet.script.toComponent(),
    helmet.style.toComponent(),
  ])
}

export const onPreRenderHTML = ({
  getHeadComponents,
  replaceHeadComponents,
}) => {
  const headComponents = getHeadComponents()
  const order = ["title", "base", "meta", "link", "noscript", "script", "style"]

  const sortedHeadComponents = headComponents
    .slice(0)
    .flat()
    .sort((x, y) => {
      return order.indexOf(x.type) - order.indexOf(y.type)
    })

  replaceHeadComponents(sortedHeadComponents)
}

Notes on what I did differently:

  • I decided to avoid modifying the array in place by using the splice method.
  • I had to flatten the headComponents array in order to evaluate each item's type property (some of the components come in as array's instead of just objects).
  • I created an order array to dictate what tags I want where and applied it in the sort method.
  • I'm now sorting by type as I felt this is the ultimate goal.

As I said, this has worked so far, but I'm still testing.

While developing (or running builds) I still don't know why I can't see all tags when calling getHeadComponents() (hence the empty arrays from my previous comment). I do see other tags that get missed with this approach, but for the most part, it seems to be working (I tested some link previews and they're working much better now).

If anyone thinks there's a more efficient approach I'd be happy to see it.

Edit: Also, gatsby-plugin-react-helmet's onRenderBody implementation is almost identical so I think removing that portion and relying on the plugin for the implementation details would be worthwhile (the plugin is well maintained and keeps up with react-helmet updates).

I'm not sure if sorting the tags would be considered a bug, since the purpose of this plugin is to just get it server-side rendered. Gatsby docs clearly state that onPreRenderHTML is a great option for rearrangement of tags (giving options specific to the head). On the flip side, one of the primary purposes is better SEO which would need better placement of certain tags I think. Does react-helmet handle all head tags generated for a page, including ones injected by other plugins? Does this kind of option belong in this plugin specifically or should it be its own plugin?

Note: I ran this code on fresh builds (cleared cache) and in some cases needed to unregister any serviceWorkers.

@rodrigo-arias
Copy link

@rodrigo-arias rodrigo-arias commented Oct 27, 2020

Hi @tterb thanks for spending time on this. I tried your solution and although I see much less tags in development than in production, even in production I don't get the meta tags from React Helmet.

So the tags are being sorted, but the meta tags are last since I don't get them with getHeadComponents().

@jmontillah
Copy link

@jmontillah jmontillah commented Nov 19, 2020

Hi, modifying gatsby-ssr worked for me, but just for the pages created with content from Contentful, not for the pages that are in src/pages directory. Anyone does know why? Or how to apply that solution to src/pages directory files? Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.