logo
Published on

Create a Native Like Mobile Experience for Your Web App with Just One File - The Web Manifest

Authors

Progressive Web Apps (PWAs) allow your web app to look and feel like a native mobile app. With a simple Web Manifest file and some service workers, you can give users a seamless experience, allowing them to install your web app directly from the browser to their home screen. Here’s how to do it!

What is a Web Manifest?

A Web Manifest is a JSON file that provides important metadata about your web app—such as its name, icons, and theme colors. It helps the browser understand how your web app should be displayed when installed, enabling a more “app-like” experience.

Step 1: Create a Manifest File

Create a manifest.json file at the root of your project. Here’s a basic example:

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

  • name: Full name of the app.
  • short_name: Shorter name for when space is limited.
  • start_url: The entry point of the app.
  • display: How the app is displayed. Options include standalone (like a native app), fullscreen, and minimal-ui.
  • theme_color and background_color: Define the app's color schemes.
  • icons: Icon sets that the app uses, for various screen sizes.

To make your manifest usable, link it in the <head> section of your HTML file:

<link rel="manifest" href="/manifest.json" />

Step 3: Add Service Workers for Offline Support

To make your web app fully "installable" like a native app, you'll also need service workers. These allow offline functionality by caching key assets. A simple setup would be:

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

The service worker handles fetching, caching, and serving content offline, which greatly enhances the experience on mobile devices.

Step 4: Add the App to Home Screen

Once the manifest and service worker are in place, users will be prompted to install your web app from supported browsers like Chrome or Edge. They can add it directly to their mobile home screen by simply tapping the "Add to Home Screen" prompt.

Using Web Manifest in React

In a React project (like a Create React App), the public/manifest.json file is already provided for you. To customize it, modify the default manifest.json in the public folder:

{
  "short_name": "ReactApp",
  "name": "My React App",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    },
    {
      "src": "logo192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "logo512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}

React's built-in service worker can be enabled by simply modifying src/index.js:

// src/index.js
import * as serviceWorkerRegistration from './serviceWorkerRegistration'

// Activate service worker
serviceWorkerRegistration.register()

Using Web Manifest in Next.js (App Router)

Next.js 13 introduced the App Router, which changes the way we manage global settings like the Web Manifest. Here's how to set it up.

Step 1: Add manifest.json

  1. Create a manifest.json file in the public directory of your Next.js project.
{
  "name": "Next.js App",
  "short_name": "NextApp",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#0070f3",
  "icons": [
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Step 2: Update the App Router Metadata

In the App Router, you manage metadata (like the manifest and theme color) in the app/layout.js or app/layout.tsx file.

Here’s how to configure it:

// app/layout.js (or layout.tsx for TypeScript)
export const metadata = {
  title: 'Next.js App',
  description: 'My awesome Next.js app',
  themeColor: '#0070f3',
  manifest: '/manifest.json',
}

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <head />
      <body>{children}</body>
    </html>
  )
}

Step 3: Add Icons to the Public Folder

Make sure you add your app icons (like icon-192x192.png and icon-512x512.png) to the public/icons/ directory so that they are properly linked to the manifest.

Step 4: Service Worker Setup (Optional)

To add offline capabilities using service workers, you can manually create a service-worker.js in the public folder and register it in the app/layout.js:

  1. Create the service worker file:
// public/service-worker.js
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v1').then((cache) => {
      return cache.addAll(['/offline.html', '/styles.css', '/icons/icon-192x192.png'])
    })
  )
})

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request)
    })
  )
})
  1. Register the service worker in the layout.js file:

import { useEffect } from 'react'

export default function RootLayout({ children }) {
  useEffect(() => {
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker
        .register('/service-worker.js')
        .catch((error) => console.error('Service worker registration failed:', error))
    }
  }, [])

  return (
    <html lang="en">
      <head />
      <body>{children}</body>
    </html>
  )
}