Progressive Web Apps (PWAs) have become a powerful way to deliver fast, reliable, and engaging experiences across all platforms — web, mobile, and desktop — using standard web technologies. They combine the best of web and native apps, offering offline access, push notifications, and installability, without the need for app stores.
On the other hand, Capacitor, developed by the Ionic team, bridges the gap between web and native by allowing you to run your web app as a fully functional native app on Android, iOS, and desktop platforms. Capacitor provides access to native device APIs (camera, geolocation, file system, etc.) while maintaining a single codebase.
With the release of Nuxt 4, building performant, modern web apps has become even more seamless. Nuxt 4 introduces improvements in performance, stability, and developer experience, along with full support for modern SSR, hybrid rendering, and Vite-powered builds. Its modular architecture also makes it easy to integrate progressive enhancements like PWAs.
In this tutorial, you’ll learn step-by-step how to build a Progressive Web App using Nuxt 4 and Capacitor, enabling your app to run smoothly as both a web PWA and a native mobile app. You’ll start by setting up a fresh Nuxt 4 project, adding PWA support using the official @vite-pwa/nuxt module, and then wrapping it with Capacitor for mobile deployment.
By the end of this guide, you’ll have a working Nuxt 4 application that:
✅ Functions as a PWA with offline caching and manifest setup
✅ Can be installed on desktop or mobile home screens
✅ Runs as a native app using Capacitor on Android or iOS
Prerequisites
Before diving into building your Progressive Web App (PWA) with Nuxt 4 and Capacitor, make sure your development environment is properly set up. This ensures smooth installation, building, and deployment across both web and mobile platforms.
What You’ll Need
-
Basic Knowledge
-
Familiarity with Vue.js and Nuxt fundamentals (pages, components, and routing).
-
Understanding of basic web development tools (Node.js, npm, or yarn).
-
-
Installed Software
-
Node.js – version 20.x or later
Check your version:node -vIf you don’t have it, download it from nodejs.org.
-
npm (comes with Node.js) or yarn/pnpm for package management.
Example check:npm -v -
Capacitor CLI – the official command-line tool to add native platforms:
npm install -g @capacitor/cli -
Git – optional but recommended for version control.
-
-
Optional (for mobile builds)
-
Android Studio – to build and test on Android devices or emulators.
-
Xcode – for macOS users building iOS apps.
-
A physical or virtual mobile device – to test the app on native platforms.
-
-
Recommended Editor
-
Visual Studio Code with the Volar and TypeScript Vue Plugin extensions for the best Nuxt 4 experience.
-
Project Setup Overview
Here’s what we’ll do in this tutorial:
-
Create a Nuxt 4 app using the latest
nuxiCLI. -
Install and configure the PWA module (
@vite-pwa/nuxt). -
Add a Capacitor and integrate it with the Nuxt build output.
-
Build and test the PWA both on the web and on native devices.
Create a New Nuxt 4 Project
Let’s start by setting up a fresh Nuxt 4 project that will serve as the foundation for our Progressive Web App. Nuxt 4 uses the new nuxi CLI tool, which simplifies project creation and configuration.
Step 1: Create a New Nuxt 4 App
Open your terminal and run the following command to create a new Nuxt project:
npx nuxi init nuxt4-pwa-app
Then navigate into the project folder:
cd nuxt4-pwa-app
This command scaffolds a minimal Nuxt 4 app with TypeScript, Vite, and a ready-to-use directory structure.
Step 2: Install Dependencies
Once the project has been initialized, install all dependencies:
npm install
Or, if you prefer using Yarn or pnpm:
yarn install
# or
pnpm install
Step 3: Run the Development Server
Start the Nuxt development server to verify that everything works correctly:
npm run dev
You should see output similar to:
Nuxt 4 ready in 2.1s
Local: http://localhost:3000/
Open your browser at http://localhost:3000/ — you’ll see the default Nuxt 4 welcome screen.

