Optimistic Offline Experiences

Many things cause connection trouble. Bad reception, flaky Wi-Fi, servers being down, computers resuming from sleep. The standard way to handle this is an App Shell with a “Cannot connect” message. This is a terrible offline experience. We can do better.

How can you balance your UI updates between optimistic and honest? Between spamming users with “Now connected!” notifications and not telling them at all? Does it matter if a Like silently fails?

You’ll learn answers to these, and other ways to make a more fault-tolerant, more fun frontend.

(upbeat music) – [Simon] We’re doing a terrible job.

Given my talk title, I’m sorry to open on such a downer but I did a lot of research and I had already named the talk and here we are.

So I just got back from being overseas on holiday for few weeks and I saw a lot of this. This is the most common offline experience we offer people. This is not great: Error internet disconnected. Can’t really do much with that.

There’s the dinosaur game that’s kind of a pro but you see this, you play the dinosaur game, you lose, and then you forget what you were doing. Within the app we’ve got something more like this, which is just still sad face, so we’ve got a bit of a negative thing about being offline. Not enough of this.

And maybe you wonder why does offline matter anyway. I don’t have any offline users for my site. But everyone’s offline some of the time.

Maybe if you’re on a plane, if you’re on a train in that spot just outside the station where they wait for five minutes while the other train’s on the thing. That’s usually an offline dead zone.

There’s that spot where you leave your house just before you’ve left your home wifi but your phone still thinks it’s connected so it doesn’t connect to the wireless network yet. Offline is everywhere, even if you’re on a wifi network in a building much like this one and you get hundreds of people on computers and phones all connected to it at the same time then you get internet that looks like this. And this internet is slow enough that before the upload test finishes then Chrome thinks you’re offline.

So, offline is not just for offline.

Offline is for slow internet, it’s for legitimately offline, just all sorts of cases we don’t consider.

So currently, how we handle offline with network requests, usually looks like this.

Try to fetch something, then we handle success, and then we’ll try to handle fail.

So, we’ll just look at this last line here. Usually, the way that we handle failure is something like this: we show an error, which is the red message at the top, that’s probably adequate, right? We just say “try again later.” More commonly, we might do something like this. (laughter from audience) I’ve never done this myself.

But again, we just consider offline as being an edge case. We don’t worry about it, it doesn’t matter. These users are not important to us.

But, offline is not an error, it happens to almost everyone. The one person it doesn’t happen to is the guy who has a cable plugged into his dial-up modem and hasn’t left his basement since 1992.

Everyone else is offline at some point when they travel or the internet goes down, their home router is misconfigured, I have got a bunch of these examples, I’ll just keep dropping them in as we go.

But it’s real life; offline is what we’re all gonna have happen at some point, but we’re not developing for it yet.

And, as such, we should probably do something about it. We should think about it, we should write something in that cache block.

You’re allowed to write code inside cache blocks. And, now we can.

This is how we do something about offline.

Since a few months ago, there’s been a “Yes.” on this page, it’s really exciting.

All major browsers support it, including everyone’s favourite, (giggles) the Samsung built-in browser.

(laughter from audience) Which they track the latest version of Chrome and then they use something like fifteen or twenty versions behind that but Samsung browser supports it now so we’re at something like 85% of commonly used browsers now support Service Worker, which gives you offline support. So, Service Worker is basically just code you can run in the browser when the tab’s not there or when they’re not connected, all your requests will check with the Service Worker first before they go through the network.

So that means we will replace the default stage. This is the first thing you’re gonna do when you’ve added a Service Worker.

You say I can add offline pages now so we’re gonna replace this guy.

You’re gonna make a file called offline.html and you’re gonna put some content in there. And there’s some good examples of this, this is a semi-common thing that gets done because it’s everyone’s first Service Worker activity. So, Trivago has a maze if you’re offline.

I didn’t solve this one, and they just have a timer. So it’s a pretty simple, static page.

