Offline Ethics
Living in Tacoma and working in Seattle meant sitting on the train a lot. There is a strange pendulum swing to that commute. Some days it gives you distraction-free time. Some days it feels like being locked in a metal box moving 60 or 70 miles per hour according to a schedule you do not control.
The Sounder train offered Wi-Fi, at least in theory. When the train was nearly empty, it worked. When the train was at normal capacity, it was a notch below coffee shop quality. When it was standing room only, it might as well not exist.
That is understandable. It is also exactly the kind of everyday condition software tends to treat as exceptional.
Most websites and web applications are bad at responding to a degraded or missing connection. They fail silently, disable important controls, lose user input, or present the browser’s default failure state as if the product had no responsibility for what happened. But browsers have APIs that can tell us about connection state. We can observe failure. We can preserve work. We can communicate honestly.
At a bare minimum, a site or application should be able to work offline, even if the definition of “work” has to narrow for the user.
The Ethical Obligation
If someone gives an application their time, attention, labor, or money, the application owes them more than a best-effort happy path.
The abstract I wrote at the time had a useful way into the problem: sometimes a loading spinner is not actually loading anything. The interface is waiting, hoping, or masking uncertainty. The ethical question is whether the product tells the user the truth about that uncertainty, or whether it lets them believe progress is happening when the application knows it has lost the conditions required to make progress.
That does not mean every product needs a perfect offline mode. It means the product should not pretend the network is guaranteed. It should not turn ordinary conditions into user failure. It should not let someone type, compose, decide, or navigate and then throw away that work because the connection changed.
That distinction matters because “offline support” can sound like a huge platform project. In practice, the talk was trying to argue for something smaller and more enforceable: establish a product standard for lost connectivity, then improve the experience one interaction at a time.
The obligation can be stripped down to three principles:
- Users should know they have gone offline.
- Do not prevent the work; batch it and dispose of it when it is safe.
- Picking up where the user left off is paramount.
Before getting practical, it is worth naming the premise: there is not one single thing we can safely assume about the user’s situation. Not their attention span. Not their device. Not the chair they may or may not be sitting in. Not their time. Not their physical ability. Not their battery. Not their orientation. Not their location. Not their network.
Responsive design was never only about screen width. The stronger promise was that the user should be able to access the thing they need without the interface blocking their goal. The device can be in any state. The network can be in any state. The interface should still behave with some dignity.
Let People Know What Happened
Devices lose connection to the Internet constantly. Bills come due. Routers fail. Networks overload. Captive portals interrupt. Trains pass through dead zones. Phones get confused.
If a user tries to refresh, submit, or navigate while that is happening, they may not know whether they made a mistake, the product broke, or the network disappeared. The interface should tell them.
This is not only a mobile concern. A laptop on a commuter train is just as vulnerable as a phone on the sidewalk. A desktop browser can lose Wi-Fi. A workplace network can collapse. A hotel connection can lie.
The simplest technical version is not complicated:
- Listen for online and offline changes.
- Represent that state in your application.
- Show the user what changed.
- Avoid making irreversible UI decisions based on a temporary network failure.
React is a decent fit for this because connection state can be modeled like any other application state. A top-level listener can observe changes, and context can expose the current status to the rest of the tree.
const NetworkContext = React.createContext({ online: true });
function NetworkProvider({ children }) {
const [online, setOnline] = React.useState(navigator.onLine);
React.useEffect(() => {
const handleOnline = () => setOnline(true);
const handleOffline = () => setOnline(false);
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
};
}, []);
return <NetworkContext.Provider value={{ online }}>{children}</NetworkContext.Provider>;
}
The point is not the snippet. The point is making network state visible to the product instead of treating it as an invisible environmental accident.
Do Not Disable the Workflow
When the user loses connection, the laziest interface response is to disable everything. That is often the wrong instinct.
If someone is composing a message, filling out a note, editing a draft, or making a choice, the product should try to preserve that work. If it cannot send a POST or PUT request now, it can hold the intended action locally and offer to retry when the network returns. If a draft exists in local storage, show it. If a send failed, say so and make the retry path obvious.
The principle is: do not prevent; batch and dispose.
Batch the work that cannot be completed yet. Dispose of the local copy when the server confirms it has been accepted. Keep the user informed the whole time.
This is not about pretending offline work is free. It creates product and engineering questions:
- What can be safely stored locally?
- What should never be stored locally?
- What needs conflict resolution?
- What should expire?
- What needs an explicit retry?
- What can be retried automatically?
Those are real questions, but they are better questions than “why did the user’s work vanish?”
Pick Up Where They Left Off
The most humane offline behavior is continuity. If someone returns to the application after losing connection, refreshing, or closing a tab, they should not feel like the product has amnesia.
That might mean caching an application shell. It might mean saving a draft. It might mean remembering a route. It might mean showing a partial view with a clear message that some parts could not be refreshed.
The important distinction is between unavailable and destroyed. If the latest discussion cannot be fetched, say that. If the existing content can still be shown, keep showing it. If an action is queued, show its status. If the user needs to intervene, tell them exactly what is waiting.
Good offline behavior is not a novelty mode. It is a way of preserving context.
Examples Worth Studying
Messaging applications often understand this better than content sites. A good messenger lets you type while offline, attempts to send when it can, marks the message as failed when it cannot, and lets you retry. The user never has to wonder whether the message disappeared into the void.
Spectrum, at the time, handled parts of this well. It could load the application shell, show some previously available content, and make the failure to fetch a discussion explicit. That is not the same as full offline support, but it is a meaningful contract: the product can say what it knows and what it cannot currently do.
The bar is not perfection. The bar is honesty plus preservation.
Small Wins Beat PWA Sermons
One thing the abstract got right was the adoption problem. Talking about progressive web apps often turns into a broad checklist: service workers, manifests, Lighthouse scores, caching strategies, installability, push notifications, and a dozen other concerns. That can be useful, but it can also make the whole topic feel too large to start.
Offline ethics is a better wedge because it starts from the user’s interrupted workflow. Can they keep typing? Can they recover a draft? Can they see that the connection dropped? Can the product retry a queued action? Can stale data remain visible without pretending it is fresh?
Those are smaller product decisions. They are also easier to advocate for inside a team because they are attached to specific moments where the product currently breaks trust.
React’s Role
React does not make offline behavior ethical on its own. It does make the state model approachable.
You can use context to expose network status. You can use listener components to observe browser events. You can use local state or a reducer to represent queued work. You can build UI that distinguishes “saved locally”, “syncing”, “failed”, and “sent” instead of flattening everything into disabled buttons and vague errors.
The component model is useful because offline behavior is not one screen. It is a cross-cutting condition. Forms, navigation, data views, messaging, and account flows may all need to know something about network state.
The Better Default
The web is powerful because it is everywhere. That also means it runs everywhere: on great networks, bad networks, shared networks, overloaded networks, and no network at all.
Offline behavior is not only a technical feature. It is a statement about whether the product respects the user’s work when conditions stop being ideal.
Tell people what happened. Preserve what they were doing. Let them pick up where they left off.