Skip to content

UX and Design for Progressive Web Apps

PWA design is the work of making a web app behave like installed software once it leaves the browser tab: it launches without a flash, responds to touch instantly, fits around notches and keyboards, navigates without a browser back button, follows the system's light or dark appearance, and stays usable for everyone, including people who zoom, use screen readers or switch off animation. Installation removes the browser's interface, and with it a set of features users took for granted, so design decisions that are optional for a website become requirements for an app. This section covers those decisions from principles down to CSS properties, platform quirks and production code.

Key takeaways

  • In a standalone window you inherit the browser's responsibilities: back navigation, reload, sharing, showing where the user is, and signaling when a link leaves the app.
  • App-like UX is built from platform primitives (<dialog>, inputmode, autocomplete, color-scheme, env(safe-area-inset-*), dynamic viewport units, View Transitions), not from frameworks that reimplement native behavior.
  • The launch experience spans the manifest (background_color, theme_color, icons), Apple's startup images and your app shell's first paint. All of them must agree on color, in light and dark mode.
  • Never trade accessibility for an app look: keep pinch zoom, respect text size and reduced motion, size touch targets to at least 24×24 CSS px (44–48 px preferred), and keep focus visible.
  • Design for three families of platform conventions (iOS/iPadOS, Android, desktop) and detect the display mode to add app chrome only where the browser's is missing.
  • Every pattern must still work as a website in a browser tab. Installation is an enhancement.

Principles of app-like web UX

Users don't judge an installed PWA against other websites. They judge it against the native apps next to it on their home screen, dock or taskbar. The principles below are what that comparison rewards. Each links to the page that turns it into code.

1. Respond to every input within a frame

Native apps acknowledge a tap before they've done anything with it: a pressed state, a ripple, a highlight. Web pages often wait for a network response before anything changes. The rule for app-like UX is that visual feedback is immediate and independent of the work behind it:

  • Draw a pressed state on :active for every control, and remove the double-tap-zoom delay with touch-action: manipulation where it matters.
  • Update the UI optimistically for actions that almost always succeed (marking as read, starring, reordering) and reconcile afterwards. Offline UX & Fallbacks covers optimistic UI and its failure handling.
  • Keep the main thread free so input is processed quickly; Interaction to Next Paint (INP) measures exactly this. See Runtime Performance and Core Web Vitals.

2. Launch instantly and never flash

The first second after tapping the icon defines whether an app "feels native". Three layers contribute: the platform's launch surface (Android's generated splash screen, iOS startup images), the browser's initial canvas, and your first paint. A cached app shell makes the first paint fast; matching colors across all three layers makes the transition invisible. Splash Screens & Theming covers every platform's launch surface and the checklist for avoiding white flashes, including dark mode.

3. Fill the screen, and respect what's on it