You don’t need anything super smart on there. You just have a reconnect button, countdown timer, and give them an activity to do.

I didn’t want to have to make a maze, so my attempt attempt was just a message and an animated version of my SVG face.

It can be simple as anything, you’re just giving them an experience that says “Here’s where you are” and something a bit more comforting than just the dinosaur game. It’s probably preferable to this but it’s a start. But people don’t really want that.

As enjoyable as the maze was, I don’t really like mazes. I didn’t try to access that page looking for a maze. I wanted content, and we can give people content. If someone’s been to a page before and they then come back to that page, then they should be able to see it again.

But they can’t; we’re obsessed with freshness. The internet’s obsession is with the newest content being visible at all times. Imagine you walk into this bakery and you see some bread rolls there you say to one of those two people in the background “I’d like this bread roll” and they say “sure, we’re gonna go bake you a fresh loaf of bread.” (laughter from audience) And they wait, this doesn’t go anywhere, it’s an infinite spinner, this is what happens if you wait for your fresh bread while you’re offline on a slow network.

Instead, I say “give me the bread from the picture before, I’ve seen bread already, you’ve got bread right here.” And, why can I not keep that available? This seems ridiculous, why would we have pictures of bread? Content is not bread.

I don’t wanna pick on Reddit because they’re great but part of the reason this is so noticeable is their initial load is so fast and then this loading thing takes so long.

This is just me mashing refresh on the page. The content doesn’t change at all between refreshes here And I’ve still got to wait the full load time because I’m not saving these things because they assume we want the freshest content. But unlike bread, your content can actually be a little bit stale and it’s fine.

I’d rather stale content than have to keep looking at this little guy.

You can just give them the stale posts and then say, we’ve got some up to date ones now and push the new ones up, which I think Twitter does as an example, there’s a lot of examples of of this sort of thing, of serve the stale content that you have available because loading spinners are getting pretty annoying.

Any time you’ve visited a page, then you should be able to save the content.

So you can do that with latest posts, you just save the entire response.

A good example, a way to check what’s possible with offline is just check the blogs and articles and other pages written by clever people.

If you look at the blog of Jeremy Keith, he’s the author of a great book on offline that you should all read, if I don’t mention it at the end, then please remind me ’cause I should tell you to look it up.

This page is saved, this is me being offline and loading this page and I’ve been there before so it works fine.

And this is really good, this means that anytime I want to go back and read this page again then I have the content there and it’s not a big ask that we save that instead of me just getting an offline message. But the problem with this is this is a pretty good experience, I can read this content.

But then I get to the bottom of the page and I have these guys and I click on newer or older, and then I’m gonna get an error, because I haven’t seen the newer or older page before. So we can do better than just saving the current page you’re on.

If you are a blog or some kind of content page for a bigger site, you might have your about and contact pages, these don’t change very often unless you’re changing your company’s phone number every week, there’s probably a good reason to cache those kinds of pages where the content doesn’t change. So, in this case, what we could do is when you load a page, we can say “save the content of this blog post and grab the newer and older articles and save the content of those as well.” And then if someone reads the article they’ll say “oh that was great, I’m gonna read something newer” and they click that and that’ll work.

You could then also save those links.

You can save a lot of text content.

You can save save lower-res versions of images. You can use up quite a bit of storage with text. For reference, consider Moby Dick is something like 1.2 meg of text.

It’s a substantial book, your posts probably aren’t that long, you can save a lot of them. But even so, 1.2 meg is not huge in the amount of storage we get available for caches. You could fit a lot of Moby Dicks in your cache and even more posts, so keep your storage cleaned up. Don’t go overboard and fetch the entire site, but you could grab most of links and this sort of thing. If you want to be even smarter about it, you can do something like get an export from analytics and just see if someone’s on this page, there’s a 90 percent chance they then read this article next so you can use analytics data from what people have been doing to determine what should be cached. Alternatively, if it is something small, like you’ve only got three posts, or if you’re a documentation site, then you can just save all the content.

