with one click
framework-to-capacitor
// Guide for integrating modern web frameworks with Capacitor. Covers Next.js static export, React, Vue, Angular, Svelte, and others. Use this skill when converting framework apps to mobile apps with Capacitor.
// Guide for integrating modern web frameworks with Capacitor. Covers Next.js static export, React, Vue, Angular, Svelte, and others. Use this skill when converting framework apps to mobile apps with Capacitor.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | framework-to-capacitor |
| description | Guide for integrating modern web frameworks with Capacitor. Covers Next.js static export, React, Vue, Angular, Svelte, and others. Use this skill when converting framework apps to mobile apps with Capacitor. |
| allowed-tools | ["Bash(node -e *)","Bash(find *)"] |
Comprehensive guide for integrating web frameworks with Capacitor to build mobile apps.
Detected framework and build dependencies:
!node -e "const fs=require('fs');if(!fs.existsSync('package.json'))process.exit(0);const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));const matchers=['next','react','vue','@angular/core','@sveltejs/kit','@builder.io/qwik','@remix-run/react','solid-js','vite','@capacitor/core','@capacitor/cli'];const out=[];for(const section of ['dependencies','devDependencies']){for(const [name,version] of Object.entries(pkg[section]||{})){if(matchers.includes(name))out.push(section+'.'+name+'='+version)}}for(const [name,cmd] of Object.entries(pkg.scripts||{})){if(['build','export','sync','cap:sync'].includes(name))out.push('scripts.'+name+'='+cmd)}console.log(out.join('\n'))"
Relevant framework and Capacitor config paths:
!find . -maxdepth 3 \( -name 'next.config.js' -o -name 'next.config.mjs' -o -name 'vite.config.ts' -o -name 'vite.config.js' -o -name 'angular.json' -o -name 'svelte.config.js' -o -name 'capacitor.config.json' -o -name 'capacitor.config.ts' -o -name 'capacitor.config.js' \)
| 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 is popular for React apps. Capacitor requires static export.
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,
};
npm run build
This creates an out/ directory with static files.
npm install @capacitor/core @capacitor/cli
npx cap init
Configuration:
out (Next.js static export output)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;
npm install @capacitor/ios @capacitor/android
npx cap add ios
npx cap add android
# Build Next.js
npm run build
# Sync with native projects
npx cap sync
iOS:
npx cap open ios
# Build and run in Xcode
Android:
npx cap open android
# Build and run in Android Studio
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/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} />;
};
API routes don't work in static export. Use alternatives:
@capacitor/preferencesimport { 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 || '{}');
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>;
}
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 works great with Capacitor using Vite or Create React App.
Create new project:
npx create-vite@latest my-app --template react-ts
cd my-app
npm install
Install Capacitor:
npm install @capacitor/core @capacitor/cli
npx 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:
npm install @capacitor/ios @capacitor/android
npx cap add ios
npx cap add android
npm run build
npx cap sync
Create new project:
npx create-react-app my-app --template typescript
cd my-app
Install Capacitor:
npm install @capacitor/core @capacitor/cli
npx cap init
capacitor.config.ts:
const config: CapacitorConfig = {
appId: 'com.company.app',
appName: 'My App',
webDir: 'build', // CRA outputs to build/
};
Build and sync:
npm run build
npx cap sync
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 works seamlessly with Capacitor.
Using Vite:
npx create-vite@latest my-app --template vue-ts
cd my-app
npm install
Install Capacitor:
npm install @capacitor/core @capacitor/cli
npx 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:
npm install @capacitor/ios @capacitor/android
npx cap add ios
npx cap add android
npm run build
npx cap sync
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 has excellent Capacitor integration.
Create Angular app:
npx @angular/cli new my-app
cd my-app
Install Capacitor:
npm install @capacitor/core @capacitor/cli
npx 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:
npm install @capacitor/ios @capacitor/android
npx cap add ios
npx cap add android
npm run build
npx cap sync
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 and SvelteKit work great with Capacitor.
Create SvelteKit app:
npx create-svelte my-app
cd my-app
npm install
Install adapter-static:
npm install -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:
npm install @capacitor/core @capacitor/cli
npx cap init
capacitor.config.ts:
const config: CapacitorConfig = {
appId: 'com.company.app',
appName: 'My App',
webDir: 'build',
};
Build and sync:
npm run build
npx cap sync
Create with Vite:
npx create-vite@latest my-app --template svelte-ts
cd my-app
npm install
Install Capacitor:
npm install @capacitor/core @capacitor/cli
npx cap init
capacitor.config.ts:
const config: CapacitorConfig = {
appId: 'com.company.app',
appName: 'My App',
webDir: 'dist',
};
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
}
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
});
Add live updates to any framework:
npm install @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 });
Use Ionic Framework for any framework:
npm install @ionic/core
React:
npm install @ionic/react @ionic/react-router
Vue:
npm install @ionic/vue @ionic/vue-router
Angular:
npm install @ionic/angular
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();
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;
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"
}
}
Hash mode (recommended for mobile):
#/aboutHistory mode (requires server):
/aboutRecommendation: Use hash mode for Capacitor apps.
Cause: Incorrect webDir or build output.
Solution:
webDir in capacitor.config.tsnpm run buildnpx cap syncCause: Using history mode without proper configuration.
Solution: Switch to hash routing:
HashRoutercreateWebHashHistory()HashLocationStrategyCause: Build-time variables not being replaced.
Solution: Use framework-specific env variable patterns:
NEXT_PUBLIC_VITE_REACT_APP_environment.tsCause: CORS or localhost URLs.
Solution:
import { CapacitorHttp } from '@capacitor/core';
const response = await CapacitorHttp.get({
url: 'https://api.example.com/data',
});
Ionic Framework provides native UI components:
Konsta UI for Tailwind CSS:
See ionic-design and konsta-ui skills for details.
output: 'export')webDir in capacitor.config.tsFor detailed guides on specific frameworks:
ionic-design skillkonsta-ui skill