React PWA with TypeScript using CRA or Vite

ยท

6 min read

Featured on Hashnode

Progressive Web Apps (PWA) are websites with some code that allows users to download it on their devices (either desktop or mobile) and use it as a native app.

Of course, there are some limitations to what a PWA can do compared with the native apps (like push notifications on ios, screen brightness, wake lock or others) but if you are happy with what you can deliver with your website then you are all set.

If you want to create a PWA from your website, all you have to do is to add a service worker, some logos and a manifest file.

I was in this scenario and I realized that is not that hard but is not that simple either, there are some steps to follow and you can have your PWA ready to download on your device.

If you want, you can check out this netlify link with my demo pwa: Gift List Demo PWA or if you are curious, take a look at my repo: Github repo

First step: Add a service worker

First scenario: PWA using create react app template

In this scenario, we'll assume that you start your project from scratch and you want to start with a template that makes all the hassle for you. In this case, a out of box solution is to use the template provided by create react app for PWA.

Create React App (CRA) it's the tool of choice for developers when it comes to constructing a React development environment on a whim.

To use the template, simply open your terminal and run the following command:

npx create-react-app my-app --template cra-template-pwa-typescript

or if don't want to use typescript:

npx create-react-app my-app --template cra-template-pwa

In addition, you'll have to switch serviceWorker.unregister() to serviceWorker.register() in your index.tsx file.

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: 
serviceWorker.register({})

This will handle everything under the hood and when you run npm run build it will add the service-worker.js file to your build directory among other things.

Just make sure to update your manifest.json file with your web app name, icons urls, colors and start url. If you are not sure how to do this, just follow the instructions in the second step.

Second scenario: PWA for an app started with the default cra template and not pwa template

Fair enough, but what should I do if I have an existing React app started with the default cra template but not the PWA template? (actually, I didn't know there is a pwa template in the first place ...)

Weeeellll, we can cheat a bit and copy the files from the pwa template provided by create-react-app. If you'll install it in a different folder and explore their template, you can add the following files to your own react app: src/service-worker.ts and src/serviceWorkerRegistration.ts

In addition, the following should be added at the end of your index.tsx / main.tsx file: serviceWorkerRegistration.register();

Also, if you look in the package.json file, you'll notice some workbox- dependencies. Go ahead and add them in your package.json and after, run an npm install in your project.

"dependencies": {
    // other dependencies
    "workbox-background-sync": "^6.5.4",
    "workbox-broadcast-update": "^6.5.4",
    "workbox-cacheable-response": "^6.5.4",
    "workbox-core": "^6.5.4",
    "workbox-expiration": "^6.5.4",
    "workbox-google-analytics": "^6.5.4",
    "workbox-navigation-preload": "^6.5.4",
    "workbox-precaching": "^6.5.4",
    "workbox-range-requests": "^6.5.4",
    "workbox-routing": "^6.5.4",
    "workbox-strategies": "^6.5.4",
    "workbox-streams": "^6.5.4"
},

Now when you build your project, your service-worker.js file is in your build directory. The only things remaining are the following steps with the icons for your app and the manifest file.

Third scenario: Having an existing React App built with Vite

Suppose you want to create a PWA from your React project, created with Vite instead of create-react-app, and you don't have a template for pwa like cra does.

I was in this situation and thankfully Vite provides a plugin that makes this process a lot easier than I thought it would be:

npm i vite-plugin-pwa -D

Edit your vite.config.js / vite.config.ts file and add the vite-plugin-pwa:

import { defineConfig } from "vite";
import { VitePWA } from "vite-plugin-pwa";
import react from "@vitejs/plugin-react";

// https://vite-pwa-org.netlify.app/guide/
export default defineConfig({
  plugins: [react(), VitePWA({ registerType: "autoUpdate" })],
});

That will be it, at the next build, Vite will take care of the service-worker file needed for your PWA.

Now we have to make sure to have our app icons and manifest.json file updated as I'll mention in the following steps (app icons, app maskable icon and app manifest).

Second step: Add favicon/app logo and other icons

If you already have these images in your app, skip to the next step.

If not, you'll need an SVG (ideally) or a large png image (512x512 for optimal results) for your app logo.

I used this favicon generator which is useful for this sort of use case.

After you download their generated files, you should move them into your public folder and add their markup code to the index.html file at the root of your project.

<!-- Favicon -->
<link rel="shortcut icon" href="favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#eab308" />
<meta name="msapplication-TileColor" content="#eab308" />
<meta name="theme-color" content="#eab308" />
<!-- End Favicon -->

Another way to generate these icons is to use tools like pwa-asset-generator.

Add maskable icon

Maskable icons are a new icon format that gives you more control and let your Progressive Web App use adaptive icons. If you supply a maskable icon, your icon can fill up the entire shape and look great on all devices.

For this, you can use the Maskable.app Editor. Upload your icon, adjust the color and size, then export the image and save it in the same public folder with the rest of your generated images.

3rd step: Add app manifest

Create a manifest.json file with the following structure:

{
  "name": "Name of your app",
  "short_name": "Name of your app",
  "icons": [
    {
      "src": "/maskable_icon.png",
      "sizes": "200x200",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-256x256.png",
      "sizes": "256x256",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    }
  ],
  "theme_color": "#eab308",
  "background_color": "#eab308",
  "display": "standalone",
  "scope": "/",
  "start_url": "willowy-moxie-f6e35f.netlify.app"
}

For each icon, you'll provide the path to the images created in the previous step, for the theme and background color I'll leave it to you to decide, as for display, you have to select standalone so your web app will behave similarly to a native one.

As for the start_url, you'll need to specify your deployed public path, which in this case was the netlify provided link.

That's it!!! ๐ŸŽ‰ Now if you build the project and deploy it, you'll have a PWA that can be installed on your android/ios/macOS/windows device. Isn't that sweeeeet?

ย