- Clone this project using the github link using
git clone - Go into the project folder, using
cd nextjs-pwa-capacitor - Run
npm install
First, run the development server:
npm run dev
# or
npm run dev:https # run the app with https
#or
yarn dev
# or
pnpm dev
# or
bun dev# To use Turbo Pack use flag
--turbo
# But be careful because @servwit / @next-pwa does not work with turbopack (Updated 22 FEB 2024)Related Issue: Click Here
Open http://localhost:3000 with your browser to see the result.
if your are using npm run dev:https, Open https://localhost:3000 with your browser to see the result.
This project uses next/font to automatically optimize and load Inter, a custom Google Font.
/public
firebase-messaging-sw.js
/src
/app
manifest.json
globals.css
favicon.ico
layout.jsx # shared layout for all pages in App router
page.jsx # similar to index.jsx when using Pages Router
/components
/firebase
firebase.js
messaging.js # helper functions for FCM, for example getToken or OnTokenRefresh, then do something
/utils
/helpers
/services
/hooks
/sw # for service worker
sw.js- First we will need to create a project, or use an existing one by going to
console.firebase.google.comandAdd Project
- Then we will need to configure
Web Credentials with FCM. The FCM Web interface uses Web credentials calledVoluntary Application Server Identification, orVAPIDkeys, to authorize send requests to supported web push services. To create thisVAPIDkey, we need to go toProject Overwiew -> Project settings.
- On the
Cloud Messagingtab of theProject settings, you will see aWeb Configurationsection, which you will consequently see a buttonGenerate key pair.
- After clicking the button, you will see the key pair, which we will use in
VAPIDkey when usinggetToken()in our PWA App
- On the
Project Overviewtab of yourFirebase Console, if you have noappadded to your project you will see aGet Startedheader, click on your preferred app to add, for this we will go for theweb appone since we are building aProgressive Web App(PWA)experience.
- On the next screen your should see
Register appwith an input forApp nickname, type your app nickname, if you wantFirebase Hostingyou can click on the checkbox as well, but we will not be using that.
- Consequently, you will see the below, which will tell to install the
Firebase SDKusingnpm install firebaseand more importantly yourFirebase Config, click on theContinue to the Console
- If you need the
Firebase Configagain, you can always find it inProject settings -> General -> Scroll toYour apps
- On the root of your project, run
npm install firebase; - In the
src/folder, createfirebase/folder- Add/Create 2 files in it,
firebase.js,messaging.js
- Add/Create 2 files in it,
- In
firebase.js, add yourFirebase Config:
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { getMessaging, isSupported } from 'firebase/messaging';
import { getStorage } from 'firebase/storage';
// Configure Firebase.
const firebaseConfig = {
apiKey: "<REPLACE-YOUR-API-KEY>",
authDomain: "fir-example-1fe2d.firebaseapp.com",
projectId: "fir-example-1fe2d",
storageBucket: "fir-example-1fe2d.appspot.com",
messagingSenderId: "<REPLACE-YOUR-MESSAGING-SENDER-ID>",
appId: "<REPLACE-YOUR-APP-ID>",
};
export const app = initializeApp(firebaseConfig);
// Call by await messaging() everytime a messaging instance is needed
export const messaging = async () => await isSupported() && getMessaging(app); - Create a service worker for FCM, in your public folder, add/create
firebase-messaging-sw.js, and we can leave it blank since we will be incorporating the configuration theFCMservice worker in our own service worker,src/utils/sw/sw.js. - In
src/utils/sw/sw.js:
import { defaultCache } from "@serwist/next/browser";
import { installSerwist } from "@serwist/sw";
import { initializeApp } from "firebase/app";
import { getMessaging, onBackgroundMessage } from "firebase/messaging/sw";
installSerwist({
precacheEntries: self.__SW_MANIFEST,
skipWaiting: true,
clientsClaim: true,
navigationPreload: true,
runtimeCaching: defaultCache,
});
const firebaseConfig = {
apiKey: "<REPLACE-YOUR-API-KEY>",
authDomain: "fir-example-1fe2d.firebaseapp.com",
projectId: "fir-example-1fe2d",
storageBucket: "fir-example-1fe2d.appspot.com",
messagingSenderId: "<REPLACE-YOUR-MESSAGING-SENDER-ID>",
appId: "<REPLACE-YOUR-APP-ID>",
};
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
onBackgroundMessage(messaging, (payload) => {
console.log(
'[firebase-messaging-sw.js] Received background message ',
payload
);
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle, notificationOptions);
});You might have other service worker actions in sw.js, this part only reflect a default one
- As observed in
sw.js, we only handledOnBackgroundMessage, which will be called when the app is in the background and it receives messages, forforegroundmessages, we useOnMessagedirectly in our pages, for example here inpage.jsx:
// Just make sure we use it in client compoenents and not in server components, as we need the navigator
// to be available to use and to be defined.
useEffect(() => {
const messaging = getMessaging();
onMessage(messaging, (payload) => {
console.log('Message received. ', payload);
// ...
});
}, []);- [ Optional ] If you want to have the
Firebase Configurationinsw.jsto be in the recommendedfirebase-messaging-sw.js, you should, then in that case yourimport's will be different as you will usingimportScripts, below is an example:
importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js');
const firebaseConfig = {
apiKey: "<REPLACE-YOUR-API-KEY>",
authDomain: "fir-example-1fe2d.firebaseapp.com",
projectId: "fir-example-1fe2d",
storageBucket: "fir-example-1fe2d.appspot.com",
messagingSenderId: "<REPLACE-YOUR-MESSAGING-SENDER-ID>",
appId: "<REPLACE-YOUR-APP-ID>",
};
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
messaging.onBackgroundMessage((payload) => {
console.log(
'[firebase-messaging-sw.js] Received background message ',
payload
);
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle, notificationOptions);
});- This snippet is important, as with this, you will get a
FCM Registration Token, which will act as adevice idfor the devices needed to connect toFirebase Cloud Message:
const msg = await messaging();
const registration = await navigator.serviceWorker.ready;
console.log("registration", registration)
const fcmToken = await getToken(
msg,
{
serviceWorkerRegistration: registration,
vapidKey: VAPID_KEY // put the key pair you generated here
}
);
if (fcmToken) {
console.log('Got FCM device Token: ', fcmToken);
// You might want to store this token in your app server to keep track of all device ids registration
}- [ NOTE ] : Don't forget to
request permission for notificationsto the user:
function requestPermission() { // you can call this function where it is need, or in the layout.jsx itself
console.log('Requesting permission...');
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.');
}
});
}- After all of the above, you should be to
Send a test messageinFirebase Console.
- In your project's
Firebase Console, go toMessaging. - Then if you do not have an existing
campaign, create one:
- Then choose
Firebase Notification messages, thenCreate:
- On the next page, enter some details, then click on
Send test message:
- Add your
FCM Device Token, we got from theconsole.login the PWA App in theAdd an FCM registration token, and then clickTest:
- If everything goes well, you should be able to see your notification when the app in the
backgroundorforeground. - [ NOTE ]: Sometimes you might need to refresh the
Send test messagepage in theFirebase Consoleto actually see a result.
- When app is running using
npm run dev:https, certicates will be created. - For Windows, not much is need to be done to allow
installationas desktop app - For Android, you need the
CA Certificate:Root CA, which need to be installed on Android,Settings -> search for Install Certificate -> CA CertificateRoot CAis located atC:\Users\<USER>\AppData\Local\mkcertby default, copy and paste to your android and then install it
Generally, you don't need to run npx cap sync or npx cap copy while you're focusing solely on PWA development. These commands are used primarily for native development to copy web assets into the native platforms (iOS/Android) and apply any necessary configuration or plugin updates. When developing a PWA, you're essentially working on a web application, so your usual web development workflow applies.
NOTE: If you plan to test or deploy your application as a native app (on iOS or Android), that's when npx cap sync or npx cap copy come into play. npx cap sync is a comprehensive command that updates dependencies, copies web assets, and applies any configuration changes to the native projects. npx cap copy is more specific and only copies the web assets to the native projects. If you've made changes to Capacitor plugins or the native configurations, you'll need to use npx cap sync.
Mercure: Cick to learn More
Capacitor: Click Here to learn More
- if constant loop full-reload:
- Stop the Next Server
- go to Application Tab in Browser DevTools
- Unregister all related service workers
- Then go to storage in Application Tab and then
clear site data
Web APIs Documentation from https://web.dev:
- Notifications: Click Here
- Displaying a Notification: Click Here
- Make your PWA feel more like an APP: Click Here
- Setting Up Firebase JS: Click Here
firebaseConfig: Click Here
- Using Firebase Cloud Messaging JS: Click Here
- App Router Setup Guide: Click Here
- Docs: Click Here
- Bootstrap Icons: Click Here
- Heroicons: Click Here
- For
components, use the component from the individual package not the from@nextui-org/react, some examples below:Button:import { Button } from '@nextui-org/button'
- Make sure not to use
/testfor testing client side components for, it causesoriginalFactory is not defined error.
To learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!