npx skills add ...
npx skills add cap-go/capgo-skills --skill framework-to-capacitor
Configures a web framework build so Capacitor can ship it as an iOS/Android app. Use for Next.js static export (output 'export', out/), Nuxt 4 (ssr false, nuxt generate, .output/public), Angular (dist/<project>/browser), SvelteKit adapter-static (SvelteKit 2 svelte.config.js or SvelteKit 3 vite.config), React/Vue/Svelte/Solid on Vite, and React Router framework SPA mode. Covers webDir mismatch, blank white screen after cap sync, missing index.html, SSR/API routes/middleware/server actions that cannot run in the app, next/image errors, deep-link reloads landing on the home page, router base paths, and env variables. Do not use for whole-app store readiness (webapp-to-capacitor), Cordova projects (cordova-to-capacitor), Capacitor major upgrades (capacitor-app-upgrades), or UI kits (ionic-design, konsta-ui, tailwind-capacitor).
npx skills add cap-go/capgo-skills --skill framework-to-capacitor
Make a framework emit a static bundle that Capacitor can copy into the native projects, and wire the scripts so build -> cap sync is one step.
TRIGGER when:
npx cap sync fails with The web assets directory (./dist) must contain an index.html file. or Could not find the web assets directory: ./dist.output: 'export' errors, prerender errors, API routes, middleware, server actions).Do not use for:
webapp-to-capacitor, capacitor-app-store, capacitor-apple-review-preflight.cordova-to-capacitor.capacitor-app-upgrades.capgo-live-updates.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','nuxt','react','react-router','@react-router/dev','vue','@angular/core','@angular/build','@sveltejs/kit','@sveltejs/adapter-static','@builder.io/qwik','@qwik.dev/core','astro','@remix-run/react','solid-js','@solidjs/start','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','generate','sync','cap:sync'].includes(name))out.push('scripts.'+name+'='+cmd)}console.log(out.join('\n'))"
Relevant framework and Capacitor config paths:
!find . -maxdepth 3 -not -path '*/node_modules/*' \( -name 'next.config.*' -o -name 'nuxt.config.*' -o -name 'vite.config.*' -o -name 'angular.json' -o -name 'svelte.config.*' -o -name 'react-router.config.*' -o -name 'astro.config.*' -o -name 'capacitor.config.json' -o -name 'capacitor.config.ts' -o -name 'capacitor.config.js' \)
webDir into the native app. It never runs Node. SSR, API routes, middleware, ISR, server actions, cookies()/headers(), and image optimization servers do not exist at runtime. Move them to a hosted backend and call it over HTTPS.capacitor://localhost (iOS) and https://localhost (Android). Your backend must allow those origins for CORS, or use CapacitorHttp.index.html for any path without a file extension. History-mode SPA routing (createWebHistory, BrowserRouter, Angular default PathLocationStrategy) works; hash routing is not required.location.reload() on /settings/ loads the root index.html. Keep navigation client-side and route deep links through App.addListener('appUrlOpen', ...) + your router./ (Vite base, Angular <base href="/">, Next basePath/assetPrefix unset). A CDN or sub-path base used for web hosting breaks asset loading in the app; use a separate mobile build config.NEXT_PUBLIC_*, VITE_*, NUXT_PUBLIC_*, PUBLIC_* in SvelteKit, Angular environment.ts). Rebuild before every cap sync; never point production builds at localhost.@next, not GA) needs Node 24+. Some framework majors also raise Node minimums (SvelteKit 3: Node 22.17+).| Framework in package.json | Load | webDir |
|---|---|---|
next | references/nextjs.md | out |
nuxt | references/vue-nuxt.md | .output/public |
vue + vite (no Nuxt) | references/vue-nuxt.md | dist |
react + vite, react-router/@react-router/dev, CRA | references/react.md | dist, build/client, or build |
@angular/core | references/angular.md | dist/<project>/browser |
@sveltejs/kit or svelte + vite | references/svelte.md | build or dist |
solid-js, @builder.io/qwik/@qwik.dev/core, astro, @remix-run/* | references/other-frameworks.md | varies |
app/api/, pages/api/, middleware.ts/proxy.ts, 'use server', getServerSideProps, +page.server.ts, +server.ts, server/api/, @angular/ssr). Report the list to the user and agree where each moves (external API, client fetch, or native plugin) before deleting anything.index.html at its root."build:mobile": "<framework build> && cap sync".Minimal config (androidScheme: 'https' is the default since Capacitor 6; only set it to keep an older origin):
Capacitor 8: npx cap run ios -l --host <LAN-IP> --port <port>. Capacitor 9: npx cap run ios --url http://<LAN-IP>:<port> (the -l/--host/--port/--https flags were merged into --url). The dev server must listen on the LAN (--host 0.0.0.0 / server.host: true in Vite). Never commit server.url into capacitor.config.*.
Then on device/simulator:
xcrun simctl openurl booted <url> / adb shell am start -a android.intent.action.VIEW -d <url>) and confirm it lands on the right screen.chrome://inspect) for 404s on JS/CSS and CORS errors.grep -rn "localhost:\|127.0.0.1" <webDir> returns nothing unexpected.| Symptom / error | Cause | Fix |
|---|---|---|
The web assets directory (./dist) must contain an index.html file. | webDir points at the wrong folder or SSR build has no index.html | Set webDir to the static output listed above; enable static/SPA mode |
Could not find the web assets directory: ./out. | Build not run or different output dir | Run the build first; match webDir to the real folder |
White screen, console Failed to load resource for /_next/... or /assets/... | Non-root base path / CDN assetPrefix | Use / base for the mobile build |
| White screen, no 404s | JS error at boot (often window/document during prerender, or SSR-only import) | Inspect WebView console; guard browser-only code |
| Reload or deep link opens home page or hydration error | Capacitor serves root index.html for extensionless paths | Client-side navigation + appUrlOpen handler; see framework reference |
Page "/x/[id]" is missing "generateStaticParams()" (Next.js) | Dynamic route in export mode | Pre-generate params or switch to a query-string/client route |
Image Optimization using the default loader is not compatible with output: export | next/image default loader | images.unoptimized: true or custom loader |
| API call works in browser, fails on device | CORS for capacitor://localhost / https://localhost, or localhost URL baked in | Allow origins on backend, or enable CapacitorHttp; fix env var |
references/nextjs.md - Next.js 14-16 static export, App Router traps, dynamic routes, images.references/react.md - Vite React, React Router framework SPA mode, CRA legacy.references/vue-nuxt.md - Vue + Vite, Nuxt 4 SPA generate.references/angular.md - Angular 17+ application builder output, SSR removal.references/svelte.md - SvelteKit 2 and 3 with adapter-static, plain Svelte + Vite.references/other-frameworks.md - Solid, Qwik, Astro, Remix v2.Related skills: webapp-to-capacitor (store readiness), capacitor-deep-linking, safe-area-handling, capgo-live-updates.
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.company.app',
appName: 'App Name',
webDir: 'dist',
};
export default config;npm run build
test -f <webDir>/index.html && echo "index.html OK"
npx cap sync
npx cap run ios # or open in Xcode
npx cap run android