Aller au contenu principal
Tutoriel

Créer une application mobile Next.js de zéro avec Capacitor 8

Guide étape par étape pour créer un nouveau projet Next.js 15 et le transformer en applications mobiles natives iOS et Android avec Capacitor 8. Idéal pour repartir de zéro avec un développement mobile-first.

Créer une application mobile Next.js de zéro avec Capacitor 8 Capgo illustration de blog

Introduction

Vous voulez créer une application mobile avec Next.js en partant de zéro ? Ce guide vous accompagne dans la création d'un tout nouveau projet Next.js 15 configuré pour le mobile dès le départ, puis dans son packaging en applications natives iOS et Android en utilisant Capacitor 8.

À la fin de ce tutoriel, vous aurez une application mobile fonctionnelle qui tourne sur les simulateurs, que vous pourrez continuer à développer et finir par publier sur l'App Store et Google Play.

Temps requis : ~30 minutes

Ce que vous allez créer :

  • Un nouveau projet Next.js 15 avec App Router
  • Configuration de l'export statique pour le mobile
  • Capacitor 8 avec les plugins essentiels
  • Des apps natives iOS et Android
  • Configuration de développement avec live reload

Vous avez déjà une app Next.js ? Découvrez Convert Your Next.js App to Mobile plutôt.

Prerequisites

Assurez-vous d'avoir installé ceci :

  • Node.js 18+ (vérifier avec node --version)
  • Bun gestionnaire de paquets (curl -fsSL https://bun.sh/install | bash)
  • Xcode (macOS uniquement, pour le développement iOS)
  • Android Studio (pour le développement Android)

Étape 1 : créer un nouveau projet Next.js

Commencez par créer un nouveau projet Next.js 15 :

bunx create-next-app@latest my-mobile-app

Lorsqu'on vous le demande, sélectionnez ces options :

  • TypeScript : Oui (recommandé)
  • ESLint : Oui
  • Tailwind CSS : Oui (recommandé pour le style mobile)
  • src/ répertoire : Oui
  • App Router : Oui (recommandé)
  • Alias d'importation : Par défaut (@/*)

Accédez à votre projet :

cd my-mobile-app

Étape 2 : configurer Next.js pour l'export statique

Capacitor nécessite des fichiers HTML/JS/CSS statiques. Configurez l'export statique de Next.js en mettant à jour 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;

Pourquoi ces paramètres ?

  • output: 'export' — Génère du HTML statique au lieu de nécessiter un serveur Node.js
  • images: { unoptimized: true } — Désactive l'optimisation des images de Next.js (nécessite un serveur)
  • trailingSlash: true — Garantit un routage correct dans la WebView native

Étape 3 : Ajouter les scripts mobiles

Mettez à jour votre package.json avec les scripts de développement mobile :

{
  "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"
  }
}

Testez le build :

bun run build

Vous devriez voir un dossier out répertoire contenant vos fichiers statiques.

Étape 4 : Installer Capacitor 8

Installez les packages core Capacitor :

bun add @capacitor/core
bun add -D @capacitor/cli

Installez les plugins essentiels à la plupart des apps mobiles :

bun add @capacitor/app @capacitor/keyboard @capacitor/splash-screen @capacitor/status-bar @capacitor/preferences

Rôle de ces plugins :

  • @capacitor/app — Événements du cycle de vie (premier plan/arrière-plan, deep links)
  • @capacitor/keyboard — Contrôle du comportement du clavier
  • @capacitor/splash-screen — Contrôle du splash screen natif
  • @capacitor/status-bar — Personnaliser la barre d'état de l'appareil
  • @capacitor/preferences — Stockage clé-valeur (comme localStorage mais natif)

Étape 5 : Initialiser Capacitor

Initialisez Capacitor avec les détails de votre projet :

bunx cap init "My Mobile App" com.example.mymobileapp --web-dir out

Remplacer :

  • "My Mobile App" par le nom d’affichage de votre application
  • com.example.mymobileapp par l’ID de votre application (notation de domaine inversée)

Cela crée capacitor.config.ts. Mettez-l’à jour avec la configuration du plugin :

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;

Étape 6 : Ajouter les plateformes natives

Installer les packages de plateforme :

bun add @capacitor/ios @capacitor/android

Générer les projets natifs :

bunx cap add ios
bunx cap add android

Cela crée ios et android répertoires contenant les projets natifs.

Étape 7 : build et exécution

Compilez votre projet et synchronisez-l’avec les plateformes natives :

bun run mobile

Ouvrir dans le simulateur iOS :

bun run mobile:ios

Ou l'émulateur Android :

bun run mobile:android

Dans Xcode (iOS) :

  1. Sélectionnez un simulateur dans le menu déroulant des appareils
  2. Cliquez sur le bouton Play ou appuyez sur Cmd + R

Dans Android Studio :

  1. Attendez la fin de la synchronisation Gradle
  2. Sélectionnez un émulateur dans le menu des appareils
  3. Cliquez sur le bouton Run ou appuyez sur Shift + F10

Étape 8 : Configurer le live reload

For faster development, enable live reload so changes appear instantly on your device.

  1. Trouvez votre adresse IP locale :
# macOS
ipconfig getifaddr en0

# Windows
ipconfig
  1. Créez une config de développement Capacitor. Ajoutez à 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;
  1. Lancez le serveur de dev et copiez la config vers le natif :
bun run dev &
NODE_ENV=development bunx cap copy
  1. Recompilez dans Xcode/Android Studio

Now edits to your Next.js code will hot-reload on the device.

Étape 9 : Créer votre premier écran mobile

Créons un écran d'accueil simple et mobile-friendly. Mise à jour 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>
  );
}

Étape 10 : gérer les safe areas

