DeveloperBreeze

Introduction

Progressive Web Apps (PWAs) are web applications that leverage modern web technologies to deliver an app-like experience to users. They combine the best of web and mobile apps, offering features such as offline access, push notifications, and device hardware access. In this tutorial, we will explore how to build a PWA using modern web APIs, including Service Workers, Web App Manifest, and IndexedDB.

Prerequisites

To follow along with this tutorial, you should have a basic understanding of HTML, CSS, and JavaScript. Familiarity with modern JavaScript (ES6+) is also helpful.

What Makes an App Progressive?

Progressive Web Apps are characterized by the following features:

  • Responsive: They adapt to different screen sizes and orientations.
  • Connectivity Independent: They work offline or on low-quality networks.
  • App-like: They provide an app-like experience with smooth interactions.
  • Fresh: They are always up-to-date thanks to background updates.
  • Safe: They are served via HTTPS to ensure security.
  • Discoverable: They can be found through search engines.
  • Re-engageable: They support push notifications and can be installed on the home screen.
  • Linkable: They can be easily shared via URLs.

Setting Up Your Project

Let's start by setting up a basic project structure for our PWA.

1. Create Project Directory

Create a new directory for your project and navigate into it:

mkdir my-pwa
cd my-pwa

2. Initialize the Project

Create the basic file structure:

touch index.html styles.css app.js manifest.json
mkdir images

3. Add Basic HTML

Open index.html and add the following basic structure:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="styles.css">
  <link rel="manifest" href="manifest.json">
  <title>My Progressive Web App</title>
</head>
<body>
  <header>
    <h1>Welcome to My PWA</h1>
  </header>
  <main>
    <p>This is a simple Progressive Web App.</p>
    <button id="notify-btn">Enable Notifications</button>
  </main>
  <script src="app.js"></script>
</body>
</html>

Creating a Web App Manifest

The Web App Manifest is a JSON file that provides metadata about your PWA, allowing it to be installed on a user's home screen.

Configure the Manifest

Open manifest.json and add the following content:

{
  "name": "My Progressive Web App",
  "short_name": "MyPWA",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#4a90e2",
  "icons": [
    {
      "src": "images/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "images/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Ensure you have the icon images in the specified sizes in the images directory.

Registering a Service Worker

Service Workers are scripts that run in the background, separate from the web page, enabling features like offline access and push notifications.

1. Create a Service Worker File

touch service-worker.js

2. Register the Service Worker

In app.js, register the service worker:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
      .then(registration => {
        console.log('Service Worker registered with scope:', registration.scope);
      })
      .catch(error => {
        console.error('Service Worker registration failed:', error);
      });
  });
}

3. Set Up the Service Worker

Open service-worker.js and add the following code to cache static assets:

const CACHE_NAME = 'my-pwa-cache-v1';
const urlsToCache = [
  '/',
  '/index.html',
  '/styles.css',
  '/app.js',
  '/images/icon-192x192.png',
  '/images/icon-512x512.png'
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(urlsToCache))
  );
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => response || fetch(event.request))
  );
});

self.addEventListener('activate', event => {
  const cacheWhitelist = [CACHE_NAME];
  event.waitUntil(
    caches.keys().then(cacheNames =>
      Promise.all(
        cacheNames.map(cacheName => {
          if (!cacheWhitelist.includes(cacheName)) {
            return caches.delete(cacheName);
          }
        })
      )
    )
  );
});

Enabling Push Notifications

Push notifications allow you to re-engage users by sending messages even when the app is not open.

Request Notification Permission

In app.js, add a function to request notification permission:

document.getElementById('notify-btn').addEventListener('click', () => {
  Notification.requestPermission().then(permission => {
    if (permission === 'granted') {
      new Notification('Hello! Notifications are enabled.');
    }
  });
});

Set Up Push Notifications (Client Side Example)

To set up push notifications, you'll need a server to send push messages. This example demonstrates client-side setup:

// Listen for push events
self.addEventListener('push', event => {
  const data = event.data.json();
  self.registration.showNotification(data.title, {
    body: data.body,
    icon: 'images/icon-192x192.png'
  });
});

Note: Actual push functionality requires a server-side setup using libraries like web-push in Node.js.

Using Modern Web APIs

PWAs can leverage various modern web APIs to enhance functionality. Here are a few examples:

1. IndexedDB API

function openDatabase() {
  const request = indexedDB.open('myDatabase', 1);

  request.onupgradeneeded = event => {
    const db = event.target.result;
    db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true });
  };

  request.onsuccess = event => {
    const db = event.target.result;
    console.log('Database opened:', db);
  };

  request.onerror = event => {
    console.error('Database error:', event.target.error);
  };
}

