Horje
How do you Make React App a PWA ?

Progressive Web Apps (PWAs) have emerged as a game-changer, offering a blend of website accessibility and native mobile app capabilities, thereby presenting the best of both worlds.

What is a Progressive Web App (PWA)?

Progressive Web Apps (PWAs) are web applications that offer a native app-like experience on the web. They are reliable, fast, and engaging, offering features such as offline access, push notifications, and home screen installation. In this article, we’ll explore how to make a React application a PWA.

Key Features of PWA

  • Offline capability: Works even without a network connection.
  • App-like experience: Offers native-like navigation and interaction.
  • Home screen installation: Users can install the app on their devices.
  • Push notifications: Allows for re-engagement with users.

Steps to Transform Your React App into a PWA

We will Create a simple React-based web application that fetches a list of popular movies from The Movie Database (TMDb) API and displays them in a user-friendly format. The app demonstrates basic concepts in React, including state management, component rendering, and API integration.

npx create-react-app pwa-application
cd pwa-application

Get TMDb API Key

Follow these steps to get an API key from TMDb:

  1. Create an account on TMDb.
  2. Go to “Settings” > “API” to generate your API key.

Enable HTTPS

Security is crucial for PWAs. Configure your web server to serve your app over HTTPs. Let’s assume you have a mechanism in place (like a free SSL certificate from Let’s Encrypt) to handle this.

Add a Web App Manifest (WAM): (manifest.json)

The WAM is a JSON file that provides essential information about your PWA, including its name, icons, theme color, and startup screen. Create a file named manifest.json at the root of your project directory and add the following content, customizing it for your app:

This JSON file provides metadata about your PWA, including its name, icons, and installation behavior on the home screen

public->manifest.json

manifest.json
{
  "short_name": "React App",
  "name": "Create React App Sample",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    },
    {
      "src": "maskable_icon_x192.png",
      "type": "image/png",
      "sizes": "192x192",
      "purpose":"any maskable"
    },
    {
      "src": "maskable_icon_x192.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}

A service worker is a script that runs in the background, enabling functionalities like offline access, push notifications, and background sync. Create a file named serviceWorker.js in your public directory and add the following code:

public->index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
    <script>
      if('serviceWorker' in navigator) {
        window.addEventListener('load', () => {
          navigator.serviceWorker.register('./serviceWorker.js').then((reg) => {
            console.log('Worker Registered')
          }).catch((err) => {
            console.log('Error in service worker registration.')
          })
        })
      }
    </script>
  </body>
</html>

Implementing Offline Functionality

Service Worker Caching: Utilize the Service Worker Caching API to cache static assets like HTML, CSS, JavaScript, and images.

Now inside the public create serviceWorker.js

//STORAGE OF BROWSER
const CACHE_NAME = "version-1";
const urlsToCache = ["index.html", "offline.html"];
const self = this;

//installation
self.addEventListener("install", (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      console.log("Opened cache");

      return cache.addAll(urlsToCache);
    })
  );
});

// listen for request
self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((res) => {
      return fetch(event.request).catch(() => caches.match("offline.html"));
    })
  );
});

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

Register the Service Worker (index.js)

In your src/index.js file, import and register the service worker:

JavaScript
// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

// Import the service worker file
import * as serviceWorkerRegistration from './serviceWorker'; 

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// Register the service worker (with optional delay)
serviceWorkerRegistration.register({
  onUpdate: (registration) => {
    const confirmationMessage = 
        `New update is available. Click OK to reload.`;
    if (confirm(confirmationMessage)) {
      registration.waiting.postMessage({ type: 'SKIP_WAITING' });
    }
  },
});

// If you want to delay service worker registration (optional)
// serviceWorkerRegistration.register({
//   delay: 10000, // Delay by 10 seconds
// });

// If you want to register the service
// worker only in production (optional)
// if (process.env.NODE_ENV === 'production') {
//   serviceWorkerRegistration.register();
// }

reportWebVitals();

Enabling Push Notifications (Optional)

Push notifications are a powerful way to re-engage users and keep them informed even when they’re not actively using your app. By implementing push notifications in your React PWA with Firebase Cloud Messaging (FCM), you can:

  • Send real-time updates and messages to users.
  • Increase user engagement and retention.
  • Promote new features or content.
  • Drive traffic back to your PWA.

Steps to Create a React App

Step 1: Create a React application using the following command:

npx create-react-app pwa-application

Step 2: After creating your project folder i.e. foldername, move to it using the following command:

cd pwa-application

Step 3: Install the required dependencies in your project using the following command:

npm install axios 

Project Structure:

Screenshot-2024-04-28-154013

Project Structure:

The updated dependencies in your packge.json file is:

"dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "axios": "^1.6.7",
},
CSS
/* App.css */

.app-container {
    padding: 20px;
}

.movie-list {
    list-style: none;
    padding: 0;
}

.movie-item {
    margin-bottom: 20px;
    display: flex;
    align-items: center;
}

.movie-poster {
    margin-right: 10px;
    width: 100px;
    /* Set a fixed width for the poster images */
    height: auto;
    /* Maintain aspect ratio */
}
JavaScript
//App.js

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import './App.css';

const API_KEY = 'paste your api key here';
const MOVIE_API_URL = `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}&language=en-US&page=1`;
const BASE_IMAGE_URL = 'https://image.tmdb.org/t/p/w200';

function App() {
    const [movies, setMovies] = useState([]);

    useEffect(() => {
        axios.get(MOVIE_API_URL)
            .then((response) => {
                setMovies(response.data.results);
            })
            .catch((error) => {
                console.error('Error fetching the movies:', error);
            });
    }, []);

    return (
        <div className="app-container">
            <h1>Popular Movies</h1>
            <ul className="movie-list">
                {movies.map((movie) => (
                    <li key={movie.id} className="movie-item">
                        <img
                            src={`${BASE_IMAGE_URL}${movie.poster_path}`}
                            alt={movie.title}
                            className="movie-poster"
                        />
                        <strong>{movie.title}</strong>
                        (Release Date: {movie.release_date})
                    </li>
                ))}
            </ul>
        </div>
    );
}

export default App;
JavaScript
// serviceWorker.js

//STORAGE OF BROWSER
const CACHE_NAME = "version-1";
const urlsToCache = ["index.html", "offline.html"];
const self = this;

//installation
self.addEventListener("install", (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      console.log("Opened cache");

      return cache.addAll(urlsToCache);
    })
  );
});

// listen for request
self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((res) => {
      return fetch(event.request).catch(() => caches.match("offline.html"));
    })
  );
});

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

Start your application using the following command:

npm start

Output:

Recording-2024-04-28-153437

Final output

Conclusion

By following these steps, you’ve converted the “Movie List Application” into a Progressive Web App. This PWA has a service worker for offline support, a manifest file for metadata and icons, and HTTPS for security. It’s installable on users’ devices and works even when offline, providing a more engaging user experience.




Reffered: https://www.geeksforgeeks.org


ReactJS

Related
How to use React Icons in Next.js ? How to use React Icons in Next.js ?
How To Add Styling To An Active Link In NextJS? How To Add Styling To An Active Link In NextJS?
How to Set NextJS Images with auto Width and Height? How to Set NextJS Images with auto Width and Height?
How to use CORS in Next.js to Handle Cross-origin Requests ? How to use CORS in Next.js to Handle Cross-origin Requests ?
Global CSS Must be in Custom Next.js App Global CSS Must be in Custom Next.js App

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
17