Les appareils mobiles ont des encoches, un indicateur d'accueil et des barres d'état. Gérez les safe areas avec Tailwind.

Mise à jour 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;
}

Structure du projet

Votre projet devrait maintenant ressembler à ceci :

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
└── ...

Étapes suivantes

Vous avez maintenant une application mobile Next.js fonctionnelle. Voici la suite :

Configuration essentielle

  • Icônes de l'app : Remplacez les icônes par défaut dans ios/App/App/Assets.xcassets et android/app/src/main/res
  • Écran de démarrage : Personnalisez dans vos projets natifs ou utilisez @capacitor/splash-screen la config
  • Deep Links : Configurez les schémas d'URL de votre app

Ajoutez plus de fonctionnalités

  • Caméra : bun add @capacitor/camera
  • Géolocalisation : bun add @capacitor/geolocation
  • Notifications push : bun add @capacitor/push-notifications
  • Système de fichiers : bun add @capacitor/filesystem

UI et transitions natives

Utilisez les plugins Capgo au lieu de Konsta UI pour un rendu mobile natif :

bun add @capgo/capacitor-native-navigation @capgo/capacitor-transitions
bunx cap sync

Pour les safe areas Tailwind, ajoutez @capgo/tailwind-capacitor:

bun add -D tailwind-capacitor

Consultez Utilisation de @capgo/capacitor-native-navigation, Utiliser @capgo/capacitor-transitions, et le le repo tailwind-capacitor pour la configuration spécifique à Next.js.

Corriger les problèmes de mise en page iOS (viewport, zone sécurisée et débordement horizontal)

Si le contenu semble coupé, décalé ou défile horizontalement sur iOS, ajouter plus de overflow-x: hidden ou le simple ajustement de la balise viewport ne règle généralement pas le problème. Effectuez ces vérifications dans l'ordre.

Assurez-vous que la balise meta viewport est correctement appliquée

App Router (app/): export viewport depuis app/layout.tsx:

import type { Viewport } from 'next';

export const viewport: Viewport = {
  width: 'device-width',
  initialScale: 1,
  viewportFit: 'cover',
};

Pages Router (pages/): placez la balise meta viewport dans pages/_app.tsx, et non _document.tsx.

Gérez la safe area iOS depuis un seul wrapper racine

Créez un shell d'application unique et appliquez-y le padding de la safe area — pas dans plusieurs composants imbriqués :

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);
}

Enveloppez tout le contenu de la page dans .app-shell. Un padding de safe area dupliqué dans les headers, les modales et les wrappers de layout donne souvent l'impression que l'UI est rognée ou trop grande.

Avec @capgo/tailwind-capacitor, vous pouvez obtenir le même padding avec des utilitaires comme pt-safe pb-safe px-safe sur ce shell unique.

Configurez Capacitor iOS contentInset vers never d'abord

Dans capacitor.config.ts, préférez désactiver les insets natifs et laissez le CSS (ou la contentInsetMode: 'css') gèrent la safe area :

const config: CapacitorConfig = {
  appId: 'com.example.myapp',
  appName: 'my-app',
  webDir: 'out',
  ios: {
    contentInset: 'never',
  },
};

Combiner le content inset automatique de Capacitor avec le CSS env(safe-area-inset-*) le padding est une cause fréquente de double espacement.

Trouvez l'élément qui déborde réellement

Le coupable habituel est un élément utilisant 100vw, Tailwind w-screen, une largeur fixe en pixels, ou un grand min-width.

Dans le Web Inspector de Safari, lancez :

[...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,
  }));

Avec Tailwind, remplacez w-screen par w-full dès que possible. De nombreux problèmes de débordement horizontal proviennent de 100vw / w-screen, d'un padding safe-area dupliqué, ou d'un conteneur à largeur fixe — et non de la balise meta viewport elle-même.

Mises à jour OTA

Configuration Capgo pour envoyer des mises à jour sans repasser par le store :

bunx @capgo/cli init

Troubleshooting

Le build échoue avec « Cannot find module » Exécutez bun install et réessayez.

iOS : « No signing identity found » Ouvrez Xcode, allez dans Signing &amp; Capabilities et sélectionnez votre équipe de développement.

Android : « SDK location not found » Créer android/local.properties par sdk.dir=/path/to/android/sdk

Les changements n'apparaissent pas sur l'appareil Assurez-vous d'avoir exécuté bun run mobile après avoir effectué les modifications. Pour le live reload, vérifiez que l'adresse IP est correcte et que le serveur de dev est lancé.

Ressources

Prêt à déployer votre app ? Découvrez comment Capgo peut vous aider à livrer des mises à jour plus rapidement — créez un compte gratuit aujourd'hui.

Continuer à partir de « Créer une app mobile Next.js de zéro avec Capacitor 8 »

Si vous utilisez Créer une application mobile Next.js de zéro avec Capacitor 8 pour planifier l'automatisation CI/CD, et le connecter avec CI/CD Capgo pour le workflow produit dans le CI/CD de Capgo, Capgo Native Build pour le workflow produit dans les Native Builds de Capgo, Intégrations Capgo pour le workflow produit dans les intégrations de Capgo, Intégration CI/CD pour le détail d'implémentation dans l'intégration CI/CD, et Intégration GitHub Actions pour les détails d'implémentation de l'intégration GitHub Actions.

Live updates pour les apps Capacitor

Lorsqu'un bug de la couche web est en ligne, déployez le correctif via Capgo au lieu d'attendre des jours l'approbation des stores. Les utilisateurs reçoivent la mise à jour en arrière-plan, tandis que les changements natifs suivent le circuit de validation habituel.

Les derniers articles du blog

Capgo vous offre les meilleures informations pour créer une application mobile vraiment professionnelle.