The Lodash docs are a good example of this. I read these on the flight back.

I didn’t even need to use Lodash but I was just amazed that I could.

It’s not just the current version, they have the docs of different versions.

So here’s me reading the docs for a version I didn’t have and didn’t need, (laughter from audience) but nothing else would work on the plane.

Aeroplane wifi is a nice dream, but it’s awful to use. If you have small enough content, I think the pages are maybe 50K each, so about 200K of total content and I have all the Lodash docs I’ll ever need.

But if you don’t do that and don’t make all your content available, so realistically for blogs, or for articles, or for news sites, say for a news site you just want to load the five latest articles, so there’s going to be a lot of cases where people are hit content that they don’t have, and we don’t want to just throw them back to the maze, so we’ll give them something.

This is Ethan Marcotte’s, this is a great example of offline.

It’ll save the page you’re on, it’ll save a few other suggested pages and if you then try to load a page you haven’t been to before, then it’ll say you’re offline at the moment, here’s some stuff that we have saved for you. One of them is called “Chimpin’.” If you’re not gonna read “Chimpin’…” But just giving people options is useful.

It’s a step up from the maze game rather than giving them something they didn’t come to do, you’re giving them something at least loosely related. Again, you could go smarter with this, you could say we couldn’t find that article about this particular topic, here’s ones related to that we have saved.

You can do all sorts of elaborate sorting and other grouping and just giving people content that they have asked for. But also, with all this forward loading, so text, mostly fine unless you’re storing a library of entire books, you can just preload articles, that’s alright.

Images, pre thread some, but be a bit thoughtful about it. Don’t be downloading full-res hero images of every single post you’ve ever written, you could download a lower-res version of the images, you could just download the alt tags of images, it’s a good reason to mark things up properly, assuming people won’t have the images when they read your article.

If you’ve got truly big things, so video files, audio, other giant stuff, or you want to have a switch to download all my articles offline, then you go for this guy.

So this is on videos for some sites, you can just toggle it and save it offline. It’s good for being opt-in, you want to tell them the size of the file, ideally, as well.

But this also means that if they notice they’re running out of room on their device, then they can switch this off and get rid of it. Just being a bit more considerate of how we use people’s data.

But, unfortunately, the web isn’t just all static content. It’s kind of an exciting, interactive, 3D, dynamic place now, so we have forms.

Forms are pretty exciting if you’re into forms. Love filling out forms.

Sometimes you’ll get an email, you click the link, you start filling out the form on your phone, and then you realise it was a little bit longer than you anticipated, “oh, I’ll finish this later.” So you do, and you finish filling out the form when you’re in transit and then you hit submit without checking your network status and then you hit this and you’re like “Shit, I’ll go back” (groans).

This is below stage zero.

I don’t need to explain how wrong this is.

Worst-best case here, we could at least keep the form data so I don’t have to go through 485 questions again.

Better than that is preventing them from submitting the form.

Don’t let them make the mistake when they’re offline. Our travel friends at Trivago again.

When you’re offline, they show you a little prompt there and they disable all the form fields.

This is kind of good because then I don’t accidentally submit my giant form, kind of bad, because they don’t tell me why these fields are disabled. So if I didn’t notice the notification, I don’t know why I can’t click on stuff.

But if you want easy way in, just do this.

Make sure people can’t submit forms and lose all their stuff.

This is a good, easy starting point.

Or we can do better than that.

We can retry stuff, this is an obvious one. If you want to allow the form to be submitted, then submit it and if it fails, write some code in the cache block that says try again. If we submit this incident report then it’ll just retry. This says retrying in five seconds and kind of increases over time.

This is a good improvement and I know I’m probably not going to lose my data unless I close this tab.

That’s still a thing that can happen.

The retrying thing is good, but while I’m waiting to be connected I’m going to go do another activity and then maybe turn off my phone later and then this is gone.

