šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

HTML For PWAs: The Markup Foundation Beneath Installability

Master the HTML-level foundation of a Progressive Web App: the manifest link tag, display mode's effect on installed chrome, multi-size and maskable icons, and how service worker registration differs from every other PWA-related HTML tag.

⚔ Total XP: 0|šŸ’» html XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

HTML For PWAs

The installability foundation.


šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

A PWA's offline behavior is a JavaScript and service-worker concern, but whether it's installable at all, what it's called, what icon it shows, and how much browser chrome survives installation are all determined at the HTML and manifest level — the foundation this lesson covers.

2display Mode And Multi-Purpose Icons

The manifest's display field determines how much browser UI survives once the app is launched from its installed icon: "standalone" hides the browser's address bar and tab chrome while keeping the OS status bar, the classic native-app-like installed appearance; "fullscreen" goes further, also hiding the status bar; "minimal-ui" retains a small amount of browser navigation; and "browser" launches as an ordinary tab, unchanged from visiting the site directly.

Icons should be provided at multiple sizes (192Ɨ192 and 512Ɨ512 covering the common minimum), and specifically marking one with "purpose": "maskable" — an icon authored with extra safe-zone padding around its essential content — lets Android's adaptive-icon system safely crop it into a circle, squircle, or other device-theme-dependent shape without clipping anything important, alongside a standard, non-maskable icon for contexts that render icons unmasked.

{ "display": "standalone" }
"icons": [
  { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" },
  { "src": "/icon-maskable.png", "sizes": "512x512", "purpose": "maskable" }
]
localhost:3000
āœ“ Deliberate Control Over The Installed Lookdisplay mode and maskable icons together shape exactly how the installed app presents itself.

3Service Worker Registration Differs From Every Other PWA Tag

Unlike the manifest, there is no dedicated HTML tag for registering a service worker — it's always done imperatively in JavaScript, via navigator.serviceWorker.register('/sw.js'), typically inside a feature-detection check (if ("serviceWorker" in navigator)) and deferred until after the page's load event fires, so the registration doesn't compete with initial page rendering and resource loading for bandwidth and main-thread time.

Rounding out cross-platform consistency, apple-touch-icon (covered in the modern meta tags lesson) fills iOS's historical gap in sourcing its home-screen icon from the manifest, and theme_color/theme-color — settable both inside the manifest and as a <meta> tag — should generally be set in both places, since the meta tag governs browser-tab chrome while the manifest's value governs the installed, standalone-launch appearance.

if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => navigator.serviceWorker.register("/sw.js"));
}
localhost:3000
āœ“ The One PWA Piece With No Declarative HTML EquivalentService worker registration is always imperative JS, unlike the manifest's declarative link.

4Step-by-Step Breakdown

Installability Starts In <head>, Not In JavaScript. A Progressive Web App's offline behavior lives in a service worker, but whether it's installable, what icon it shows, and how it looks once launched from a home screen are all determined by a JSON manifest and a handful of <head> tags — the HTML-level foundation this lesson covers.

The Web App Manifest Is What Makes A Site Installable. A manifest.webmanifest JSON file, linked via <link rel="manifest">, declares the app's name, icons, start_url, and display mode — its presence (alongside a registered service worker and HTTPS) is what triggers a browser's install prompt and defines how the app behaves once installed.

The Web App Manifest's Role. What links a page's HTML to its web app manifest?

  • →<link rel="manifest" href="..."> in the document head
  • →A <meta name="manifest"> tag
  • →Browsers detect it automatically with no link required

display Mode Controls How Much Browser Chrome Survives Installation. The manifest's display field — "standalone" (no browser UI, looks like a native app), "fullscreen" (also hides the OS status bar), "minimal-ui" (a few browser controls remain), or "browser" (a normal tab) — determines exactly how much of the browser's own UI remains visible once the app is launched from its installed icon.

The display Manifest Field. Which display value gives an installed PWA the most native-app-like appearance, with no browser address bar or tab UI visible?

  • →"standalone"
  • →"browser"
  • →"minimal-ui", since it removes the most browser chrome

Multiple Icon Sizes And Purposes Across Platforms. The manifest's icons array should include multiple sizes (commonly including 192x192 and 512x512 at minimum) and can mark specific icons with purpose: "maskable" — a maskable icon has extra padding so Android's adaptive-icon system can safely crop it into a circle, squircle, or other shape without clipping important content.

Maskable Icons. What problem does a maskable icon (purpose: "maskable") solve?

  • →It prevents important icon content from being clipped when an OS's adaptive-icon system crops it into a different shape
  • →It reduces the icon file's size on disk
  • →It automatically increases the icon's resolution

The Service Worker Is Registered From A <script>, Not Declared In HTML. Unlike the manifest, there's no dedicated HTML tag for service worker registration — it's registered imperatively via navigator.serviceWorker.register('/sw.js') in a regular <script>, typically gated behind a feature-detection check and deferred until after the page's load event to avoid competing with initial page rendering for resources.

