Skip to main content

Angular Installation Prompts

Introduction

Progressive Web Applications (PWAs) combine the best features of web and mobile applications. One of the key features of PWAs is their ability to be installed on a user's device, providing an experience similar to native applications. In this tutorial, we'll learn how to implement installation prompts in an Angular PWA, which encourage users to add your application to their home screen.

Installation prompts are crucial for improving user engagement and retention. When users install your PWA on their device, they're more likely to return to your application and engage with its features regularly.

Prerequisites

Before diving into installation prompts, make sure you have:

  • Node.js and npm installed
  • Angular CLI installed (npm install -g @angular/cli)
  • A basic understanding of Angular
  • An existing Angular project with PWA support

If you haven't added PWA support to your Angular project yet, you can do so with:

bash
ng add @angular/pwa

How Installation Prompts Work

Modern browsers that support PWAs can detect when a web application meets the criteria for installation. When these criteria are met, the browser triggers a native installation prompt. As developers, we can programmatically:

  1. Detect when the installation prompt is available
  2. Defer the prompt to show it at a more appropriate time
  3. Trigger the prompt at a strategic moment in user interaction

Implementing Installation Prompts in Angular

Let's create a service to handle the installation prompt logic in our Angular application.

Step 1: Create an Installation Service

First, generate a new service:

bash
ng generate service services/pwa-install

Now, implement the service:

typescript
// src/app/services/pwa-install.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class PwaInstallService {
private deferredPrompt: any;
private installPromptAvailable = new BehaviorSubject<boolean>(false);

public installPromptAvailable$ = this.installPromptAvailable.asObservable();

constructor() {
this.initialize();
}

private initialize() {
window.addEventListener('beforeinstallprompt', (event) => {
// Prevent the default prompt display
event.preventDefault();

// Store the event for later use
this.deferredPrompt = event;

// Signal that the prompt is available
this.installPromptAvailable.next(true);
});

window.addEventListener('appinstalled', () => {
// Log that the app was installed
console.log('PWA was installed');

// Reset the deferred prompt
this.clearPrompt();
});
}

public showInstallPrompt() {
if (!this.deferredPrompt) {
console.log('Installation prompt not available');
return;
}

// Show the install prompt
this.deferredPrompt.prompt();

// Wait for the user's choice
this.deferredPrompt.userChoice.then((choiceResult: { outcome: string }) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the installation');
} else {
console.log('User dismissed the installation');
}

// Reset the deferred prompt
this.clearPrompt();
});
}

private clearPrompt() {
this.deferredPrompt = null;
this.installPromptAvailable.next(false);
}

public isInstallable(): boolean {
return !!this.deferredPrompt;
}
}

Step 2: Create an Installation Button Component

Let's create a component that will display an installation button when appropriate:

bash
ng generate component components/install-prompt

Implement the component:

typescript
// src/app/components/install-prompt/install-prompt.component.ts
import { Component, OnInit } from '@angular/core';
import { PwaInstallService } from '../../services/pwa-install.service';

@Component({
selector: 'app-install-prompt',
templateUrl: './install-prompt.component.html',
styleUrls: ['./install-prompt.component.scss']
})
export class InstallPromptComponent implements OnInit {
showButton = false;

constructor(private pwaInstallService: PwaInstallService) { }

ngOnInit() {
this.pwaInstallService.installPromptAvailable$.subscribe(isAvailable => {
this.showButton = isAvailable;
});
}

installPwa() {
this.pwaInstallService.showInstallPrompt();
}
}

And the HTML template:

html
<!-- src/app/components/install-prompt/install-prompt.component.html -->
<div *ngIf="showButton" class="install-container">
<div class="install-prompt">
<div class="install-content">
<h3>Install our App</h3>
<p>Install this application on your home screen for quick and easy access when you're on the go.</p>
</div>
<div class="install-actions">
<button mat-raised-button color="primary" (click)="installPwa()">
Install Now
</button>
</div>
</div>
</div>

Add some styles:

scss
/* src/app/components/install-prompt/install-prompt.component.scss */
.install-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 16px;
background-color: #f8f9fa;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
z-index: 999;
}

.install-prompt {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 900px;
margin: 0 auto;
}

