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:
- The
beforeinstallprompt
event.
- State management (using Zustand).
- 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:- The event is lost when navigating between pages, requiring a reload to trigger again.
- 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
- Centralized State with Zustand:
Using Zustand, we stored the
beforeinstallprompt
event in a central location, making it accessible across components without needing to reload the page.- Session Flag for Fallback:
By storing a flag (
deferredPromptFired
) in sessionStorage
, we ensured the install button is displayed even if the user navigates back and forth between pages.- Custom UI for Better Experience:
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! 🚀