DEV Community

tinycoder-studio
tinycoder-studio

Posted on

Vanilla JavaScript PWA Tutorial: Build With Zero Dependencies

Every PWA tutorial starts with "install React, then Next.js, then configure Webpack..." By the time you're done, you have 200MB of node_modules for a to-do app.

This tutorial uses zero dependencies. Just HTML, CSS, and JavaScript. You'll have a working PWA in 10 minutes.

What We're Building

A simple web app that:

  • Works offline
  • Installs to the home screen
  • Has a service worker for caching
  • Runs on any static host (Vercel, Netlify, GitHub Pages)

Step 1: The HTML

Create index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My PWA</title>
  <link rel="manifest" href="/manifest.json">
  <meta name="theme-color" content="#0F172A">
  <link rel="apple-touch-icon" href="/icon-192.png">
</head>
<body>
  <h1>Hello, PWA!</h1>
  <p>This app works offline.</p>
  <script src="/app.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Step 2: The Manifest

Create manifest.json:

{
  "name": "My PWA",
  "short_name": "PWA",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#0F172A",
  "theme_color": "#06B6D4",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Step 3: The Service Worker

Create sw.js:

const CACHE = 'my-pwa-v1';
const ASSETS = ['/', '/index.html', '/app.js', '/style.css'];

self.addEventListener('install', e => {
  e.waitUntil(
    caches.open(CACHE).then(cache => cache.addAll(ASSETS))
  );
});

self.addEventListener('activate', e => {
  e.waitUntil(
    caches.keys().then(keys =>
      Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))
    )
  );
});

self.addEventListener('fetch', e => {
  e.respondWith(
    caches.match(e.request).then(r => r || fetch(e.request))
  );
});
Enter fullscreen mode Exit fullscreen mode

The service worker does three things:

  • Install — caches all app files
  • Activate — deletes old caches
  • Fetch — serves from cache, falls back to network

Step 4: Register the Service Worker

Create app.js:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(reg => console.log('SW registered:', reg.scope))
    .catch(err => console.log('SW failed:', err));
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Deploy

If you're using Vercel:

npm i -g vercel
vercel deploy --prod
Enter fullscreen mode Exit fullscreen mode

That's it. Your PWA is live.

Testing Offline

  1. Open Chrome DevTools → Application → Service Workers
  2. Check "Offline" in the Network tab
  3. Refresh the page — it should still load
  4. On Android: three-dot menu → "Add to Home Screen"

Why Vanilla JS?

  • Zero dependencies — nothing to update, nothing to break
  • Fast — no build step, no transpilation
  • Small — the entire app is under 10KB gzipped
  • Understandable — you can read every line

React and Next.js are great for complex apps. But for a simple PWA? Vanilla JS is enough.

Want a head start?

TinyCoder is a vanilla JS PWA with 5 offline dev tools. Config-driven, deployable in minutes.


Originally published at toolbox-lilac-three.vercel.app/blog/vanilla-js-pwa-tutorial.html

Top comments (0)