openDatabase();

2. Geolocation API

function getLocation() {
  if ('geolocation' in navigator) {
    navigator.geolocation.getCurrentPosition(position => {
      console.log('Latitude:', position.coords.latitude);
      console.log('Longitude:', position.coords.longitude);
    });
  } else {
    console.log('Geolocation is not supported by this browser.');
  }
}

getLocation();

3. Web Share API

function shareContent() {
  if (navigator.share) {
    navigator.share({
      title: 'My PWA',
      text: 'Check out my Progressive Web App!',
      url: window.location.href
    }).then(() => {
      console.log('Content shared successfully!');
    }).catch(error => {
      console.error('Error sharing content:', error);
    });
  } else {
    console.log('Web Share API is not supported in this browser.');
  }
}

shareContent();

Testing Your PWA

To test your PWA, you need to serve it over HTTPS. You can use a local development server with HTTPS support, such as http-server:

npm install -g http-server
http-server -S -C cert.pem -K key.pem

Conclusion

Progressive Web Apps offer a compelling way to build web applications that provide a native-like experience. By leveraging modern web APIs such as Service Workers, Web App Manifest, and IndexedDB, you can create PWAs that are responsive, offline-capable, and engaging. This tutorial covered the essential steps to set up a PWA and integrate modern web APIs to enhance functionality.

Next Steps

  • Explore advanced Service Worker features such as background sync and periodic background sync.
  • Implement a server-side push notification service using libraries like web-push.
  • Experiment with more modern web APIs, such as the Payment Request API and Web Bluetooth API.

By mastering these techniques, you'll be well-equipped to build powerful and engaging Progressive Web Apps.

Continue Reading

Discover more amazing content handpicked just for you

Article

Mastering Modern Web Development: Trends, Tools, and Tutorials for 2025 and Beyond

Next.js (for React) and Nuxt.js (for Vue) are powerful frameworks that enable server-side rendering, static site generation, and API routes. They simplify complex tasks, improve performance, and enhance SEO.

  • Why Use Them? They offer a seamless developer experience with built-in routing, code splitting, and optimized builds.
  • Getting Started: Follow official tutorials to set up your first project. Experiment with dynamic routing and serverless functions.

Feb 11, 2025
Read More
Tutorial
php

Optimizing Large Database Queries in Laravel

   $topProducts = Cache::remember('top_products', 3600, function () {
       return Product::withCount('orders')->orderBy('orders_count', 'desc')->take(10)->get();
   });

Use DB::listen() to capture query times and identify bottlenecks:

Nov 16, 2024
Read More
Code
javascript

Dynamic and Responsive DataTable with Server-Side Processing and Custom Styling

$('#exampleTable').DataTable({
    "responsive": true,
    "processing": true,
    "serverSide": true,
    "ajax": {
        "url": "{{ route('fetchUserData') }}",
        "type": "GET",
        "error": function(xhr, status, errorThrown) {
            console.error("Error during AJAX request: ", errorThrown);
        },
        "dataSrc": function(response) {
            console.info("Data received from server: ", response);
            if (response.records && response.records.length) {
                console.info("Data entries loaded:");
                response.records.forEach(function(record) {
                    console.log(record);
                });
                return response.records;
            } else {
                console.warn("No records found.");
                return [];
            }
        }
    },
    "columns": [
        { "data": "username" },
        { "data": "points" },
        { "data": "followers" },
        { "data": "referrals" }
    ],
    "language": {
        "emptyTable": "No records to display" // Custom message for empty table
    },
    "initComplete": function() {
        $('div.dataTables_length select').addClass('form-select mt-1 w-full');
        $('div.dataTables_filter input').addClass('form-input block w-full mt-1');
    },
    "drawCallback": function() {
        $('table').addClass('min-w-full bg-gray-50');
        $('thead').addClass('bg-blue-100');
        $('thead th').addClass('py-3 px-5 text-left text-xs font-semibold text-gray-700 uppercase tracking-wide');
        $('tbody tr').addClass('border-t border-gray-300');
        $('tbody td').addClass('py-3 px-5 text-sm text-gray-800');
    }
});

This JavaScript code initializes a DataTables instance on an HTML table with the ID #exampleTable. It enhances the table with various features like responsiveness, server-side processing, AJAX data fetching, and custom styling.

Oct 24, 2024
Read More
Article

