I mentioned yesterday that blog-pwa uses a static rendering layer for cases when the browser or whatever is fetching the page doesn’t support JavaScript. This was a slight simplification on my part. blog-pwa defines four primary cases:
- Are you a known bot or tool that isn’t going to understand JavaScript or understand the noscript redirect? STATIC
- Are you a browser that supports JavaScript? PWA
- Are you a browser with JavaScript support turned off? PWA + NOSCRIPT Redirect to STATIC
- Are you a browser with JavaScript support but no ES Modules support? PWA + NOMODULE Redirect to STATIC.
These four primary cases are what I use for any content serving progressive web app I build today. Doesn’t matter the scale, if I’m building a PWA for someone, I consider these four cases the key to serving as many users and bots that I can (though in the case of #4 above, that might serve a different ES5 bundle based on what features I can patch).
Note: I said “content serving” because some PWA’s are offering more native app-like feature sets using things like WebUSB or WebBluetooth and the expectation that these should fallback the same way is not the same in my opinion.
In the case of blog-pwa, this takes place at both the server level (blog-pwa’s Google App Engine backend) and the client level (blog-pwa’s index.html shell). In most PWA buildouts in the last year or so, I always use server and client approaches.
In the case of server side, I check two cases:
- Are you a bot or tool in the known list?
- Are you a request with the url queryparam
static
set to true?
The second option works in conjunction with the blog-pwa’s index.html shell, which itself defines two cases to cause clients to redirect with static set to true based on enabled browser feature set:
- In the no JavaScript support enabled case, we use
noscript
and a meta
redirect tag.
- In the no ES Module support case, we use
script nomodule
and location.href
to handle the redirect.
To make this work, I cheat on first load by injecting a template variable server side into index.html that in modern browsers with JavaScript enabled won’t matter to handle the above two cases:
<noscript>
<p>JavaScript appears to be turned off.
No problem, this Progressive Web App is Progressive!
Redirecting to static page...</p>
<meta http-equiv="refresh" content="0;url={{ noscript }}">
</noscript>
<script nomodule>
// IE11 and anything that doesn't support ES Modules gets the no-JS
// version of the site
location.href = '{{ noscript }}';
</script>
This isn’t fancy or heavy engineering; this is just testing against features. No fancy user agent checking required.
With these cases and the static side setup, I’m able to safely say that the site renders in things like browsers like Lynx quite beautifully.
Even more crazy is that you still get a reasonably readable render in something like Internet Explorer 8.
Are there a lot of users viewing my site in these browsers? No. Does that mean I (or you) shouldn’t plan for this scenario? I’m of the opinion people should be able to read my content even on the most terrible of connections or browsers.
Should it all looks the same in every browser ever designed? No. A lot of folks might disagree, but the engineering effort to make that happen is significantly high and you better be doing some cost-benefit analysis before you jump into those waters. I’ve seen projects crash and burn and take down jobs. Don’t be that team.
Not every PWA might need these cases or offer more fine grained approaches. If you’re not thinking about these cases and you’re only relying on those JavaScript based analytics, do you really know how much syndication or user base you’re missing? Dig a little deeper and you might be surprised.