- Framework to Capacitor Integration
- Comprehensive guide for integrating web frameworks with Capacitor to build mobile apps.
- When to Use This Skill
- Converting a Next.js app to a mobile app
- Integrating React, Vue, Angular, or Svelte with Capacitor
- Configuring static exports for Capacitor
- Setting up routing for mobile apps
- Optimizing framework builds for native platforms
- Framework Support Matrix
- Framework
- Static Export
- SSR Support
- Recommended Approach
- Next.js
- ✅ Yes
- ❌ No
- Static export (output: 'export')
- React
- ✅ Yes
- N/A
- Create React App or Vite
- Vue
- ✅ Yes
- ❌ No
- Vite or Vue CLI
- Angular
- ✅ Yes
- ❌ No
- Angular CLI
- Svelte
- ✅ Yes
- ❌ No
- SvelteKit with adapter-static
- Remix
- ✅ Yes
- ❌ No
- SPA mode
- Solid
- ✅ Yes
- ❌ No
- Vite
- Qwik
- ✅ Yes
- ❌ No
- Static site mode
- CRITICAL
-
- Capacitor requires
- static HTML/CSS/JS files
- . SSR (Server-Side Rendering) does not work in native apps.
- Next.js + Capacitor
- Next.js is popular for React apps. Capacitor requires static export.
- Step 1: Create or Update next.config.js
- For Next.js 13+ (App Router):
- // next.config.js
- /**
- @type
- {
- import
- (
- 'next'
- )
- .
- NextConfig
- }
- */
- const
- nextConfig
- =
- {
- output
- :
- 'export'
- ,
- images
- :
- {
- unoptimized
- :
- true
- ,
- // Required for static export
- }
- ,
- trailingSlash
- :
- true
- ,
- // Helps with routing on mobile
- }
- ;
- module
- .
- exports
- =
- nextConfig
- ;
- For Next.js 12 (Pages Router):
- // next.config.js
- module
- .
- exports
- =
- {
- output
- :
- 'export'
- ,
- images
- :
- {
- unoptimized
- :
- true
- ,
- }
- ,
- trailingSlash
- :
- true
- ,
- }
- ;
- Step 2: Build Static Files
- bun run build
- This creates an
- out/
- directory with static files.
- Step 3: Install Capacitor
- bun
- add
- @capacitor/core @capacitor/cli
- bunx cap init
- Configuration:
- App name
-
- Your app name
- App ID
- com.company.app Web directory : out (Next.js static export output) Step 4: Configure Capacitor Create capacitor.config.ts: import type { CapacitorConfig } from '@capacitor/cli' ; const config : CapacitorConfig = { appId : 'com.company.app' , appName : 'My App' , webDir : 'out' , // Next.js static export directory server : { androidScheme : 'https' , } , } ; export default config ; Step 5: Add Platforms bun add @capacitor/ios @capacitor/android bunx cap add ios bunx cap add android Step 6: Build and Sync
Build Next.js
bun run build
Sync with native projects
bunx cap sync Step 7: Run on Device iOS: bunx cap open ios
Build and run in Xcode
Android: bunx cap open android
Build and run in Android Studio
- Next.js Routing Considerations
- Use hash routing for complex apps:
- // next.config.js
- const
- nextConfig
- =
- {
- output
- :
- 'export'
- ,
- basePath
- :
- ''
- ,
- assetPrefix
- :
- ''
- ,
- }
- ;
- Or use Next.js's built-in routing
- (works with
- trailingSlash: true
- ).
- Next.js Image Optimization
- next/image doesn't work with static export. Use alternatives:
- Option 1: Use standard img tag
- // Instead of next/image
- <
- img
- src
- =
- "
- /images/photo.jpg
- "
- alt
- =
- "
- Photo
- "
- />
- Option 2: Use a custom Image component
- // components/CapacitorImage.tsx
- import
- {
- Capacitor
- }
- from
- '@capacitor/core'
- ;
- export
- const
- CapacitorImage
- =
- (
- {
- src
- ,
- alt
- ,
- ...
- props
- }
- )
- =>
- {
- const
- isNative
- =
- Capacitor
- .
- isNativePlatform
- (
- )
- ;
- const
- imageSrc
- =
- isNative
- ?
- src
- :
- src
- ;
- return
- <
- img
- src
- =
- {
- imageSrc
- }
- alt
- =
- {
- alt
- }
- {
- ...
- props
- }
- />
- ;
- }
- ;
- Next.js API Routes
- API routes don't work in static export.
- Use alternatives:
- External API
-
- Call a separate backend
- Capacitor plugins
-
- Use native features
- Local storage
- Use
@capacitor/preferences
import
{
Preferences
}
from
'@capacitor/preferences'
;
// Save data
await
Preferences
.
set
(
{
key
:
'user'
,
value
:
JSON
.
stringify
(
userData
)
,
}
)
;
// Load data
const
{
value
}
=
await
Preferences
.
get
(
{
key
:
'user'
}
)
;
const
userData
=
JSON
.
parse
(
value
||
'{}'
)
;
Next.js Middleware
Middleware doesn't work in static export.
Handle logic client-side:
// In your React components
import
{
useEffect
}
from
'react'
;
import
{
useRouter
}
from
'next/router'
;
export
default
function
ProtectedPage
(
)
{
const
router
=
useRouter
(
)
;
useEffect
(
(
)
=>
{
const
checkAuth
=
async
(
)
=>
{
const
{
value
}
=
await
Preferences
.
get
(
{
key
:
'token'
}
)
;
if
(
!
value
)
{
router
.
push
(
'/login'
)
;
}
}
;
checkAuth
(
)
;
}
,
[
]
)
;
return
<
div
Protected content < / div
; } Complete Next.js + Capacitor Example package.json: { "name" : "my-capacitor-app" , "scripts" : { "dev" : "next dev" , "build" : "next build" , "build:mobile" : "next build && cap sync" , "ios" : "cap open ios" , "android" : "cap open android" } , "dependencies" : { "next" : "^14.0.0" , "react" : "^18.2.0" , "react-dom" : "^18.2.0" , "@capacitor/core" : "^6.0.0" , "@capacitor/ios" : "^6.0.0" , "@capacitor/android" : "^6.0.0" , "@capacitor/camera" : "^6.0.0" } , "devDependencies" : { "@capacitor/cli" : "^6.0.0" , "typescript" : "^5.0.0" } } React + Capacitor React works great with Capacitor using Vite or Create React App. Option 1: Vite (Recommended) Create new project: bun create vite my-app --template react-ts cd my-app bun install Install Capacitor: bun add @capacitor/core @capacitor/cli bunx cap init Configure vite.config.ts: import { defineConfig } from 'vite' ; import react from '@vitejs/plugin-react' ; export default defineConfig ( { plugins : [ react ( ) ] , build : { outDir : 'dist' , // Capacitor webDir } , } ) ; capacitor.config.ts: import type { CapacitorConfig } from '@capacitor/cli' ; const config : CapacitorConfig = { appId : 'com.company.app' , appName : 'My App' , webDir : 'dist' , } ; export default config ; Add platforms and build: bun add @capacitor/ios @capacitor/android bunx cap add ios bunx cap add android bun run build bunx cap sync Option 2: Create React App Create new project: bunx create-react-app my-app --template typescript cd my-app Install Capacitor: bun add @capacitor/core @capacitor/cli bunx cap init capacitor.config.ts: const config : CapacitorConfig = { appId : 'com.company.app' , appName : 'My App' , webDir : 'build' , // CRA outputs to build/ } ; Build and sync: bun run build bunx cap sync React Router Configuration Use HashRouter for mobile: import { HashRouter as Router , Routes , Route } from 'react-router-dom' ; function App ( ) { return ( < Router
< Routes
< Route path = " / " element = { < Home /> } /> < Route path = " /about " element = { < About /> } /> </ Routes
</ Router
) ; } Vue + Capacitor Vue works seamlessly with Capacitor. Create Vue + Capacitor Project Using Vite: bun create vite my-app --template vue-ts cd my-app bun install Install Capacitor: bun add @capacitor/core @capacitor/cli bunx cap init vite.config.ts: import { defineConfig } from 'vite' ; import vue from '@vitejs/plugin-vue' ; export default defineConfig ( { plugins : [ vue ( ) ] , build : { outDir : 'dist' , } , } ) ; capacitor.config.ts: const config : CapacitorConfig = { appId : 'com.company.app' , appName : 'My App' , webDir : 'dist' , } ; Add platforms: bun add @capacitor/ios @capacitor/android bunx cap add ios bunx cap add android bun run build bunx cap sync Vue Router Configuration Use hash mode for mobile: // router/index.ts import { createRouter , createWebHashHistory } from 'vue-router' ; const router = createRouter ( { history : createWebHashHistory ( ) , routes : [ { path : '/' , component : Home } , { path : '/about' , component : About } , ] , } ) ; export default router ; Angular + Capacitor Angular has excellent Capacitor integration. Create Angular + Capacitor Project Create Angular app: bunx @angular/cli new my-app cd my-app Install Capacitor: bun add @capacitor/core @capacitor/cli bunx cap init capacitor.config.ts: const config : CapacitorConfig = { appId : 'com.company.app' , appName : 'My App' , webDir : 'dist/my-app/browser' , // Angular 17+ output } ; For Angular 16 and below: webDir : 'dist/my-app' , Add platforms: bun add @capacitor/ios @capacitor/android bunx cap add ios bunx cap add android bun run build bunx cap sync Angular Router Configuration HashLocationStrategy for mobile: // app.config.ts (Angular 17+) import { provideRouter , withHashLocation } from '@angular/router' ; export const appConfig : ApplicationConfig = { providers : [ provideRouter ( routes , withHashLocation ( ) ) , ] , } ; For Angular 16 and below: // app.module.ts import { LocationStrategy , HashLocationStrategy } from '@angular/common' ; @ NgModule ( { providers : [ { provide : LocationStrategy , useClass : HashLocationStrategy } ] , } ) export class AppModule { } Svelte + Capacitor Svelte and SvelteKit work great with Capacitor. SvelteKit + Capacitor Create SvelteKit app: bunx create-svelte my-app cd my-app bun install Install adapter-static: bun add -D @sveltejs/adapter-static Configure svelte.config.js: import adapter from '@sveltejs/adapter-static' ; const config = { kit : { adapter : adapter ( { pages : 'build' , assets : 'build' , fallback : 'index.html' , } ) , } , } ; export default config ; Install Capacitor: bun add @capacitor/core @capacitor/cli bunx cap init capacitor.config.ts: const config : CapacitorConfig = { appId : 'com.company.app' , appName : 'My App' , webDir : 'build' , } ; Build and sync: bun run build bunx cap sync Vite + Svelte (Simpler Option) Create with Vite: bun create vite my-app --template svelte-ts cd my-app bun install Install Capacitor: bun add @capacitor/core @capacitor/cli bunx cap init capacitor.config.ts: const config : CapacitorConfig = { appId : 'com.company.app' , appName : 'My App' , webDir : 'dist' , } ; Common Patterns Across Frameworks 1. Environment Detection Detect if running in native app: import { Capacitor } from '@capacitor/core' ; const isNative = Capacitor . isNativePlatform ( ) ; const platform = Capacitor . getPlatform ( ) ; // 'ios', 'android', or 'web' if ( isNative ) { // Use native plugins } else { // Use web APIs } 2. Deep Linking Handle deep links in your app: import { App } from '@capacitor/app' ; App . addListener ( 'appUrlOpen' , ( data ) => { // Handle deep link const slug = data . url . split ( '.app' ) . pop ( ) ; // Navigate to route } ) ; 3. Live Updates with Capgo Add live updates to any framework: bun add @capgo/capacitor-updater import { CapacitorUpdater } from '@capgo/capacitor-updater' ; // Check for updates const { id } = await CapacitorUpdater . download ( { url : 'https://api.capgo.app/updates' , } ) ; // Apply update await CapacitorUpdater . set ( { id } ) ; 4. Native UI Components Use Ionic Framework for any framework: bun add @ionic/core React: bun add @ionic/react @ionic/react-router Vue: bun add @ionic/vue @ionic/vue-router Angular: bun add @ionic/angular 5. Storage Use Capacitor Preferences for all frameworks: import { Preferences } from '@capacitor/preferences' ; // Set value await Preferences . set ( { key : 'theme' , value : 'dark' } ) ; // Get value const { value } = await Preferences . get ( { key : 'theme' } ) ; // Remove value await Preferences . remove ( { key : 'theme' } ) ; // Clear all await Preferences . clear ( ) ; 6. Camera Access Same API across all frameworks: import { Camera , CameraResultType } from '@capacitor/camera' ; const photo = await Camera . getPhoto ( { quality : 90 , allowEditing : true , resultType : CameraResultType . Uri , } ) ; const imageUrl = photo . webPath ; Build Scripts for All Frameworks Add these to package.json: { "scripts" : { "dev" : "vite" , // or next dev, ng serve, etc. "build" : "vite build" , // or next build, ng build, etc. "build:mobile" : "vite build && cap sync" , "ios" : "cap run ios" , "android" : "cap run android" , "sync" : "cap sync" } } Routing Best Practices Hash vs. History Mode Hash mode (recommended for mobile): Works without server configuration URLs look like:
/about
- No server-side routing needed
- History mode (requires server):
- Clean URLs:
- /about
- Requires server fallback to index.html
- Can have issues on mobile
- Recommendation
-
- Use hash mode for Capacitor apps.
- Common Issues and Solutions
- Issue: Blank Screen on Mobile
- Cause
-
- Incorrect
- webDir
- or build output.
- Solution:
- Check build output directory matches
- webDir
- in capacitor.config.ts
- Rebuild:
- bun run build
- Sync:
- bunx cap sync
- Check browser console in device
- Issue: Routing Doesn't Work
- Cause
-
- Using history mode without proper configuration.
- Solution:
- Switch to hash routing:
- React:
- HashRouter
- Vue:
- createWebHashHistory()
- Angular:
- HashLocationStrategy
- SvelteKit: Configure fallback
- Issue: Environment Variables Not Working
- Cause
-
- Build-time variables not being replaced.
- Solution:
- Use framework-specific env variable patterns:
- Next.js:
- NEXT_PUBLIC_
- Vite:
- VITE_
- Create React App:
- REACT_APP_
- Angular:
- environment.ts
- Issue: API Calls Fail on Device
- Cause
-
- CORS or localhost URLs.
- Solution:
- Use production API URLs
- Configure CORS on backend
- Use Capacitor HTTP plugin for native requests:
- import
- {
- CapacitorHttp
- }
- from
- '@capacitor/core'
- ;
- const
- response
- =
- await
- CapacitorHttp
- .
- get
- (
- {
- url
- :
- 'https://api.example.com/data'
- ,
- }
- )
- ;
- Framework-Specific Plugins
- Ionic Framework provides native UI components:
- @ionic/react
- - React components
- @ionic/vue
- - Vue components
- @ionic/angular
- - Angular components
- Konsta UI for Tailwind CSS:
- Works with React, Vue, Svelte
- iOS and Material Design themes
- See
- ionic-design
- and
- konsta-ui
- skills for details.
- Deployment Checklist
- Configure static export (Next.js:
- output: 'export'
- )
- Set correct
- webDir
- in capacitor.config.ts
- Use hash routing for mobile
- Disable image optimization (Next.js)
- Remove SSR/API routes dependencies
- Add native permissions (Info.plist, AndroidManifest.xml)
- Test on physical devices
- Configure splash screen and icons
- Set up live updates with Capgo (optional)
- Build and test on iOS and Android
- Resources
- Capacitor Docs
- :
- https://capacitorjs.com/docs
- Next.js Static Export
- :
- https://nextjs.org/docs/app/building-your-application/deploying/static-exports
- Ionic Framework
- :
- https://ionicframework.com
- Capgo Blog
- :
- https://capgo.app/blog
- Community Forum
- :
- https://forum.ionicframework.com
- Framework-Specific Guides
- For detailed guides on specific frameworks:
- Next.js + Capacitor
- :
- https://capgo.app/blog/how-to-use-capacitor-with-nextjs
- Ionic Framework
-
- See
- ionic-design
- skill
- Konsta UI
- See konsta-ui skill Next Steps Choose your framework and follow the setup above Configure static export/build Install and configure Capacitor Add platforms (iOS/Android) Build and sync Test on devices Add native features with plugins Set up live updates with Capgo