Service Worker Registration. Is there a dedicated HTML tag or attribute for registering a service worker, the way <link rel="manifest"> registers a manifest?

  • →No — service worker registration is done imperatively via JavaScript, not declared in HTML
  • →Yes, a <link rel="serviceworker"> tag registers it declaratively
  • →Yes, via a dedicated <meta name="serviceworker"> tag

apple-touch-icon And theme-color Round Out Cross-Platform Support. Alongside the manifest, apple-touch-icon (covered in the modern meta tags lesson) fills iOS's historical gap in reading icons from the manifest directly, and theme-color — settable both as a <meta> tag and inside the manifest itself — keeps the installed app's system UI color consistent whether launched from a browser tab or its home-screen icon.

Cross-Platform PWA Consistency. Why does a well-configured PWA typically set theme_color in BOTH the manifest and as a <meta name="theme-color"> tag?

  • →The meta tag covers the browser-tab context; the manifest's value covers the installed, standalone-launch context
  • →There's no real reason; one of the two is always redundant
  • →The manifest version has fully deprecated the meta tag

HTML For PWAs Mastered. You now know how the web app manifest connects to your HTML, how display mode and maskable icons control the installed experience, and that service worker registration — unlike the manifest — is imperative JavaScript with no dedicated HTML tag.

Link The Web App Manifest. A PWA's installability starts with linking its manifest.json.

Level Up šŸš€

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1An Installed PWA's Accessibility Depends Entirely On The Same Underlying HTML Semantics Covered Throughout This Course

Installability doesn't introduce any separate accessibility model — a PWA is exactly as accessible as its underlying HTML, semantic structure, and ARIA usage, whether viewed in a browser tab or launched standalone from a home screen icon.

SEO Implications

  • 1

    A Well-Configured Manifest And Installability Are Not Themselves Direct Search-Ranking Factors, But Correlate With Overall Site Quality Signals

    Core Web Vitals and general page-experience quality (both covered elsewhere in this course) remain the actual ranking-relevant factors; PWA installability is a genuine user-experience and retention feature independent of any direct SEO effect.

Best Practices

Always Provide Both A Standard And A Maskable Icon, Not Maskable Alone

Contexts that render icons unmasked (some browser install UI, certain platforms) need a standard icon without the extra maskable safe-zone padding to look correctly proportioned.

Defer Service Worker Registration Until After The load Event

Registering immediately competes with initial page rendering and critical resource loading — deferring it avoids that contention with no meaningful downside, since offline capability isn't typically needed in the first few moments of a page visit.

Frequent Bugs

THE BUG

A PWA's install prompt never appears despite having a seemingly correct manifest.webmanifest file.

THE FIX

Verify the manifest is actually linked via <link rel="manifest">, the page is served over HTTPS, and a service worker is genuinely registered — all three are typically required together.

THE BUG

An app's icon appears with important content clipped off on Android devices using certain icon shape themes.

THE FIX

Add a dedicated maskable icon (purpose: "maskable") with sufficient safe-zone padding, alongside the existing standard icon.

Real-World Examples

A Complete Minimal PWA HTML/Manifest Setup

The full set of HTML-level and manifest pieces needed for a basic installable PWA.

<!-- index.html -->
<link rel="manifest" href="/manifest.webmanifest">
<link rel="apple-touch-icon" href="/icons/apple-touch-icon-180.png">
<meta name="theme-color" content="#0f172a">
<script>
if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => navigator.serviceWorker.register("/sw.js"));
}
</script>

// manifest.webmanifest
{
  "name": "Acme Notes",
  "start_url": "/",
  "display": "standalone",
  "theme_color": "#0f172a",
  "icons": [
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" },
    { "src": "/icon-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Providing only a maskable icon, with no standard non-maskable icon

"icons": [ { "src": "/icon-512.png", "sizes": "512x512" }, { "src": "/icon-maskable.png", "sizes": "512x512", "purpose": "maskable" } ]

The Solution //

Include both — some contexts render icons unmasked and need a standard icon without maskable safe-zone padding.

The Error //

Registering a service worker immediately on script execution, before the load event

window.addEventListener("load", () => navigator.serviceWorker.register("/sw.js"));

The Solution //

Defer registration until after window's load event fires, avoiding contention with initial page rendering.

Lesson Glossary

[01]Web App Manifest

A JSON file declaring a PWA's name, icons, and display mode.

Code Preview
<link rel="manifest" href="...">

[02]display mode

Controls how much browser chrome survives installation.

Code Preview
"standalone" | "fullscreen" | "minimal-ui" | "browser"

[03]Maskable Icon

An icon with safe-zone padding for adaptive-icon cropping.

Code Preview
"purpose": "maskable"

[04]Service Worker Registration

Imperative JS enabling offline capability; no HTML tag exists for it.

Code Preview
navigator.serviceWorker.register("/sw.js")

Continue Learning