mcpField
F
framework-to-capacitor

framework-to-capacitor

github.com/cap-goDevelopment73876 installsUpdated 2w ago
Repository

About framework-to-capacitor

A development skill for adding Capacitor to projects built with frameworks such as Next.js, Nuxt, and Vite. It focuses on producing static assets, connecting build and sync steps, and resolving routing, deep-link, and server-runtime issues.

Key features

  1. 01Configure static framework builds
  2. 02Set the Capacitor webDir
  3. 03Link build and cap sync
  4. 04Troubleshoot routing and deep links
  5. 05Identify server-only runtime features

Use cases

  1. 01Fix cap sync errors about missing index.html
  2. 02Prepare framework output for a mobile app
  3. 03Investigate white screens or route errors after reload

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/cap-go/capgo-skills --skill framework-to-capacitor

After installing, type/framework-to-capacitorin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

metadata.json
references/
angular.md
nextjs.md
other-frameworks.md
react.md
svelte.md
vue-nuxt.md

Details

From the project README

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.

When to Use

TRIGGER when:

  • Adding Capacitor to a Next.js, Nuxt, Angular, SvelteKit, Vite (React/Vue/Svelte/Solid), React Router, Qwik, or Astro project.
  • 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.
  • The app launches to a white screen or loads the wrong page after a reload or deep link.
  • The framework build fails because of SSR-only features (output: 'export' errors, prerender errors, API routes, middleware, server actions).

Do not use for:

  • Store readiness, native UX, permissions, billing, review risk -> webapp-to-capacitor, capacitor-app-store, capacitor-apple-review-preflight.
  • Cordova/PhoneGap apps -> cordova-to-capacitor.
  • Capacitor version upgrades -> capacitor-app-upgrades.
  • Live updates setup -> capgo-live-updates.

Live Project Snapshot

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' \)

Core Facts (apply to every framework)

  • Capacitor copies 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.
  • The app is served from capacitor://localhost (iOS) and https://localhost (Android). Your backend must allow those origins for CORS, or use CapacitorHttp.
  • Capacitor's local server returns the root index.html for any path without a file extension. History-mode SPA routing (createWebHistory, BrowserRouter, Angular default PathLocationStrategy) works; hash routing is not required.
  • The same fallback means per-route HTML files (Next.js export, SvelteKit prerender, Astro) are never served for extensionless URLs. A cold start or location.reload() on /settings/ loads the root index.html. Keep navigation client-side and route deep links through App.addListener('appUrlOpen', ...) + your router.
  • Asset paths: keep the framework's base at / (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.
  • Environment variables are inlined at build time (NEXT_PUBLIC_*, VITE_*, NUXT_PUBLIC_*, PUBLIC_* in SvelteKit, Angular environment.ts). Rebuild before every cap sync; never point production builds at localhost.
  • Capacitor 8 needs Node 22+. Capacitor 9 (currently @next, not GA) needs Node 24+. Some framework majors also raise Node minimums (SvelteKit 3: Node 22.17+).

Procedure

  1. Read the snapshot. Identify framework, major version, and the current build output directory. If several apps live in a monorepo, ask which one ships to mobile.
  2. Load the matching reference (only the one in play):
Framework in package.jsonLoadwebDir
nextreferences/nextjs.mdout
nuxtreferences/vue-nuxt.md.output/public
vue + vite (no Nuxt)references/vue-nuxt.mddist
react + vite, react-router/@react-router/dev, CRAreferences/react.mddist, build/client, or build
@angular/corereferences/angular.mddist/<project>/browser
@sveltejs/kit or svelte + vitereferences/svelte.mdbuild or dist
solid-js, @builder.io/qwik/@qwik.dev/core, astro, @remix-run/*references/other-frameworks.mdvaries
  1. Inventory server-only features before changing config (grep for 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.
  2. Make the static build work and inspect the output directory: it must contain index.html at its root.
  3. Install and initialise Capacitor (use the repo's package manager for installs):
npm install @capacitor/core @capacitor/ios @capacitor/android
npm install -D @capacitor/cli
npx cap init "App Name" com.company.app --web-dir <webDir>
npx cap add ios
npx cap add android
  1. Add one script that always builds before syncing, e.g. "build:mobile": "<framework build> && cap sync".
  2. Run the verification below on both platforms.

Minimal config (androidScheme: 'https' is the default since Capacitor 6; only set it to keep an older origin):

import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.company.app',
  appName: 'App Name',
  webDir: 'dist',
};

export default config;

Live Reload

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.*.

Verification

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

Then on device/simulator:

  • App opens on the first screen without a white flash beyond the splash.
  • Navigate to a nested route, background + resume, and confirm the route survives.
  • Open a deep link (xcrun simctl openurl booted <url> / adb shell am start -a android.intent.action.VIEW -d <url>) and confirm it lands on the right screen.
  • Inspect the WebView (Safari Web Inspector / chrome://inspect) for 404s on JS/CSS and CORS errors.
  • grep -rn "localhost:\|127.0.0.1" <webDir> returns nothing unexpected.

Error Handling

Symptom / errorCauseFix
The web assets directory (./dist) must contain an index.html file.webDir points at the wrong folder or SSR build has no index.htmlSet 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 dirRun 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 assetPrefixUse / base for the mobile build
White screen, no 404sJS 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 errorCapacitor serves root index.html for extensionless pathsClient-side navigation + appUrlOpen handler; see framework reference
Page "/x/[id]" is missing "generateStaticParams()" (Next.js)Dynamic route in export modePre-generate params or switch to a query-string/client route
Image Optimization using the default loader is not compatible with output: exportnext/image default loaderimages.unoptimized: true or custom loader
API call works in browser, fails on deviceCORS for capacitor://localhost / https://localhost, or localhost URL baked inAllow origins on backend, or enable CapacitorHttp; fix env var

References

  • 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.

Comments

Markdown supported