Einführung
Möchtest du eine mobile App mit Next.js von Grund auf neu entwickeln? Dieser Guide zeigt dir, wie du ein Next.js 15 Projekt direkt für Mobile konfigurierst und es anschließend als native iOS- und Android-Apps verpackst mit Capacitor 8.
Am Ende dieses Tutorials hast du eine funktionierende mobile App auf Simulatoren laufen, die du weiterentwickeln und schließlich im App Store und Google Play veröffentlichen kannst.
Zeitaufwand: ~30 Minuten
Das baust du:
- Ein neues Next.js-15-Projekt mit App Router
- Static-Export-Konfiguration für Mobile
- Capacitor 8 mit essenziellen Plugins
- Native iOS- und Android-Apps
- Live-Reload-Setup für die Entwicklung
Schon eine Next.js App? Dann schau dir das an: Konvertiere deine Next.js App in eine Mobile App stattdessen.
Voraussetzungen
Stelle sicher, dass Folgendes installiert ist:
- Node.js 18+ (prüfen mit
node --version) - Bun Package Manager (
curl -fsSL https://bun.sh/install | bash) - Xcode (nur macOS, für iOS-Entwicklung)
- Android Studio (für Android-Entwicklung)
Schritt 1: Neues Next.js-Projekt erstellen
Erstelle zuerst ein neues Next.js 15 Projekt:
bunx create-next-app@latest my-mobile-app
Wähle bei den Prompts diese Optionen:
- TypeScript: Ja (empfohlen)
- ESLint: Ja
- Tailwind CSS: Ja (empfohlen für mobiles Styling)
src/Verzeichnis: Ja- App Router: Ja (empfohlen)
- Import-Alias: Standard (
@/*)
Navigiere zu deinem Projekt:
cd my-mobile-app
Schritt 2: Next.js für den Static Export konfigurieren
Capacitor benötigt statische HTML/JS/CSS-Dateien. Konfiguriere Next.js für den Static Export, indem du next.config.ts:
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'export',
images: {
unoptimized: true,
},
// Ensure trailing slashes for proper routing in Capacitor
trailingSlash: true,
};
export default nextConfig;
Warum diese Einstellungen?
output: 'export'— Erzeugt statisches HTML und braucht keinen Node.js-Serverimages: { unoptimized: true }— Deaktiviert die Next.js Image Optimization (benötigt einen Server)trailingSlash: true— Sorgt für korrektes Routing im nativen WebView
Schritt 3: Mobile Scripts hinzufügen
Aktualisiere dein package.json mit Scripts für die mobile Entwicklung:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"mobile": "bun run build && bunx cap sync",
"mobile:ios": "bun run mobile && bunx cap open ios",
"mobile:android": "bun run mobile && bunx cap open android"
}
}
Teste den Build:
bun run build
Du solltest einen out Verzeichnis mit deinen statischen Dateien.
Schritt 4: Capacitor 8 installieren
Installiere die Capacitor Core-Packages:
bun add @capacitor/core
bun add -D @capacitor/cli
Installiere die wichtigsten Plugins für mobile Apps:
bun add @capacitor/app @capacitor/keyboard @capacitor/splash-screen @capacitor/status-bar @capacitor/preferences
Was diese Plugins machen:
- @capacitor/app — App-Lifecycle-Events (Foreground/Background, Deep Links)
- @capacitor/keyboard — Tastaturverhalten steuern
- @capacitor/splash-screen — Native Splash-Screen steuern
- @capacitor/status-bar — Styling der Statusleiste
- @capacitor/preferences — Key-Value-Storage (wie localStorage, aber nativ)
Schritt 5: Capacitor initialisieren
Initialisiere Capacitor mit deinen Projektdaten:
bunx cap init "My Mobile App" com.example.mymobileapp --web-dir out
Ersetze:
"My Mobile App"durch den Anzeigenamen deiner Appcom.example.mymobileappdurch deine App-ID (Reverse-Domain-Notation)
Dadurch entstehen capacitor.config.ts. Aktualisiere sie mit der Plugin-Konfiguration:
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.mymobileapp',
appName: 'My Mobile App',
webDir: 'out',
plugins: {
SplashScreen: {
launchShowDuration: 2000,
launchAutoHide: true,
androidScaleType: 'CENTER_CROP',
splashFullScreen: true,
splashImmersive: true,
},
Keyboard: {
resize: 'body',
resizeOnFullScreen: true,
},
StatusBar: {
style: 'light',
},
},
};
export default config;
Schritt 6: Native Plattformen hinzufügen
Installiere die Plattform-Packages:
bun add @capacitor/ios @capacitor/android
Native Projekte generieren:
bunx cap add ios
bunx cap add android
Dadurch entstehen ios und android Verzeichnisse mit den nativen Projekten.
Schritt 7: Build und Run
Baue dein Projekt und synchronisiere es mit den nativen Plattformen:
bun run mobile
Im iOS Simulator öffnen:
bun run mobile:ios
Oder Android Emulator:
bun run mobile:android
In Xcode (iOS):
- Wähle einen Simulator aus dem Geräte-Dropdown
- Klicke auf den Play-Button oder drücke
Cmd + R
In Android Studio:
- Warten, bis Gradle das Syncing beendet hat
- Wähle einen Emulator aus dem Device-Dropdown
- Klicke auf Run oder drücke
Shift + F10
Schritt 8: Live Reload einrichten
Aktiviere Live Reload für schnelleres Entwickeln, damit Änderungen sofort auf deinem Device erscheinen.
- Ermitteln Sie Ihre lokale IP-Adresse:
# macOS
ipconfig getifaddr en0
# Windows
ipconfig
- Erstelle eine Capacitor Dev-Config. Füge sie zu
capacitor.config.ts:
import type { CapacitorConfig } from '@capacitor/cli';
const devConfig: CapacitorConfig = {
appId: 'com.example.mymobileapp',
appName: 'My Mobile App',
webDir: 'out',
server: {
url: 'http://YOUR_IP_ADDRESS:3000',
cleartext: true,
},
plugins: {
// ... same plugin config
},
};
const prodConfig: CapacitorConfig = {
appId: 'com.example.mymobileapp',
appName: 'My Mobile App',
webDir: 'out',
plugins: {
// ... same plugin config
},
};
const config = process.env.NODE_ENV === 'development' ? devConfig : prodConfig;
export default config;
- Starte den Dev-Server und kopiere die Config zum Native-Projekt:
bun run dev &
NODE_ENV=development bunx cap copy
- In Xcode/Android Studio neu builden
Änderungen an deinem Next.js code werden nun per Hot-Reload auf dem Device angezeigt.
Schritt 9: Deinen ersten mobilen Screen erstellen
Erstellen wir einen einfachen, mobile-optimierten Home-Screen. Update src/app/page.tsx:
'use client';
import { useEffect, useState } from 'react';
import { App } from '@capacitor/app';
import { Keyboard } from '@capacitor/keyboard';
export default function Home() {
const [appInfo, setAppInfo] = useState<{ name: string; version: string } | null>(null);
useEffect(() => {
// Get app info on mount
App.getInfo().then(setAppInfo).catch(console.error);
// Handle back button on Android
const backHandler = App.addListener('backButton', ({ canGoBack }) => {
if (!canGoBack) {
App.exitApp();
} else {
window.history.back();
}
});
// Hide keyboard when tapping outside inputs
const keyboardHandler = Keyboard.addListener('keyboardWillShow', () => {
document.body.classList.add('keyboard-open');
});
return () => {
backHandler.then(h => h.remove());
keyboardHandler.then(h => h.remove());
};
}, []);
return (
<main className="min-h-screen bg-linear-to-b from-blue-500 to-blue-700 flex flex-col items-center justify-center p-6 text-white">
<h1 className="text-4xl font-bold mb-4">My Mobile App</h1>
<p className="text-xl mb-8 text-center opacity-90">
Built with Next.js 15 + Capacitor 8
</p>
{appInfo && (
<div className="bg-white/20 rounded-lg p-4 backdrop-blur-sm">
<p className="text-sm">
{appInfo.name} v{appInfo.version}
</p>
</div>
)}
<div className="mt-12 space-y-4 w-full max-w-sm">
<button className="w-full py-4 px-6 bg-white text-blue-600 rounded-xl font-semibold text-lg shadow-lg active:scale-95 transition-transform">
Get Started
</button>
<button className="w-full py-4 px-6 bg-white/20 text-white rounded-xl font-semibold text-lg backdrop-blur-sm active:scale-95 transition-transform">
Learn More
</button>
</div>
</main>
);
}
Schritt 10: Safe Area Handling hinzufügen
Mobile Geräte haben Notches, Home-Indikatoren und Statusleisten. Füge Safe Area Handling mit Tailwind hinzu.
Update src/app/globals.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--sat: env(safe-area-inset-top);
--sar: env(safe-area-inset-right);
--sab: env(safe-area-inset-bottom);
--sal: env(safe-area-inset-left);
}
body {
padding-top: var(--sat);
padding-right: var(--sar);
padding-bottom: var(--sab);
padding-left: var(--sal);
}
/* Prevent text selection on mobile */
* {
-webkit-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
/* Allow text selection in inputs */
input, textarea {
-webkit-user-select: auto;
user-select: auto;
}
/* Keyboard handling */
.keyboard-open {
--sab: 0px;
}
Projektstruktur
Dein Projekt sollte nun so aussehen:
my-mobile-app/
├── android/ # Android native project
├── ios/ # iOS native project
├── out/ # Static build output
├── src/
│ ├── app/
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ └── ...
├── capacitor.config.ts # Capacitor configuration
├── next.config.ts # Next.js configuration
├── package.json
└── ...
Nächste Schritte
Du hast jetzt eine funktionierende Next.js-Mobile-App. Das sind die nächsten Schritte:
Grundlegendes Setup
- App-Icons: Ersetze die Standard-Icons in
ios/App/App/Assets.xcassetsundandroid/app/src/main/res - Splash Screen: In nativen Projekten anpassen oder nutzen:
@capacitor/splash-screenKonfiguration - Deep Links: URL-Schemes für deine App konfigurieren
Mehr Features hinzufügen
- Kamera:
bun add @capacitor/camera - Geolocation:
bun add @capacitor/geolocation - Push-Benachrichtigungen:
bun add @capacitor/push-notifications - Dateisystem:
bun add @capacitor/filesystem
Native UI und Übergänge
Nutze Capgo Plugins statt Konsta UI für ein natives Mobile-Feeling:
- @capgo/capacitor-native-navigation — Liquid Glass Tab-Bar und native Navbar
- @capgo/capacitor-transitions — Page Transitions, die sich nativ anfühlen
bun add @capgo/capacitor-native-navigation @capgo/capacitor-transitions
bunx cap sync
Für Tailwind Safe Areas füge hinzu @capgo/tailwind-capacitor:
bun add -D tailwind-capacitor
Siehe Verwendung von @capgo/capacitor-native-navigation, Verwendung von @capgo/capacitor-transitions, und die tailwind-capacitor Repo für das Next.js-spezifische Setup.
iOS-Layoutprobleme beheben (Viewport, Safe Area und horizontaler Overflow)
Wenn Inhalte auf iOS abgeschnitten, verschoben oder horizontal scrollbar wirken, hilft es meist nicht, mehr overflow-x: hidden hinzuzufügen oder nur das Viewport-Tag zu ändern. Gehe diese Checks der Reihe nach durch.
Stelle sicher, dass das Viewport-Meta-Tag korrekt angewendet wird
App Router (app/): export viewport von app/layout.tsx:
import type { Viewport } from 'next';
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
viewportFit: 'cover',
};
Pages Router (pages/): füge das Viewport-Meta-Tag in pages/_app.tsx, nicht _document.tsx.
iOS Safe Area nur von einem Root-Wrapper aus handhaben
Erstelle eine einzige App-Shell und wende dort das Safe-Area-Padding an – nicht in mehreren verschachtelten Komponenten:
html,
body,
#__next {
width: 100%;
min-height: 100%;
margin: 0;
padding: 0;
overflow-x: hidden;
}
* {
box-sizing: border-box;
}
.app-shell {
min-height: 100dvh;
width: 100%;
padding-top: env(safe-area-inset-top);
padding-right: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
}
Umschließe den gesamten Seiteninhalt mit .app-shellDoppeltes Safe-Area-Padding in Headern, Modals und Layout-Wrappern lässt das UI oft abgeschnitten oder zu groß wirken.
Mit @capgo/tailwind-capacitor, kannst du dasselbe Padding mit Utilities wie pt-safe pb-safe px-safe in dieser einen Shell ausdrücken.
Setze Capacitor iOS contentInset zu never zuerst
Unter capacitor.config.ts, deaktiviere lieber native Insets und überlasse CSS (oder der Native Navigation contentInsetMode: 'css') die Safe Area:
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'my-app',
webDir: 'out',
ios: {
contentInset: 'never',
},
};
Kombination von Capacitor’s automatischen Content Insets mit CSS env(safe-area-inset-*) Padding ist eine häufige Ursache für doppelte Abstände.
Das tatsächlich überlaufende Element finden
Der übliche Übeltäter ist ein Element mit 100vw, Tailwind w-screen, einer festen Pixelbreite oder einem großen min-width.
Im Safari Web Inspector ausführen:
[...document.querySelectorAll('*')]
.filter(el => el.scrollWidth > document.documentElement.clientWidth)
.map(el => ({
el,
tag: el.tagName,
class: el.className,
scrollWidth: el.scrollWidth,
clientWidth: document.documentElement.clientWidth,
}));
Mit Tailwind ersetzen w-screen mit w-full wann immer möglich. Viele Probleme mit horizontalem Overflow entstehen durch 100vw / w-screen, doppeltes Safe-Area-Padding oder einen Container mit fester Breite – nicht durch das Viewport-Meta-Tag selbst.
Over-the-Air Updates
Einrichten Capgo um Updates ohne erneute Einreichung im App Store zu pushen:
bunx @capgo/cli init
Fehlerbehebung
Build schlägt fehl: „Cannot find module“
Ausführen bun install und versuche es erneut.
iOS: „No signing identity found“ Öffne Xcode, gehe zu Signing & Capabilities und wähle dein Development Team aus.
Android: „SDK location not found“
Erstellen android/local.properties mit sdk.dir=/path/to/android/sdk
Änderungen werden nicht auf dem Gerät angezeigt
Stelle sicher, dass du ausgeführt hast bun run mobile nachdem du Änderungen vorgenommen hast. Für Live Reload prüfe, ob die IP-Adresse korrekt ist und der Dev-Server läuft.
Ressourcen
- Capacitor 8 Dokumentation
- Next.js 15 Dokumentation
- Capgo - Live Updates
- @capgo/capacitor-native-navigation
- @capgo/capacitor-transitions
- @capgo/tailwind-capacitor
Bereit für den App-Release? Erfahre, wie Capgo dir hilft, Updates schneller auszuliefern — Registrieren Sie sich für ein kostenloses Konto noch heute.
Weiter mit: Eine Next.js Mobile App von Grund auf mit Capacitor bauen 8
Wenn du es nutzt Next.js Mobile App von Grund auf bauen mit Capacitor 8 um CI/CD-Automatisierung zu planen, verbinde es mit Capgo CI/CD für den Produkt-Workflow in Capgo CI/CD, Capgo Native Builds optimieren für den Produkt-Workflow in Capgo Native Builds, Capgo Integrationen für den Produkt-Workflow in Capgo Integrations, CI/CD-Integration für die Implementierungsdetails in der CI/CD-Integration und GitHub Actions-Integration für die Implementierungsdetails in der GitHub Actions-Integration.