Minimizing flash on app load

I’m building an single-page, progressive web, todo app in Elm (code, demo). Because the PWA features are not available inside Elm, I need to embed the compiled JS into custom HTML alongside some more JS setup (e. g. for the service worker).

I’ve noticed that when loading the app, it quickly flashes the disclaimer about JS not being available before Elm kicks in. However, I think that some while back, when reloading the page, it didn’t appear, so maybe I introduced the flash when adding PWA features. But I know that in Elm Reactor there is absolutely no flash.

Playing around with where I put the compiled Elm code (e. g. separate JS file, embedded into HTML like Elm Reactor) did not give me much insight to what’s happening. I’m unsure whether this is due to the service worker, other Elm-external tech I used, or if there is a better way of including the Elm code so that this does not happen.

Do you have any pointers on how to integrate the Elm code more tightly to prevent the flash?

I solved a similar issue in my app by surrounding the “JS disabled” message with a <noscript> tag. That way it only shows up if Elm wouldn’t work anyway.

Wow, that actually seems to have worked. I was sceptical at first, but I think I understand what’s going on.

I didn’t use <noscript>, because I thought it only worked when JavaScript is deactivated. I thought that it might be nice to display a message also when the loading failed (in which case JS is activated, so the <noscript> shouldn’t kick in, if I understand correctly).

However, I just tried <noscript> and when I refresh the page, there is no flash. So my guess is that the browsers eagerly try to paint, unless there is nothing to paint. With a <noscript>, the browser correctly determines that there is nothing of interest to paint and so seems to wait until Elm populates the DOM with something paintable. That’s my theory at least. :mortar_board:

So thanks a lot for your response! Maybe someone knows more about this issue and can check my theory or provide more details.

1 Like

I can try and answer that!

(Un)Blocking Browser Paint

There are certain things that will block rendering in the browser. Two common ones are scripts in <head> and an external stylesheet linked with <link rel="stylesheet">. Your application does not link a stylesheet (that I could find looking at index.html), so the rendering does not block. Instead, styles come in as interleaved <style> tags, I guess from elm-css or similar. Thus, any content in index.html will paint as soon as the html comes in. Putting it in <noscript> makes the intent about “no JS available” explicit.

Not blocking the browser and allowing it to paint as soon as it has data is typically a good thing! One common optimisation is to inline the CSS that will be used for that page only in the <head>, and load the rest lazily. If your styles are rendered from the script, you get part of this built-in.

Playing with eager paints

In the case of client-only applications (i.e. ones that need the script to come in so that it can render), it seems nice to me to have a minimal placeholder while the app loads. I tend to avoid text, and rather try to match what the general UI (navigation, content, colours) will look like once the script has rendered. The UI that you have linked is pretty minimal right now, but I think it would be fun to try this once it is more settled. For instance, you might have another “flash” if your app has a background other than white, since the browser will render with white first, then swap.

Some more references

Google refers to parts of this strategy as “App shell”. They describe something more involved, and I think minimalism goes a long way here.

I am doing what I described above in my side project. I don’t have the source published right now, but if you compare the raw index.html with the rendered one, or slow down the page load, you will see the loading strategy.

Hope this helps to some degree! I love this topic :slight_smile:

2 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.