Step 4: Project Structure Overview
After creation, your project structure will look like this:
nuxt4-pwa-app/
│
├─ .nuxt/ # Auto-generated build files
├─ node_modules/ # Installed dependencies
├─ public/ # Static assets (icons, manifest, etc.)
├─ pages/ # Application pages
├─ components/ # Vue components
├─ nuxt.config.ts # Nuxt 4 configuration file
├─ package.json # Project dependencies and scripts
└─ tsconfig.json # TypeScript configuration
We’ll modify these files in the next steps to add PWA features and integrate Capacitor.
✅ At this point, you have a working Nuxt 4 application ready to be enhanced with PWA capabilities.
Add and Configure the PWA Module
Now that your Nuxt 4 app is up and running, it’s time to make it a Progressive Web App (PWA). We’ll use the official @vite-pwa/nuxt module, which provides full integration between Nuxt 4 and the Vite PWA plugin.
This module automatically generates a web manifest, service worker, and handles offline caching — everything your app needs to be installable and work offline.
Step 1: Install the PWA Module
Run the following command inside your Nuxt project:
npm install @vite-pwa/nuxt
Step 2: Enable the PWA Module in nuxt.config.ts
Open your nuxt.config.ts file and add @vite-pwa/nuxt to the modules array:
// nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
modules: ['@vite-pwa/nuxt'],
compatibilityDate: '2025-07-15',
devtools: { enabled: true },
pwa: {
registerType: 'autoUpdate',
manifest: {
name: 'Nuxt 4 PWA App',
short_name: 'NuxtPWA',
description: 'A Progressive Web App built with Nuxt 4 and Capacitor',
theme_color: '#ffffff',
background_color: '#ffffff',
display: 'standalone',
start_url: '/',
icons: [
{
src: '/pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: '/pwa-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
},
workbox: {
cleanupOutdatedCaches: true,
clientsClaim: true
},
devOptions: {
enabled: true, // enable PWA during development
type: 'module'
}
},
nitro: {
preset: 'static' // ✅ ensures static files are generated
}
})
Step 3: Add PWA Icons
Add your app icons inside the /public directory.
For now, you can create placeholder icons or download sample ones:
public/
├─ pwa-192x192.png
└─ pwa-512x512.png
These icons will be referenced in your web manifest and used for the app installation prompt.
Step 4: Test the PWA
Run your development server again:
npm run dev
Then open http://localhost:3000/ in Chrome.
Open Developer Tools → Application → Manifest to verify:
-
A manifest file (
manifest.webmanifest) is detected. -
The service worker is registered under Service Workers.
-
The app is installable.
You can also click “Install” in the browser’s address bar to test installation.
✅ At this point, your Nuxt 4 application is fully PWA-enabled — complete with caching, offline access, and installability.
Add a Capacitor to the Project
Now that your Nuxt 4 app is a working PWA, it’s time to extend its reach to native mobile platforms using Capacitor. A capacitor allows you to run your web app as a native app on Android, iOS, and even desktop, while maintaining a single codebase.
In this section, you’ll install Capacitor, initialize it in your Nuxt project, and prepare it for mobile builds.
Step 1: Install Capacitor Packages
From your project root, install the Capacitor core libraries and CLI:
npm install @capacitor/core
npm install -D @capacitor/cli
Step 2: Initialize Capacitor
Next, initialize Capacitor to create its configuration files:
npx cap init
You’ll be prompted to enter a few details:
-
App name:
Nuxt 4 PWA App -
App ID: something unique like
com.djamware.nuxt4pwa
This creates two key files:
-
capacitor.config.ts– main Capacitor configuration file -
package.jsonupdates with@capacitor/cliscripts
Step 3: Configure the Build Output Directory
By default, Nuxt builds its production files into the .output/public directory.
We need to tell Capacitor to load that directory when building the native app.
Open capacitor.config.json and update it like this:
{
"appId": "com.djamware.nuxt4pwa",
"appName": "Nuxt 4 PWA App",
"webDir": ".output/public",
"bundledWebRuntime": false
}
This ensures Capacitor knows where your Nuxt production build files are located.
Step 4: Add a Build Script for Capacitor
In your package.json, add the following script under "scripts":
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"build:cap": "nuxt build && npx cap copy"
},
The build:cap command will:
-
Build the Nuxt app for production.
-
Copy the generated files into Capacitor’s native project directories.
Step 5: Add a Native Platform (Android or iOS)
You can now add a native platform:
npm install @capacitor/android
npx cap add android
# or
npm install @capacitor/ios
npx cap add ios
This command will create an android/ or ios/ folder in your project, each containing a complete native project ready to run in Android Studio or Xcode.
Step 6: Sync and Run the Project
After adding a platform, sync your web build with Capacitor:
npm run build:cap
Then open the native project in your IDE:
npx cap open android
# or
npx cap open ios
This will launch Android Studio or Xcode, where you can build and run your Nuxt PWA natively on an emulator or device.
✅ At this stage, your Nuxt 4 app is integrated with Capacitor and can run as a native Android or iOS application while still functioning as a PWA in browsers.
Integrate Capacitor with Nuxt Build
Now that your Nuxt 4 app can generate a proper static output, and Capacitor can successfully copy it to native platforms, let’s verify and fine-tune the integration so everything works seamlessly — both as a PWA in the browser and as a native app on Android or iOS.
Step 1: Verify the Build Output
Make sure Nuxt generates your static files correctly:
npm run build
After the build completes, confirm that the .output/public directory contains:
index.html
manifest.webmanifest
_sw.js (if generated by @vite-pwa/nuxt)
assets/
If you see these files, Nuxt has successfully produced a static version that Capacitor can use.
Step 2: Sync and Copy the Build to Capacitor
Run the following commands to sync and copy your web build into the native project folders:
npx cap sync
npx cap copy
These commands:
-
Ensure Capacitor recognizes your latest web build.
-
Copy the contents of
.output/publicto the corresponding Android/iOS project’swwwdirectory.
You should see output similar to:
✔ copy android - done in 1.2s
✔ copy ios - done in 0.9s
Step 3: Test the App in the Native Environment
Open your native project in its respective IDE:
npx cap open android
# or
npx cap open ios
This will launch Android Studio or Xcode.
From there:
-
Run the app on a simulator or connected device.
-
You should see your Nuxt 4 app rendered inside a native web view.
-
Check that offline access and installability still work (Capacitor includes the same PWA behavior by default).
Step 4: Automate the Nuxt–Capacitor Build Flow
To streamline your workflow, update your package.json with a new build script that runs all the necessary steps in one command:
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"build:cap": "nuxt build && npx cap sync && npx cap copy"
},
Now, you can just run:
npm run build:cap
to rebuild Nuxt, sync Capacitor, and update your native projects automatically.
Step 5: Handle Environment Differences (Web vs. Native)
Sometimes you may need to load different configurations depending on where the app runs (web or native). Capacitor exposes a simple API to detect this:
import { Capacitor } from '@capacitor/core';
if (Capacitor.isNativePlatform()) {
console.log('Running in a native app!');
} else {
console.log('Running on the web!');
}
You can use this logic to:
-
Adjust API endpoints.
-
Disable browser-only features.
-
Modify layouts or navigation for native builds.
Step 6: Confirm PWA + Native Coexistence
Your Nuxt app should now:
✅ Load normally as a PWA in browsers (with offline caching).
✅ Run as a native app with Capacitor on Android or iOS.
✅ Share a single codebase with platform-specific behavior when needed.
To verify everything works together:
-
Run
npm run devand test the web app’s PWA features. -
Run
npm run build:capand test the native app build.
Both should function identically, except for native integrations.
✅ At this stage, you’ve successfully integrated Capacitor with the Nuxt 4 build process — achieving a unified workflow for web, PWA, and native mobile deployment.
Build and Run on Android/iOS
Now that Capacitor and Nuxt 4 are fully integrated, it’s time to build, run, and test your Progressive Web App as a native mobile application on Android and iOS.
This section will guide you through the complete build process — from creating a production-ready web build to launching it inside a native environment.
Step 1: Build the Production Version
Start by generating the static build of your Nuxt 4 app that Capacitor will package into the native project:
npm run build
Then, sync the build with Capacitor to copy it into your platform projects:
npx cap sync
Alternatively, you can use your all-in-one command:
npm run build:cap
This ensures that the latest Nuxt build is deployed into your android/ or ios/ folders.
Step 2: Run the Android App
If you’ve already added Android as a platform (npx cap add android), open the project in Android Studio:
npx cap open android
In Android Studio:
-
Wait for Gradle to sync and build.
-
Select your test device or emulator.
-
Click Run ▶️ to build and launch the app.
Your Nuxt 4 app should appear as a native Android application, loading instantly just like a PWA — but now with full native capabilities such as access to the camera, file system, or notifications via Capacitor plugins.
Step 3: Run the iOS App
For macOS users with Xcode installed, open the iOS project:
npx cap open ios
Then in Xcode:
-
Choose a simulator (e.g., iPhone 15) or connect your physical device.
-
Click the Run button (▶).
-
Wait for the app to compile and install.
You’ll see the same Nuxt 4 interface running natively inside a WebView on iOS.
Step 4: Debugging and Live Reload (Optional)
Capacitor supports a development workflow where you can serve the app locally and view changes in real time on a connected device.
To enable live reload during development:
-
Run the Nuxt development server:
npm run dev -
Edit your
capacitor.config.jsontemporarily to point to your local dev server:{ "server": { "url": "http://YOUR_LOCAL_IP:3000", "cleartext": true } } -
Sync the project:
npx cap sync
Now, when you run your native app, it will load the Nuxt app directly from your dev server — any saved changes will instantly appear on your phone or simulator.
💡 Tip: Don’t forget to remove the
server.urlsection before creating production builds.
Step 5: Build and Ship to Stores (Optional)
Once your PWA + Capacitor app is stable and ready:
-
For Android: use Android Studio → Build → Generate Signed App Bundle to prepare an
.aabor.apkfor Google Play. -
For iOS: use Xcode → Product → Archive to upload your app to TestFlight or the App Store.
Your Nuxt 4 + Capacitor app can now live both as a web PWA and a published native app — powered by a single codebase.
✅ At this point, you’ve successfully built, run, and tested your Nuxt 4 PWA as a native app on Android and iOS.
Best Practices for PWA + Capacitor Apps
Combining Nuxt 3, PWA, and Capacitor gives you the best of both worlds — web performance and native-like experience. However, to ensure your app remains maintainable, efficient, and production-ready, follow these best practices:
1. Use a Unified Codebase
Keep your web and native app code in a single Nuxt project. Use platform detection (like Capacitor’s Capacitor.isNativePlatform()) to conditionally load native plugins only when running on a device.
import { Capacitor } from '@capacitor/core'
if (Capacitor.isNativePlatform()) {
// Use native plugins here
} else {
// Fallback for web
}
2. Always Test Both Web and Native Builds
Before every release:
-
Run
npm run devornpm run buildto ensure your PWA works perfectly in the browser. -
Run
npx cap sync androidornpx cap sync iosto confirm all native assets and configs are up to date.
3. Optimize for Offline Experience
Since your Nuxt PWA uses caching and service workers:
-
Cache critical assets and API responses with the Nuxt PWA module.
-
Gracefully handle offline states by showing cached content or a custom “You’re offline” page.
Example (using PWA module config in nuxt.config.ts):
pwa: {
workbox: {
navigateFallback: '/offline.html',
},
}
4. Manage Permissions Carefully
Capacitor plugins often require permissions (camera, location, etc.).
Always request them at runtime and only when needed to avoid unnecessary prompts and app store rejections.
Example for Camera plugin:
import { Camera } from '@capacitor/camera'
const photo = await Camera.getPhoto({
quality: 90,
allowEditing: false,
resultType: 'base64'
})
5. Use Environment Variables
Separate configurations for development, staging, and production using .env files and Nuxt’s runtime configuration:
// nuxt.config.ts
runtimeConfig: {
public: {
apiBase: process.env.API_BASE_URL || 'https://api.example.com'
}
}
Then in code:
const config = useRuntimeConfig()
console.log(config.public.apiBase)
6. Optimize App Performance
-
Enable lazy loading for components and routes.
-
Use image optimization via Nuxt Image module.
-
Keep bundle size small by importing only necessary Capacitor plugins.
7. Update Dependencies Regularly
Capacitor, Nuxt, and plugin ecosystems evolve quickly. Run:
npm outdated
and periodically update your dependencies to maintain compatibility and security.
8. Test with Real Devices
Emulators are good for initial testing, but real devices reveal platform-specific issues — especially around file systems, camera access, or push notifications.
9. Automate Builds
Integrate CI/CD pipelines (like GitHub Actions or GitLab CI) to automate:
-
Nuxt build
-
Capacitor sync
-
App store deployment
10. Keep a Consistent App Manifest
Your manifest.json should accurately reflect your PWA and native app identity:
{
"name": "Nuxt PWA App",
"short_name": "NuxtPWA",
"theme_color": "#00DC82",
"background_color": "#ffffff",
"display": "standalone",
"start_url": "/"
}
This ensures consistent branding across web and native versions.
✅ Pro Tip:
If your PWA already supports installation, make sure it gracefully transitions between web-installed and native-installed states by syncing user data via local storage or APIs.
Conclusion and Next Steps
You’ve now successfully transformed your Nuxt 3 app into a Progressive Web App (PWA) and integrated it with Capacitor, allowing it to run seamlessly as a native app on Android and iOS — all from a single codebase.
This setup gives you a modern, scalable architecture capable of serving both web users and mobile users without maintaining separate projects.
What You’ve Learned
By following this tutorial, you covered the full workflow of hybrid app development:
-
Created a Nuxt 3 app — using the latest version of Vue and Vite-powered builds.
-
Added the Nuxt PWA module — to enable offline caching, installability, and app manifest.
-
Configured and installed Capacitor — bridging your web app to native device capabilities.
-
Built and tested on Android/iOS — with working PWA and native versions.
-
Applied best practices — for offline support, environment separation, and performance.
Benefits of This Approach
-
🧩 Single Codebase — One project for web and mobile.
-
⚡ Fast Updates — Web updates automatically reflect in PWA; native builds just need sync.
-
📱 Native Features — Access camera, filesystem, push notifications, and more with Capacitor plugins.
-
🌐 Offline-First Experience — Works even without an internet connection.
Next Steps
Here’s how to extend your hybrid Nuxt app even further:
-
Add Native Features
Integrate Capacitor plugins like:-
@capacitor/geolocation(location tracking) -
@capacitor/haptics(vibration feedback) -
@capacitor/push-notifications(push messages)
-
-
Deploy Your Web App
Host your Nuxt PWA on:-
Vercel
-
Netlify
-
Cloudflare Pages
Example:
npm run build npm run generate vercel deploy -
-
Publish to App Stores
-
For Android: use Android Studio → Build → Generate Signed APK/AAB
-
For iOS: open the project in Xcode → Product → Archive → Distribute App
-
-
Automate with CI/CD
Use GitHub Actions or GitLab pipelines to automate Nuxt builds, Capacitor sync, and deployment steps. -
Monitor Performance
Track web and app analytics with Google Analytics 4, Firebase Analytics, or Vercel Insights.
Final Thoughts
By combining Nuxt 3, PWA, and Capacitor, you’ve built a future-ready app that merges the power of the web with the reach of native platforms.
This setup is perfect for startups, MVPs, or production-grade applications that need to serve users across multiple devices — efficiently and cost-effectively.
💡 Tip: Continue exploring Capacitor plugins and Nuxt’s ecosystem (such as Nuxt UI and Nuxt Image) to keep improving user experience and performance.
You can get the full source code on our GitHub.
That's just the basics. If you need more deep learning about Nuxt.js, you can take the following cheap course:
- Nuxt 3 & Supabase Mastery: Build 2 Full-Stack Apps
- Build Web Apps with Nuxt.js 3: Master TypeScript & API [New]
- The Nuxt 3 Bootcamp - The Complete Developer Guide
- Complete Nuxt.js Course (EXTRA React)
- The Complete NUXT 3 Bootcamp: Full-Stack Guide
- Nuxt 3 Authentication with Laravel Sanctum:A Practical Guide
- Learn How to Make Your Nuxt 3 App SEO-Friendly
- Headless Prestashop with Nuxt JS
Thanks!