.install-content {
flex: 1;

h3 {
margin: 0;
margin-bottom: 8px;
}

p {
margin: 0;
}
}

.install-actions {
margin-left: 16px;
}

Step 3: Add the Install Prompt Component to Your App

Include the install prompt component in your app, typically in your AppComponent:

html
<!-- src/app/app.component.html -->
<div class="app-container">
<!-- Your existing app content -->
<router-outlet></router-outlet>

<!-- Install prompt will appear when available -->
<app-install-prompt></app-install-prompt>
</div>

Custom Installation Strategies

The basic implementation above shows the installation prompt at the bottom of the screen. However, for a better user experience, you might want to customize when and how you display the prompt. Here are some strategies:

Strategic Prompt Timing

Rather than showing the prompt immediately, consider displaying it:

  1. After the user has interacted with your app for a certain time
  2. After the user has completed a specific action
  3. When the user visits for the second or third time

Let's implement a strategic timing approach:

typescript
// src/app/services/pwa-install.service.ts
// Add these methods to the existing PwaInstallService

public showPromptAfterInteraction(minInteractionTime: number = 30000) {
// Only proceed if the prompt is available
if (!this.deferredPrompt) {
return;
}

// Show the prompt after the user has spent some time on the site
setTimeout(() => {
if (this.deferredPrompt) {
this.showInstallPrompt();
}
}, minInteractionTime);
}

public showPromptAfterPageViews(storageKey: string = 'app_page_views', minViews: number = 3) {
// Track page views in local storage
const views = parseInt(localStorage.getItem(storageKey) || '0', 10) + 1;
localStorage.setItem(storageKey, views.toString());

// Show prompt after minimum views reached
if (views >= minViews && this.deferredPrompt) {
this.showInstallPrompt();
}
}

Custom Installation UI

Instead of using the browser's native prompt, you can create a more customized UI experience. Let's modify our install component to show a more prominent modal:

typescript
// src/app/components/install-prompt/install-prompt.component.ts
// Add to the existing component
import { MatDialog } from '@angular/material/dialog';

// Add this to the component class
openCustomInstallDialog() {
const dialogRef = this.dialog.open(CustomInstallDialogComponent);

dialogRef.afterClosed().subscribe(result => {
if (result === true) {
this.pwaInstallService.showInstallPrompt();
}
});
}

Create a custom dialog component:

bash
ng generate component components/custom-install-dialog
typescript
// src/app/components/custom-install-dialog/custom-install-dialog.component.ts
import { Component } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';

@Component({
selector: 'app-custom-install-dialog',
templateUrl: './custom-install-dialog.component.html',
styleUrls: ['./custom-install-dialog.component.scss']
})
export class CustomInstallDialogComponent {
constructor(public dialogRef: MatDialogRef<CustomInstallDialogComponent>) {}

install() {
this.dialogRef.close(true);
}

cancel() {
this.dialogRef.close(false);
}
}
html
<!-- src/app/components/custom-install-dialog/custom-install-dialog.component.html -->
<h2 mat-dialog-title>Install Our App</h2>
<mat-dialog-content>
<div class="dialog-content">
<div class="app-icon">
<img src="assets/icons/icon-128x128.png" alt="App Icon">
</div>
<div class="app-details">
<h3>Get the full experience</h3>
<p>Install our app on your device for:</p>
<ul>
<li>Faster access</li>
<li>Better performance</li>
<li>Offline capabilities</li>
<li>App-like experience</li>
</ul>
</div>
</div>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button (click)="cancel()">Not Now</button>
<button mat-raised-button color="primary" (click)="install()">Install</button>
</mat-dialog-actions>
scss
/* src/app/components/custom-install-dialog/custom-install-dialog.component.scss */
.dialog-content {
display: flex;
align-items: center;

.app-icon {
margin-right: 16px;

img {
width: 80px;
height: 80px;
border-radius: 12px;
}
}

.app-details {
h3 {
margin-top: 0;
}
}
}

mat-dialog-actions {
margin-top: 16px;
}

Testing Installation Prompts