Integrating Flowbite with Tailwind CSS: A Step-by-Step Tutorial

   live-server src

This command opens your project in the default browser and reloads automatically on file changes.

Oct 24, 2024
Read More
Tutorial
bash

How to Install and Configure Apache on Ubuntu

To customize the default website served by Apache, edit the configuration file:

sudo nano /etc/apache2/sites-available/000-default.conf

Oct 21, 2024
Read More
Tutorial
bash

How to Create SSL for a Website on Ubuntu

sudo certbot renew --dry-run

If there are no errors, Certbot is successfully configured to automatically renew your certificates before they expire.

Oct 21, 2024
Read More
Article
bash

Top 25 Nginx Web Server Best Security Practices

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
limit_req zone=one burst=5;

Limit access to sensitive areas like admin panels by allowing only specific IP addresses.

Sep 24, 2024
Read More
Tutorial
css

CSS Variables and Custom Properties: Dynamic Theming and Beyond

You can create a light and dark theme by defining two sets of CSS variables and switching between them.

:root {
    --primary-color: #3498db;
    --background-color: #ffffff;
    --text-color: #333333;
}

.dark-theme {
    --primary-color: #1abc9c;
    --background-color: #2c3e50;
    --text-color: #ecf0f1;
}

body {
    background-color: var(--background-color);
    color: var(--text-color);
}

.button {
    background-color: var(--primary-color);
}

Sep 05, 2024
Read More
Tutorial
css

Advanced Flexbox Techniques: Creating Responsive and Adaptive Designs

  • Using Flexbox for Grid-Like Layouts without CSS Grid
  • Aligning Content within Grid Items
  • Creating Multi-Level Responsive Layouts with Nested Flex Containers

Sep 05, 2024
Read More
Tutorial
javascript php

Building a Custom E-commerce Platform with Laravel and Vue.js

  • components/: This directory contains reusable Vue components such as ProductList.vue and ProductDetail.vue.
  • store/: This directory will contain the Vuex store, which manages the state of your application.
  • views/: This directory contains the main pages or views of your application, such as the Home and Product pages.

Vue Router is the official router for Vue.js, enabling navigation between different views of your application. To set it up, create a new file named router.js inside the resources/js directory:

Aug 27, 2024
Read More
Cheatsheet

Responsive Design Frameworks Cheatsheet

Bootstrap is a widely-used front-end framework offering a responsive grid system, pre-styled components, and utilities for rapid development.

Pros:

Aug 21, 2024
Read More
Tutorial
python

Optimizing HTML Delivery in Flask with Minification and Compression

  • Basic understanding of Flask and Python.
  • Familiarity with web performance concepts.

Minification is a technique that removes unnecessary characters from your HTML code (such as whitespace, comments, and optional tags) without changing its functionality. While this is beneficial in production, it can make debugging more difficult during development. Therefore, it's important to conditionally apply minification based on the environment.

Aug 20, 2024
Read More
Features 1
Tailwind Component
css html

Features 1

No preview available for this content.

Aug 05, 2024
Read More
Tutorial
javascript css +1

Building a Real-Time Chat Application with WebSockets in Node.js

node server.js

http://localhost:3000/

Aug 03, 2024
Read More
Tutorial
python bash

Deploying a Flask Application on a VPS Using Gunicorn and Nginx

Generate a requirements.txt file based on your project imports:

pipreqs . --force

Aug 03, 2024
Read More
Cheatsheet
html

HTML5 Cheatsheet

HTML tables consist of rows and columns:

<table>
    <thead>
        <tr>
            <th>Header 1</th>
            <th>Header 2</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Data 1</td>
            <td>Data 2</td>
        </tr>
    </tbody>
</table>

Aug 03, 2024
Read More
Tutorial
css html

CSS Grid and Flexbox: Mastering Modern Layouts

  • flex-direction: Direction of flex items (row, row-reverse, column, column-reverse).
  • justify-content: Aligns items along the main axis (flex-start, flex-end, center, space-between, space-around).
  • align-items: Aligns items along the cross axis (flex-start, flex-end, center, baseline, stretch).
.flex-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

Aug 03, 2024
Read More
Note
javascript css +1

Automatically add Tailwind CSS and jQuery classes to any table

  • min-w-full: Full-width table.
  • bg-white: White background.
  • border border-gray-200: Adds border.
  • bg-gray-50: Light gray header background.

Aug 03, 2024
Read More
Code
php

Get Current URL

No preview available for this content.

Jan 26, 2024
Read More

Discussion 0

Please sign in to join the discussion.

No comments yet. Start the discussion!