UX patterns for software that must function under low battery, weak connectivity, and resource constraints. A practical guide to graceful degradation.
Graceful degradation is the discipline of ensuring that a system fails in the direction of less functionality rather than no functionality. Under Protective Computing, it is a design requirement, not a stretch goal.
This page catalogs the patterns that make degraded-mode operation practical, and the anti-patterns that make systems brittle under constraint. It is paired with the normative Degraded-Infrastructure Functionality principle.
Before launch, verify that your critical path survives each of these conditions:
Write operations are accepted immediately into a local queue and confirmed to the user at once. Sync to the server happens asynchronously when connectivity is available. The user is never blocked waiting for a network acknowledgement.
When to use: Any operation that the user needs confirmed immediately — form submissions, record creation, updates.
Serve cached data immediately while fetching fresh data in the background. The user sees content without waiting for a network round-trip. When fresh data arrives, update quietly if possible, or notify the user of changes if the difference matters.
When to use: Read-heavy flows where slightly stale data is acceptable — dashboards, history views, reference content.
Load the minimum assets required for core functionality first. Load images, fonts, and enhancement scripts only after core content is interactive. Use loading="lazy" for images not in the initial viewport. Serve low-resolution image placeholders on constrained connections.
When to use: All pages with non-critical assets.
Displaying a full-screen or flow-blocking spinner while waiting for a server response assumes the network will respond quickly. On degraded connections, the spinner blocks the user indefinitely. There is no affordance to retry, cancel, or proceed offline.
Replace with: Show partial content immediately, indicate sync status non-blockingly (e.g., a small status indicator in the corner), and allow the user to proceed with locally available data.
Heavy sync operations (full data refresh, media upload, large downloads) are scheduled only when the device is on Wi-Fi and charging. On battery with cellular, only essential sync occurs. Users can override this for urgent cases.
When to use: Apps that sync media, large datasets, or bulk records.
Detect low battery (under 15%) and automatically switch to a stripped-down UI: animations off, background processes suspended, non-critical content hidden. The user sees a clear indication that the app is in essential mode and can exit it manually.
When to use: Apps where the user might be using the device in an emergency.
Polling a server every 30 seconds for updates, maintaining WebSocket connections, or keeping GPS active in the background — these drain battery and mobile data. On low-resource devices, they compete with core functionality and can cause crashes.
Replace with: Push notifications for time-sensitive updates; user-triggered refresh for everything else. Use platform-appropriate background fetch APIs with strict rate limits.
Each screen in the critical path presents exactly one decision or action. Navigation, secondary information, and optional features are hidden or collapsed. The user knows what to do next without reading the whole screen.
When to use: Forms, submission flows, emergency actions.
Multi-step flows show a clear, persistent indicator of how many steps remain. The user can see where they are in the process and what completing it requires. Partial progress is saved so a crash or interruption does not require restarting.
When to use: Any multi-step form or onboarding flow where interruption is likely.
Actions that cannot be undone require explicit, plain-language confirmation that describes exactly what will be lost. The confirmation is a deliberate step, not a dismissable dialog.
When to use: Deletion, account termination, data export, and any action that changes data in ways the user cannot recover from.
Push notifications, upsell prompts, review requests, or "while you're here" banners injected into a critical flow introduce cognitive load at the worst possible moment. A user trying to submit evidence, complete a medical form, or exit a coercive situation does not need a rating prompt.
Replace with: Reserve all engagement mechanics for sessions where the user is not in the middle of a critical task. Detect active critical paths and suppress all non-essential prompts during them.
Build each critical flow so it works as plain HTML with no JavaScript. Add JavaScript as an enhancement layer that improves — but does not gate — functionality. This ensures the flow survives JavaScript failures, overly aggressive ad-blockers, and corporate content filtering.
Use system font stacks rather than requiring web font loads for all text on the critical path. Web fonts are a network dependency and a render-blocking resource. The visual difference is minor; the degraded-mode benefit is significant.
Using CSS transitions or JavaScript animations to make state changes visible — and then reading the post-animation DOM state — creates dependencies on animation completion. On slow devices, animations drop frames or skip, leaving the UI in an inconsistent state.
Replace with: Separate state management from visual presentation. State changes should be immediate and correct; animations should be cosmetic overlays that are safe to skip.
No degraded-mode design is verified until it has been tested under actual degraded conditions:
Protective Computing — back to home