So, in recent years, we can do better than this with background sync.

So background sync is the next level of retry. We still want to do retry because that’s just a good idea, maybe they’ll be offline soon.

But if that fails again and we have repeated fails, then we want to be able to tell them “we’ll do this when we can.” So often you’re writing a form and you think if I don’t write this now I’m gonna forget what I’m saying so you just hit submit and it can’t send but background sync will then grab this form, and then send it in the background regardless of the tab being open. So background sync is still very new.

It’s less widely supported than Service Worker and other things.

But I think it’s only Chrome and Opera.

But it’s coming to the others pretty rapidly. This is kind of an ideal experience that you give to people who support it.

This means that they can close the tab and the Service Worker, when it’s available, will just send this form data.

This is great; this means that you could safely fill in your application and send stuff.

This is how chat can work, this is how any kind of dynamic online experience can work is just do things when you can online.

I would rather I’m talking to my computer at some time, I tell it to do some stuff and it says I’ll do it later.

That’s good, I can trust it to actually do it. But this does introduce some complications. This is another great example of the offline. This is in Chrome now the download when online button. This is if you hit any content when you’re offline then you get this button that says I will tell you when you reconnect.

And this is a very good experience.

This means I can’t access the content right now but when it’s available, you will get it for me automatically and you will tell me when that’s done. So when I reconnect, then it’ll notify you. You can get a push notification, or some kind of other telling that it’s ready. This is kind of peak offline experience.

Rather than giving someone internet, this is about as well as we can do.

But it does introduce some complications.

So we’ve kind of made our interface fully async. We’ve gone from now someone might come to the site, they try to do stuff and they leave and we need a way to communicate this status with them later and we can’t do this like with a form where you just show them an error message.

We’re trusting that when they submit stuff then it will probably work later and if it doesn’t then we need a way to tell them. And we can do that with push notifications if we have permissions, they’re pretty widely supported and you can then have a push notification saying this works or this failed. Usually you shouldn’t even need to notify on success. Consider I checkout of a store.

You’re shopping online and then you suddenly get disconnected but but you still want to enter your payment details and hit pay.

You should ideally be able to just leave that page then and then when you next connect it’ll send the request and say confirmed, and you get an order confirmation email, and everything’s fine. I want to just be able to hit pay, checkout and I know that it’ll work later.

The only time I’ll need to be notified is if it fails and then I can get a notification that says error or something. That is quite tricky, because it’s dependent on getting push notification permissions as sort of a whole thing there.

But the other tricky aspect of adding offline push notifications is you send a notification to someone and then they might be offline.

If you’re offline and you get a push notification and then you click because you want to sign up, then the push notification disappears and you’re offline. So now I don’t really have a record of that page. Probably gonna run that again.

So we lose the push notification and we have no content on this page.

It’s not great, this is giving someone an opportunity and then taking it away.

Again, hard to do, if you’re gonna be sending push notifications when you ask for permission then you can kind of say here’s pages we’re likely to send you through and we’ll pre-cache some content for those. At least don’t send them to the dinosaur.

Offline isn’t an error.

It happens to me all the time, I don’t know if it’s just ’cause I’m constantly toggling network on my phone so I can find these offline pages but it happens to people all the time.

The experience right now: not great.

We can do a much better job than we are.

So lets try to make better offline experiences. (audience applause) (upbeat techno music)

Questions

  • why go offline
  • what is offline support?
  • why is it often bad
  • how to do it
  • what does offline/disconnected MEAN?
  • what can cause connection problems?
  • why does it matter in australia
  • should offline be the same as online experience? can it be?
  • should I jarringly reconnect ASAP or try for smooth transition
  • is there perf impact to lots of requests being made and 404ing? should i pause
  • when should you tell user they are offline? instant vs when a request fails. It depends
  • how to add this to my existing stack?
  • what is back/forward cache?
  • do i need service worker?
  • what is wrong with basic offline support?
  • should I show stale data or blank page?
  • how to indicate stale data?
  • does stale data matter? always/sometimes?

