Gatsby: Recommended way to gracefully display a message for unsupported browsers

Created on 12 Oct 2018  路  12Comments  路  Source: gatsbyjs/gatsby

I'm using MobX 5 in my Gatsby app, which requires proxy support and thus it'll simply throw an error and crash on IE (all versions). I don't care at all about IE (nor Edge actually), but I still don't want my users to see an empty page when they try to access my app from one of those browsers.

I've tried a couple of different approaches, but they all seemed hacky and unpleasant.

What's the best way to display a message for these users ? I reckon it has to be done at the lowest (or earliest) stage possible, as MobX will crash JS right after it's been imported (and I import in in my layout component of course).

I considered using conditional HTML comments in my html.js file ([If !IE]...), and serve a specific bundle to those users, but that proved to be quite challenging because React doesn't really allow this in a simple fashion.

I have a feeling the key is in html.js but I can't quite figure it out. I'm pretty sure other people wanted to achieve something similar, any tips ?

question or discussion

All 12 comments

Maybe just using proxy polyfill would be easy way out of it - something like https://github.com/GoogleChrome/proxy-polyfill?

As for [If !IE] you would probably need to adjust html.js as you suspected and use something like https://zhenyong.github.io/react/tips/dangerously-set-inner-html.html so react just render strings there. Not sure if this will really work, but worth a try I think

Hey, thanks for taking the time to take a look at this.

However, Google's polyfill doesn't support all of proxy's traps and unfortunately won't help me. I still have the option to downgrade MobX, but since I don't really care about IE I chose the "display a message" route. But yeah, ideally, a polyfill would have been what I would have chosen.

As for HTML comments, React requires a wrapper to use dangerouslySetInnerHTML (like a div, a span, etc..), which doesn't really cut it for me.

I have researched this a little further since posting this issue, but no good way to do this came up.

This is why I geared this question towards "how do I detect browser early on, and chose what top component to render in Gatsby", if this makes any sense ? Ideally, I'd like to be able to run code in the browser as early as html.js, right as it mounts and implement my logic here.

@Coriou what about just adding a custom script? e.g. in html.js something like the below--consider this pseudocode. Not 100% sure it will work but worth a shot. The general approach would be:

  1. Create an ie.html file (outside of Gatsby, or at least in a Gatsby route without Mobx--if that's possible!)
  2. Add a custom script to feature detect Proxy, see this article
  3. Redirect to this ie.html file
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"
        />
        <script dangerouslySetInnerHTML={{ __html: `
      // note: this needs to go before any JS is loaded, e.g. probably in the head tags?
      if (typeof window !== 'undefined' && typeof window.Proxy === 'undefined') {
        // do something, e.g.
        window.location.replace('/ie.html') // this presumes you set up an ie.html fallback, i.e. a vanilla HTML file you create yourself
      }
     ` }} />
        {props.headComponents}
      </head>
      <body {...props.bodyAttributes}>
        {props.preBodyComponents}
        <div
          key={`body`}
          id="___gatsby"
          dangerouslySetInnerHTML={{ __html: props.body }}
        />
        {props.postBodyComponents}
      </body>
    </html>
  )
}

export default Html

@pieh also recommended using onClientEntry. This is a great place to test out the above script and see if it works for you!

Damn it. Yes, that鈥檚 it guys. Just gotta redirect manually to a dummy page using the onClientEntry hook. As simple as that.

Thank you very much to both of you !

Hey. Just a little follow up on this issue :

  • I found out that onClientEntry isn't guaranteed to run first, using this hook didn't help me because html.js was already rendered by the time I ran my code and the JS was crashed on IE. Maybe I'm doing something wrong here, but it seemed pretty straight forward

  • I tried using usual logic in html.js to implement my own version of this hook : in the render method, I wait for window to be available (by "blocking" the rendering of the page until it is). When I becomes available, I do my user agent checking to determine whether it's IE or not. When that check is done, I either redirect to my IE specific page or allow the rendering of the real page. This approach didn't work because for some reason the html.js doesn't seem to behave like a regular React component (state update doesn't trigger a re-render and componentDidMount is never called)

I placed a lot of faith in the second approach. It should by all means work if the html.js was behaving as a regular React component, but it seems that I can not trigger a re-render at any point... I didn't dive deep into Gatsby's core code, but it seems that html.js is only processed server-side, never in the browser.

I feel like I'm closer to solving this, but I'm still missing something here ...

@Coriou is there a reason the approach I posted won't work? It seems pretty clear that that snippet will execute before any other JS executes, and will therefore work exactly like you want?

I just tested it out, and it it seems like that script executes like you'd want and works appropriately.

@DSchau I鈥檒l give this exact approach a try tomorrow, you鈥檙e right it might just work... sorry for wasting your time. I鈥檒l let you know

Indeed, it just works... Thanks a lot !

@Coriou, @DSchau Just wondering how to create a page outside of Gatsby, as recommended above? I have a similar issue.

@chocobuckle You can generate it using the build hooks of Gatsby. In my case, I created the HTML page and using the onPostBootstrap hook to write it to the public directory on build:

// gatsby-node.js

exports.onPostBootstrap = () => {
    let iePage = path.resolve("./src/ie.html"),
        iePublicPath = path.resolve("./public/ie.html")

    if (fs.existsSync(iePage))
        fs.createReadStream(iePage).pipe(fs.createWriteStream(iePublicPath))
}

Making the ie.html page available publicly at mysite.com/ie.html.

@Coriou Thanks for the explanation. All working perfectly.

Was this page helpful?
0 / 5 - 0 ratings