Apps draw edge to edge: under translucent status bars, around camera cutouts, above home indicators and gesture bars, and around the on-screen keyboard. The web has precise tools for each (viewport-fit=cover, env(safe-area-inset-*), svh/lvh/dvh, interactive-widget, the visualViewport API and Chromium's VirtualKeyboard API), and each has per-browser behavior you need to know. App-Like UX Patterns covers them in depth.

4. Follow platform conventions instead of inventing new ones

Users carry muscle memory from their platform: where the back button is, how tabs work, what a long press does, what happens when they swipe down. A PWA that behaves like its host platform needs less explanation than a consistent-everywhere design that behaves like none of them. You don't need separate designs per platform, but you do need to honor a few platform-specific expectations (see Platform conventions below) and adapt layout to the device class (Responsive & Adaptive Design).

5. Keep the user in control

Some "app-like" techniques take control away from users: disabling zoom, blocking text selection everywhere, hijacking the back gesture, auto-playing motion, forcing a light theme. Native platforms don't allow apps to do most of these, and the web shouldn't either:

  • Never set user-scalable=no or maximum-scale=1.
  • Disable selection only on UI chrome, never on content.
  • Let back close the topmost overlay, then navigate, then leave the app, in that order.
  • Honor prefers-reduced-motion, prefers-color-scheme and the user's text size.

6. Be honest about state

An installed app is expected to work on a train, in a basement and on a flaky conference Wi-Fi. That doesn't mean everything must work offline, but it does mean the UI must say what's happening: cached content with its age, queued actions with their status, errors with a way to retry. Skeleton screens show progress for data that's genuinely on its way; stale-while-revalidate content with a "last updated" note is often better than any loading state. Offline UX & Fallbacks and Offline-First Data & Sync cover this in detail.

7. Make motion meaningful

Transitions between screens tell users where they came from and where they are. The View Transitions API gives the web the shared-element and slide transitions native apps use, for single-page apps and, in supporting browsers, across full page navigations. Motion should explain spatial relationships and be brief, and it must switch off under prefers-reduced-motion. View Transitions covers the API and patterns.

8. Accessibility is part of "native quality"

Native UI toolkits give apps accessibility semantics, focus management and dynamic text sizing mostly for free. On the web you get them from semantic HTML and lose them the moment you build custom widgets from <div>s. An app that can't be used with a screen reader, a keyboard or at 200% zoom is not app-like, whatever it looks like. Accessibility covers the PWA-specific concerns: custom title bars, standalone windows without browser UI, gestures, focus after client-side navigation, and announcements for offline and sync state.

How standalone mode changes the design problem

The display mode decides how much browser interface surrounds your app. In browser mode, your PWA is a website in a tab and the browser provides navigation, the URL, sharing and reload. In standalone (the mode almost every PWA requests), all of that disappears:

Browser feature In a tab Standalone on Android Standalone on iOS/iPadOS Standalone on desktop
Back navigation Back button, gestures System back gesture/button None Keyboard shortcut only
Reload Button, pull-to-refresh Pull-to-refresh (Chrome) None Keyboard shortcut only
Current URL Address bar None None Copy URL in the app menu
Share current page Browser menu None None Varies by browser version
Page title Tab label Recents entry App switcher shows the app name Window title
Security indicator Padlock, URL None None App menu, site info
Leaving your site Obvious from the URL Out-of-scope pages open in a custom tab Out-of-scope pages open in an in-app Safari view Out-of-scope pages show a URL bar or open in the browser

The design consequences follow directly from the table:

  • Add a back affordance on every non-root screen where the platform lacks one, and make modals and sheets close on Android's back gesture.
  • Offer refresh wherever content can go stale, and connect it to your service worker update flow (Updating Service Workers).
  • Offer share and copy-link actions, because the address bar is gone (Web Share API).
  • Make titles meaningful, because document.title is what the window and task switcher show.
  • Mark external links, because nothing else tells users they're leaving the app.

Because the same code also runs in a browser tab, you detect the display mode and add app chrome only where it's missing:

flowchart TD
    A["Page loads"] --> B{"display-mode: standalone,<br/>fullscreen or WCO?<br/>or navigator.standalone"}
    B -- "no (browser tab)" --> C["Browser provides back, reload, URL, share<br/>Show install promotion where appropriate"]
    B -- "yes (installed app)" --> D{"Platform provides back?"}
    D -- "Android" --> E["Rely on system back<br/>Make overlays history-aware"]
    D -- "iOS, desktop" --> F["Show in-app back button<br/>when there is history"]
    E --> G["Add refresh, share, external-link hints<br/>Hide install promotion"]
    F --> G
standalone.css
/* App-only chrome: hidden in browser tabs, shown in app windows. */
.app-only { display: none; }

@media (display-mode: standalone), (display-mode: fullscreen),
  (display-mode: window-controls-overlay) {
  .app-only { display: revert; }
  .install-promo { display: none; }
}

The display-mode media feature is supported in Chrome 42, Firefox 47 and Safari 13 (iOS 12.2). Safari also exposes the non-standard navigator.standalone (on iOS and iPadOS, and on macOS since Safari 17, where it is true in Dock web apps). Check it before the media query on Apple platforms: an iOS web app whose manifest says standalone matches display-mode: fullscreen, and one without a manifest reports browser. Display Modes has a complete detection module, and Detecting Installed Apps covers the related question of whether a user has installed your app at all.

Platform conventions you design against

A PWA runs on platforms with different, well-established conventions. Designing "for the web" means designing so each of them feels at home:

Convention iOS and iPadOS Android Desktop (Windows, macOS, Linux, ChromeOS)
Primary navigation Tab bar at the bottom Navigation bar at the bottom, or a rail/drawer Sidebar, top tabs, menus
Back In-app back button, top left System back gesture or button In-app back button, keyboard shortcuts
Title and status area Status bar always visible; your content may run under it Status bar tinted with the theme color Title bar tinted with the theme color, or a custom one with Window Controls Overlay
Minimum touch target 44 × 44 pt (Apple HIG) 48 × 48 dp (Material Design) Pointer precision; keyboard access matters more
UI font San Francisco (system-ui) Roboto or the device maker's font (system-ui) Segoe UI, San Francisco, Cantarell and others (system-ui)
Launch surface Startup image if provided Generated splash screen None
Dismissing overlays Swipe down on sheets, close buttons Back gesture, scrim tap Esc, click outside
Refreshing Pull to refresh in lists Pull to refresh in lists Toolbar button, keyboard shortcut

A single responsive design can satisfy most of this: a bottom navigation bar that becomes a rail on wide screens, a top app bar with a back button that's hidden where the platform provides back, system-ui for text, and touch targets sized for fingers when (pointer: coarse) matches. Platform details beyond design, such as installation flows and API availability, live in Platform Support.

A design checklist for installed PWAs

Use this as a review checklist before shipping. Each item links to where it's explained.

Launch and theming

  • background_color matches the app shell's first frame (Splash Screens & Theming)
  • theme_color in the manifest, plus light and dark <meta name="theme-color" media> values
  • <meta name="color-scheme" content="light dark"> and an inline html background, so no white flash in dark mode
  • iOS startup images generated for current devices, or a deliberate decision to go without
  • A 512 × 512 maskable icon for Android's splash screen and launcher (Icons & Maskable Icons)

Layout

  • width=device-width, initial-scale=1, viewport-fit=cover, and no zoom restrictions (App-Like UX Patterns)
  • Fixed headers and tab bars padded with env(safe-area-inset-*), checked in landscape
  • Full-height layouts use dvh/svh with vh fallbacks
  • The composer, toolbar or submit button stays reachable when the on-screen keyboard is open
  • Layout adapts from phone to tablet to desktop window, including narrow desktop windows (Responsive & Adaptive Design)

Interaction

  • Touch targets at least 24 × 24 CSS px, ideally 44–48 px
  • Visible :active and :focus-visible states on every control
  • Hover-only affordances wrapped in @media (hover: hover) and never the only way to reach a control
  • Scroll containers use overscroll-behavior: contain; any disabled pull-to-refresh is replaced by a refresh control
  • Inputs use the right type, inputmode, enterkeyhint and autocomplete, with 16 px text on iOS

Navigation

  • Back affordance on non-root screens in standalone mode on iOS and desktop
  • Dialogs, sheets and drawers close on Android back and Esc
  • Deep links work on cold launch, with a way back to the app's root
  • External links are marked and open outside the app window
  • Screen changes announce themselves and move focus appropriately (Accessibility)

Resilience and motion

  • Loading states only after a short delay; cached content shown with its age
  • Offline and queued states visible and understandable (Offline UX & Fallbacks)
  • Screen transitions respect prefers-reduced-motion (View Transitions)
  • Everything above verified in a browser tab too, not only in the installed app

Pages in this section

  • App-Like UX Patterns


    Bottom navigation and history-aware modals, touch targets and touch-action, press states, overscroll and pull-to-refresh, safe areas, dvh/svh/lvh, the on-screen keyboard (interactive-widget, visualViewport, VirtualKeyboard API), inputmode and autocomplete, selective user-select, system fonts, skeletons, dark mode and standalone-only CSS.

    App-Like UX Patterns

  • Splash Screens & Theming


    Android's generated splash screen and how Chromium picks its icon, iOS apple-touch-startup-image with per-device media queries and generators, desktop launch behavior, theme_color versus <meta name="theme-color">, dark-mode theme colors, runtime theme changes, iOS status bar styles and avoiding launch flashes.

    Splash Screens & Theming

  • View Transitions


    Animated transitions between screens with the View Transitions API, for single-page apps and cross-document navigations: shared elements, direction-aware animations, performance and reduced motion.

    View Transitions

  • Responsive & Adaptive Design


    Layouts that work from phones to desktop windows: breakpoints versus container queries, adaptive navigation, input-aware design with pointer and hover media features, foldables and resizable app windows.

    Responsive & Adaptive Design

  • Accessibility


    Accessible installed apps: semantics and focus in client-side navigation, standalone windows without browser UI, custom title bars, gestures and target sizes, zoom and text scaling, motion, and announcing offline and sync states.

    Accessibility

  • Display Modes


    standalone, minimal-ui, fullscreen, window-controls-overlay and tabbed, the display-mode media feature and how each platform renders them.

    Display Modes

  • Window Controls Overlay


    Custom title bars for desktop PWAs, draggable regions and the title bar geometry API.

    Window Controls Overlay

  • App Shell Model


    The cached UI skeleton that makes launches and navigations instant.

    App Shell Model

  • Offline UX & Fallbacks


    Connectivity states, offline pages, optimistic UI, queued actions and freshness indicators.

    Offline UX & Fallbacks

Suggested reading order

If you're turning an existing site into an installable app, read the pages in the order the problems appear:

  1. App-Like UX Patterns first. The viewport, safe-area, navigation and keyboard fixes are the most visible difference between "website in a window" and "app".
  2. Splash Screens & Theming next, because launch is the first thing users see after installing, and the manifest colors you choose are hard to change for existing installs.
  3. Responsive & Adaptive Design when your app runs on tablets and in resizable desktop windows, where a phone layout stretched wide looks unfinished.
  4. View Transitions once navigation works, to add motion that explains it.
  5. Accessibility throughout. It's listed last only because it touches every other page; review it before each release.

For the broader rollout (installation prompts, service worker, offline support), follow Migrating an Existing Site, and use the Production Checklist before launch.

What this section doesn't cover

To keep each topic in one place, some design-adjacent subjects live elsewhere:

Further reading

On this site

External references