Notes

1. Why

  • current offline UX mostly awful
  • offline not just for offline. slow internet is almost worse
  • but offline still common with mobile networks
  • and lots of still-offline locations
  • we can!

2. How

How do we know if someone is offline? What does offline even mean?

detecting offline is easy – not connected to any network. So no wifi or cable. But if you are connected to a network, how can you tell if that’s the internet or just a regular net?

navigator.onLine. weird casing. this can tell you if you are offline (onLine === false) or “not offline” (navigator.onLine === true). Not offline is not necessarily online.

If their router is connected (and real), maybe their ISP is down. Maybe their government censors content. If the ISP is up, mayeb the server of the site is down. Or at capacity.

So the most reliable way to test it monitor network requests. If a bunch of them fail, you’re probably offline. There’s some events you can listen for, you could use a dedicated polling thing that checks some cheap isUp endpoint, whatever. Lots of options here.

brief PSA on using .catch correctly. What does .catch mean for fetch what does it mean for your network lib, or your API wrappers or whatever. Learn some stuff about errors they are great. Optimistic approach to errors means expecting them. Plan for them. Do something better than showing unhelpful error.

Assume someone clicks a button, then puts their phone straight back into their pocket. And their pants are made of lead, blocking any signal.

3. What

Plan for the person that has been to site once before. Now they’re waiting at an airport, their flight has been delayed 45 minutes, and they want to read some more.

Step 1. offline.html Plain page to replace browser default. Bad news for Chrome users. No longer have a dinosaur game, or the Download When Online button. Arguably worse experience than browser default.

Step 2. blog.html So we want something better than static page, let’s give people some content. Fallback to “sorry, can’t get that. Would you like to view one of these?”

NB: on pre-emptive loading. Be nice to people’s data. TODO: is there a way to detect network type ala “wifi only download”? Assume small is ok, so you can download text content and maybe small images. Check what your market is ok with.

We can do better than this still. If we can grab text in the background, and we have a “people also enjoyed:” links. Use Analytics data, and then pre-emptively fetches content for those in idle times. So if you come to my blog and visit two pages, auto-download a couple more.

Also the idea of timebox/location-based “predictive” offline. If a user is going to leave the house and going to the station, sync some things that matter. (opt-in to download all)

This is a common pattern for News, Social Media, anything that is post-oriented is ok. In SW/Toolbox terms it’s called staleWhileRevalidate. But holy shit please do not use this for time-sensitive data. Especially not if you are printing relative times on screen. Looking at you PTV app.

Step 3. forms

Adds a lot of potential, and a lot of complexity

The traditional optimistic UI: like/star button works, but does nothing. Probably not a big deal The offline optimistic UI: updates live now, really later. Button queues a request, then actually updates later. Sounds simple… BUT:

  • should you notify the user?
    • if it succeeds, probably not.
    • If it fails?…
      • “Your like failed. Click here if you still like it”
      • toast next time they are in app.
      • (ab)use push notifications

Race conditions:

  • Like button clicked on Phone. Phone placed in faraday cage. Or lead pants
  • Like button clicked on PC. Change of mind, thing unliked.

So, so far so simple. Let’s try a more complex form.

You know what is worse than filling out forms? Submitting them when you’re offline and getting an error page. Because then you have to fill them out twice.

For any short fields, make autocomplete work. For comment fields, longer notes, save locally. And save lots. Discarded data is very annoying.

Caveat: don’t cache sensitive info. In either formdata or localstorage. Also applies to caching data that requires auth. If you need to be logged in, you can encrypt data locally and access with a token, or keep it in sessionStorage or something else. But be sure that the data doesn’t live longer than a session

Step 4. Collaborative Docs

This is where data storage method gets more important. You cannot just store “here’s the text” type values anymore. Think about changes, about conflicts.

