cover
PWAReactZustandState managementdownload button

How to build and display a custom download button to install PWA (Progressive Web App) on your website

Cover
Screenshot_20241121_184127.png
Slug
custom-button-to-install-pwa
Published
Published
Date
Nov 21, 2024
Category
PWA
React
Zustand
State management
download button

Building a Custom Install Button for Your Progressive Web App (PWA)

Introduction
Progressive Web Apps (PWAs) have gained massive popularity in modern web development. PWAs are web applications designed to deliver app-like experiences directly through the browser. They can be installed on a device’s home screen, work offline, and utilize push notifications, making them a bridge between native apps and web apps.
One of the key features of a PWA is the ability to allow users to install it directly from the browser. While browsers often display a default "Add to Home Screen" banner, creating a custom install button ensures better control over the user experience.
In this blog, we’ll walk through how we created a custom download button for our PWA using:
  1. The beforeinstallprompt event.
  1. State management (using Zustand).
  1. A seamless experience that doesn’t require a page refresh.

What is the beforeinstallprompt Event?

The beforeinstallprompt event is fired by the browser when a web app meets the PWA installation criteria (e.g., HTTPS, manifest, service worker). This event gives developers control over how and when to show the installation prompt, allowing for custom UI elements like an "Install App" button.

Key Points about beforeinstallprompt:

  • Prevents default behavior: By calling e.preventDefault(), you stop the browser from automatically showing the default install banner.
  • Stores the event: The event must be stored temporarily to trigger the prompt later when the user clicks the custom install button.
  • Single-use: The beforeinstallprompt event can be used only once after firing. If dismissed, it won't fire again unless the user navigates to another page.

The Challenge

While the beforeinstallprompt event is straightforward to use, it has limitations:
  1. The event is lost when navigating between pages, requiring a reload to trigger again.
  1. Storing and reusing the event requires careful handling since it's an event object and not serializable.
Our goal was to overcome these challenges by implementing state management to store and reuse the event, allowing for a seamless install flow without refreshing the page.

Solution: Managing the beforeinstallprompt Event with Zustand

Zustand is a lightweight state management library perfect for global state management in React applications. We used Zustand to store the beforeinstallprompt event, making it accessible across components without needing to refresh the page.
Here’s how we did it:

1. Capturing the beforeinstallprompt Event

On the login page, we added an event listener to capture the beforeinstallprompt event and stored it in the Zustand store for later use.
import { useEffect } from 'react'; import useInstallPromptStore from './stores/useInstallPromptStore'; const LoginPage = () => { const setDeferredPrompt = useInstallPromptStore((state) => state.setDeferredPrompt); useEffect(() => { const handleBeforeInstallPrompt = (e) => { e.preventDefault(); // Prevent the default banner setDeferredPrompt(e); // Store the event in Zustand window.sessionStorage.setItem('deferredPromptFired', true); // Optional: Store a flag }; window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt); return () => { window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt); }; }, [setDeferredPrompt]); return <div>Login Page</div>; };

2. Storing the Event in Zustand

We created a Zustand store to manage the beforeinstallprompt event and a flag to track whether it has been fired.
import { create } from 'zustand'; const useInstallPromptStore = create((set) => ({ deferredPrompt: null, // Store the event isPromptFired: false, // Flag to indicate event firing setDeferredPrompt: (event) => set({ deferredPrompt: event, isPromptFired: true }), clearDeferredPrompt: () => set({ deferredPrompt: null, isPromptFired: false }), })); export default useInstallPromptStore;

3. Displaying the Custom Install Button

On the next page (e.g., the main dashboard or home page), we retrieved the stored beforeinstallprompt event from the Zustand store and used it to display a custom install button.
import { useEffect, useState } from 'react'; import useInstallPromptStore from './stores/useInstallPromptStore'; const HomePage = () => { const deferredPrompt = useInstallPromptStore((state) => state.deferredPrompt); const isPromptFired = useInstallPromptStore((state) => state.isPromptFired); const clearDeferredPrompt = useInstallPromptStore((state) => state.clearDeferredPrompt); const [showInstallButton, setShowInstallButton] = useState(false); useEffect(() => { const promptFired = window.sessionStorage.getItem('deferredPromptFired'); if (isPromptFired || promptFired) { setShowInstallButton(true); } }, [isPromptFired]); const handleInstallClick = () => { if (deferredPrompt) { deferredPrompt.prompt(); // Trigger the install prompt deferredPrompt.userChoice.then((choiceResult) => { if (choiceResult.outcome === 'accepted') { console.log('User accepted the install prompt'); } else { console.log('User dismissed the install prompt'); } clearDeferredPrompt(); // Clear the stored event after use window.sessionStorage.removeItem('deferredPromptFired'); }); } }; return ( <div> {showInstallButton && <button onClick={handleInstallClick}>Install App</button>} </div> ); }; export default HomePage;

Why This Works

  1. Centralized State with Zustand:
    1. Using Zustand, we stored the beforeinstallprompt event in a central location, making it accessible across components without needing to reload the page.
  1. Session Flag for Fallback:
    1. By storing a flag (deferredPromptFired) in sessionStorage, we ensured the install button is displayed even if the user navigates back and forth between pages.
  1. Custom UI for Better Experience:
    1. The custom install button gave us full control over the user experience, ensuring the prompt appears when users are most likely to engage with it.

Conclusion

Creating a custom install button for your PWA using the beforeinstallprompt event and Zustand is a robust solution for managing the PWA installation flow. By leveraging state management, you can provide a seamless user experience, eliminate the need for page reloads, and encourage more users to install your app.
PWAs are powerful tools for bridging the gap between web and native applications. Adding a touch of customization like this can make a significant difference in user adoption.
Happy coding! 🚀