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

Recommended way to gracefully display a message for unsupported browsers #9062

Closed
Coriou opened this issue Oct 12, 2018 · 12 comments
Closed
Labels
type: question or discussion Issue discussing or asking a question about Gatsby

Comments

@Coriou
Copy link

Coriou commented Oct 12, 2018

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 ?

@pieh pieh added the type: question or discussion Issue discussing or asking a question about Gatsby label Oct 15, 2018
@pieh
Copy link
Contributor

pieh commented Oct 15, 2018

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

@Coriou
Copy link
Author

Coriou commented Oct 15, 2018

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.

@DSchau
Copy link
Contributor

DSchau commented Oct 15, 2018

@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

@DSchau
Copy link
Contributor

DSchau commented Oct 15, 2018

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

@Coriou
Copy link
Author

Coriou commented Oct 15, 2018

Damn it. Yes, that’s 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 !

@Coriou Coriou closed this as completed Oct 15, 2018
@Coriou
Copy link
Author

Coriou commented Oct 16, 2018

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 ...

@DSchau
Copy link
Contributor

DSchau commented Oct 16, 2018

@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.

@Coriou
Copy link
Author

Coriou commented Oct 16, 2018

@DSchau I’ll give this exact approach a try tomorrow, you’re right it might just work... sorry for wasting your time. I’ll let you know

@Coriou
Copy link
Author

Coriou commented Oct 17, 2018

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

@chocobuckle
Copy link

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

@Coriou
Copy link
Author

Coriou commented Oct 7, 2020

@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.

@chocobuckle
Copy link

@Coriou Thanks for the explanation. All working perfectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question or discussion Issue discussing or asking a question about Gatsby
Projects
None yet
Development

No branches or pull requests

4 participants