Testing installation prompts can be challenging because they depend on specific criteria. Here's how to test them effectively:

  1. Make sure your app meets PWA criteria:

    • Has a valid manifest.json
    • Uses HTTPS
    • Has registered service workers
    • Has appropriate icons
  2. Use Chrome DevTools:

    • Open your app in Chrome
    • Open DevTools (F12)
    • Go to Application tab > Manifest section
    • Click on "Add to home screen" to manually trigger the prompt
  3. Clear stored installation decisions:

    • In Chrome DevTools, go to Application > Storage
    • Click "Clear site data"
    • Reload the page

Real-world Example: E-commerce App Installation Prompt

Let's see a more complete example of strategically showing an installation prompt in an e-commerce application:

typescript
// Example: e-commerce-app.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';
import { PwaInstallService } from './services/pwa-install.service';
import { CartService } from './services/cart.service';

@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
private pageViews = 0;
private hasAddedToCart = false;

constructor(
private router: Router,
private pwaInstallService: PwaInstallService,
private cartService: CartService
) {}

ngOnInit() {
// Track page views
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe(() => {
this.pageViews++;
this.checkInstallCriteria();
});

// Track cart additions
this.cartService.cartUpdated.subscribe(() => {
if (!this.hasAddedToCart && this.cartService.getCartItems().length > 0) {
this.hasAddedToCart = true;
this.checkInstallCriteria();
}
});
}

private checkInstallCriteria() {
// Show install prompt if:
// 1. User has viewed at least 3 pages
// 2. User has added something to cart
// 3. The prompt is available
if (this.pageViews >= 3 && this.hasAddedToCart) {
this.pwaInstallService.isInstallable() && this.pwaInstallService.showInstallPrompt();
}
}
}

This example shows an installation prompt only after the user has engaged with the site by viewing multiple pages and adding items to their cart, suggesting they have interest in returning to the app.

Common Installation Prompt Issues and Solutions

Prompt Doesn't Appear

If your installation prompt doesn't appear:

  1. Verify your app meets PWA criteria:
bash
# Check with Lighthouse in headless mode
npx lighthouse https://your-app-url.com --output=html --output-path=./lighthouse-report.html --chrome-flags="--headless"
  1. Ensure you're handling the beforeinstallprompt event correctly
  2. Make sure the app isn't already installed
  3. Verify that your manifest.json has all required fields:
json
{
"name": "My Angular PWA",
"short_name": "PWA App",
"theme_color": "#1976d2",
"background_color": "#fafafa",
"display": "standalone",
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
// Include more icon sizes...
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

Installation Analytics

To track installation rates, you can implement analytics:

typescript
// Add this to your PwaInstallService
private trackInstallation(wasAccepted: boolean) {
// If you're using Google Analytics
if (typeof gtag !== 'undefined') {
gtag('event', wasAccepted ? 'pwa_install_accepted' : 'pwa_install_rejected', {
'event_category': 'pwa',
'event_label': 'installation'
});
}

// You can also track this to your own analytics service
console.log(`PWA installation was ${wasAccepted ? 'accepted' : 'rejected'}`);
}

// Then modify the showInstallPrompt method to use it
public showInstallPrompt() {
// ...existing code...

this.deferredPrompt.userChoice.then((choiceResult: { outcome: string }) => {
const wasAccepted = choiceResult.outcome === 'accepted';
this.trackInstallation(wasAccepted);
// ...existing code...
});
}

Summary

Installation prompts are a powerful feature of PWAs that can help drive user engagement with your Angular application. Here's what we've learned:

  • How to detect when a PWA installation prompt is available
  • How to defer and trigger the prompt at strategic times
  • How to create custom UI for installation prompts
  • How to test installation prompts during development
  • Best practices for timing and displaying installation prompts
  • How to track installation analytics

By implementing effective installation prompts, you can increase the likelihood that users will install your PWA, leading to better engagement and retention rates.

Additional Resources

Exercises

  1. Implement a basic installation prompt in an existing Angular application
  2. Create a custom installation dialog with animations
  3. Implement a strategy that shows the installation prompt after a user completes a specific action (like submitting a form)
  4. Add analytics tracking to measure how many users view vs. accept your installation prompts
  5. Create an A/B test with different installation prompt designs and track which one converts better


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)