NB: sending and handling notifications offline. What happens if someone clicks a notification while offline? It closes. Does your site work? Can they save it or get notified again when they can reach it. Can you pre-emptively fetch the page content. Should you? There are limits, don’t be a dick

Step 5. Chat / Games etc

Highly interactive, network-dependent. This requires a different experience for online vs offline. e.g. Bot that pops up “you’re offline, want me to send that for you later?” For games, you could drop into training mode. e.g. Chess – “Currently offline, analyze board”. Or “continue against computer”. Or training mode.

Scratchpad

Connection continuum: Offline – Flaky – Slow – Fast

Slow and fast are self explanatory. Flaky is an interesting case. What do you think would happen if every 4th request failed? That is something like what happens when you have congestion issues. Some requests will take ages, some will fail.

Quality continuum: someone else’s error « your error « some content « the actual content

Be Optimistic – Assume devices will reconnect at some point.

Australian internet is rather awful – e.g speedtest screenshot from home Also: https://twitter.com/SwiftOnSecurity/status/1008561176408707078

show that the default offline experience means abandoning people. You are trusting the browser to do something

requests are like sheep. Body can only be consumed once, so clone first


whatsapp is good for offline – message just gets sent. duplicates not a big deal; most people only use one device, so won’t accidentally send twice facebook failed file upload from phone Self-first sync (for chat). Confirm before auto sending. More hassle than benefit


Does offline last? Why was Guardian crossword shutdown? TODO. Maintenance concerns??

Updating cached data when your service worker updates note on updating SW version without losing your precious offline pages. You need em.

if you are offline, it should tell you as soon as possssible if your connection is slow, should offer them the chance to come back later

button for dinosaur game that uses chrome’s version alternatively just use regular js version


Polling

  • don’t retry non-repeatable actions e.g. if Post Comment starts polling

Offline sync this is the sync problem compounded. Because you are very likely to cause conflicts somewhere. if you do cause conflicts, cause them in the way that doesn’t again cause future conflicts. Let’s quickly look at something that does a terrible job of this: git. We are editing the following 3 line file. 3 lines, what could possibly happen.

Jane and Jim are editing a shared file. line 1 has some text added by both Now both see a conflicts view. This is designed to be resolved offline.

Choosing a sync model this is a tricky, tricky thing but pretty great experience. If we have comments, and I want to copy them to notepad or something, then back to the browser, I should be able to do that Rather than storing text, store actions/deltas. See redux, quilljs etc for things here.

Online vs Offline conflict resolution. Offline is actually easier, pushes the responsibility onto them. Online requires lock or something. There are tools to help with this (firebase or quilljs), but you want to have a conceptual understanding of what’s going on

Offline-only mode

  • You must be offline to continue page (offline-only page)
  • could have focus mode, and you add not distracted mode which requires you be disconnected

tip: I started this in dropbox note. simple and pretty reliable (NEVER want to lose) terrible sync support though. If it detects a conflict it just duplicates your files

Getting Buy In: if you need to convince people of business value.

  • tell them you can do push notifications. Marketing people love push notifcations. I think they are horrible. But that’s your in.
  • resumeable downloads. Probably handled by browser (since some chrome version) but for time limited auth some things might need to be done.

Devs vs users

  • avoid making too much distinction between users and devs. Hopefully you should be both. If not, please please start using your site more. Helps develop empathy.
  • having reload cache all the time can be good. But then you forget what its really like for users. Most people don’t force reload all the time. So make sure that you’re sure that your caches are being invalidated and people aren’t stuck with the new stuff forever

Offline Examples

  • Trivago maze
  • Guardian crossword
  • Ethan Marcotte site
  • Jeremy Keith site
  • Google Docs offline
  • Flipkart (disables almost everything)
  • Offline Wikipedia
  • Much of https://pwa.rocks/

Join the conversation!

Your email address will not be published. Required fields are marked *

No comment yet.