In questo tutorial, partiremo da una nuova React app React e passeremo allo sviluppo mobile nativo con Capacitor. Puoi anche aggiungere Capgo Native Navigation and Transitions per un feeling nativo, e usare tailwind-capacitor per le safe areas.
Capacitor ti permette di convertire facilmente la tua web app React in un'app mobile nativa senza modifiche sostanziali né dover imparare una nuova skill come React Native.
Con pochi semplici passaggi, la maggior parte delle applicazioni React può essere trasformata in app mobile.
Questo tutorial ti guiderà nel processo, partendo da una nuova app React e integrando Capacitor per passare al mondo delle app mobile native. Puoi anche usare Capgo Native Navigation, Transitions e tailwind-capacitor per le safe areas.
Informazioni su Capacitor
CapacitorJS cambia le regole del gioco! Puoi integrarlo senza sforzo in qualsiasi progetto web: incapsulerà la tua applicazione in una webview nativa, generando per te i progetti Xcode e Android Studio. Inoltre, i suoi plugin danno accesso alle funzionalità native del dispositivo, come la fotocamera, tramite un bridge JS.
Con Capacitor, ottieni una fantastica app mobile nativa senza configurazioni complicate né curve di apprendimento ripide. Il suo API leggero e le funzionalità snelle lo rendono semplicissimo da integrare nel tuo progetto. Fidati, rimarrai stupito da quanto sia facile ottenere un'app nativa pienamente funzionante con Capacitor!
Preparare la tua app React
Sebbene esistano vari modi per avviare applicazioni React, in questo tutorial utilizzeremo il metodo più semplice per ottenere un'applicazione React vuota:
npx create-react-app my-app
Per creare un'app mobile nativa, ci serve un export del nostro progetto. Quindi aggiungiamo uno script semplice nel nostro package.json da usare per buildare ed esportare il progetto React:
{
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
}
Ora puoi eseguire npm run build senza preoccupazioni; dovresti trovare una nuova cartella out nella root del tuo progetto.
Questa cartella verrà utilizzata da Capacitor in seguito, ma per ora dobbiamo configurarla correttamente.
Aggiungere Capacitor alla tua app React
Per impacchettare qualsiasi web app in un container mobile nativo, dobbiamo seguire alcuni passaggi iniziali, ma dopo basta eseguire un singolo sync comando.
Per prima cosa, possiamo installare Capacitor CLI come dev dependency e poi configurarlo nel progetto. Durante il setup, puoi premere “enter” per accettare i valori predefiniti di name e bundle ID.
Successivamente, dobbiamo installare il pacchetto core e i pacchetti necessari per le piattaforme iOS e Android.
Infine, possiamo aggiungere le piattaforme e Capacitor creerà le cartelle per ogni piattaforma nella root del progetto:
# Install the Capacitor CLI locally
npm install -D @capacitor/cli
# Initialize Capacitor in your React project
npx cap init
# Install the required packages
npm install @capacitor/core @capacitor/ios @capacitor/android
# Add the native platforms
npx cap add ios
npx cap add android
A questo punto, dovresti essere in grado di osservare nuovi iOS e Android cartelle nel tuo progetto React.
Sono veri progetti nativi!
Per accedere al progetto Android in seguito, devi installare Android Studio. Per iOS, ti serve un Mac e devi installare Xcode.
Inoltre, dovresti trovare un file capacitor.config.ts nel tuo progetto, che contiene alcune impostazioni fondamentali di Capacitor utilizzate durante la sync. L'unica cosa a cui prestare attenzione è webDir, che deve puntare al risultato del tuo comando di build. Attualmente, non è corretto.
Per risolvere, apri il file capacitor.config.json file e aggiornare il webDir:
{
"appId": "com.example.app",
"appName": "my-app",
"webDir": "out",
"bundledWebRuntime": false
}
Puoi provarlo eseguendo i seguenti comandi:
npm run build
npx cap sync
Il primo comando npm run build eseguirà semplicemente la build del tuo progetto React ed esporterà la build statica.
Mentre il secondo comando npx cap sync sincronizzerà tutti i code web nelle posizioni corrette delle piattaforme native, così da poterli visualizzare nell'app.
Inoltre, il comando sync potrebbe aggiornare le piattaforme native e installare i plugin, quindi quando installi un nuovo plugin Capacitor è il momento di eseguire npx cap sync ancora.
Senza accorgertene, hai finito: ora proviamo l'app su un dispositivo!
Build e deploy di app native
Per sviluppare app iOS, devi avere Xcode installato, mentre per le app Android devi avere Android Studio installato. Inoltre, se intendi distribuire la tua app sugli store, devi iscriverti all'Apple Developer Program per iOS e alla Google Play Console per Android.
Se sei alle prime armi con lo sviluppo mobile nativo, puoi usare Capacitor CLI per aprire facilmente entrambi i progetti nativi:
npx cap open ios
npx cap open android
Una volta configurati i tuoi progetti nativi, distribuire l'app su un dispositivo connesso è semplicissimo. In Android Studio, devi solo attendere che tutto sia pronto e puoi distribuire l'app su un dispositivo connesso senza modificare alcuna impostazione. Ecco un esempio:

In Xcode, devi configurare il tuo account di signing per distribuire l'app su un dispositivo reale invece che solo sul simulatore. Se non l'hai mai fatto, Xcode ti guiderà nel processo (ma ricorda che devi essere iscritto al Developer Program). Dopodiché, ti basterà premere play per avviare l'app sul dispositivo connesso, che puoi selezionare in alto. Ecco un esempio:

Congratulazioni! Hai deployato con successo la tua app web React su un dispositivo mobile. Ecco un esempio:
Ma aspetta, c'è anche un modo più veloce per farlo durante lo sviluppo...
Live Reload con Capacitor
A questo punto, probabilmente sei abituato all'hot reload con tutti i framework moderni, e la buona notizia è che puoi avere la stessa funzionalità su un dispositivo mobile con il minimo sforzo!
Abilita l'accesso alla tua applicazione ospitata localmente con il live reload sulla tua rete facendo caricare i contenuti all'app Capacitor tramite l'URL specifico.
Il primo passo è individuare il tuo indirizzo IP locale. Se usi un Mac, puoi trovarlo eseguendo questo comando nel terminale:
ipconfig getifaddr en0
Su Windows, esegui:
ipconfig
Poi cerca l'indirizzo IPv4.
Possiamo indicare a Capacitor di caricare l'app direttamente dal server aggiungendo un'altra voce al nostro capacitor.config.ts dell'app:
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.example.app',
appName: 'my-app',
webDir: 'out',
bundledWebRuntime: false,
server: {
url: 'http://192.168.x.xx:3000',
cleartext: true
}
};
export default config;
Assicurati di usare l'IP e la porta corretti, in questo esempio ho utilizzato la porta predefinita di React.
Ora possiamo applicare queste modifiche copiandole nel nostro progetto nativo:
npx cap copy
Il copy il comando è simile a sync, ma copierà solo le modifiche apportate alla cartella web e alla configurazione, senza aggiornare il progetto nativo.
Ora puoi distribuire di nuovo la tua app tramite Android Studio o Xcode. Dopo, se modifichi qualcosa nella tua app React, l'app si ricaricherà automaticamente e mostrerà le modifiche!
Tieni presente che se installi nuovi plugin come la camera, serve comunque un rebuild del progetto nativo. Questo perché i file nativi vengono modificati e l'operazione non può essere fatta al volo.
Nota che dovresti utilizzare l'IP e la porta corretti nella tua configurazione. Il blocco code sopra mostra la porta predefinita di React a scopo dimostrativo.
Utilizzo dei plugin Capacitor
Vediamo come usare in pratica un plugin Capacitor, di cui abbiamo già parlato un paio di volte. Per farlo, possiamo installare un plugin abbastanza semplice eseguendo:
npm i @capacitor/share
Non c'è niente di speciale in Plugin di condivisione, ma apre comunque il dialog di condivisione nativo! Per farlo, ora basta importare il pacchetto e chiamare la share() funzione dalla nostra app. Cambiamo src/App.js in questo modo:
import React from 'react';
import { Share } from '@capacitor/share';
function App() {
const share = async () => {
await Share.share({
title: 'Open Youtube',
text: 'Check new video on youtube',
url: 'https://www.youtube.com',
dialogTitle: 'Share with friends'
});
};
return (
<div>
<h1>Welcome to React and Capacitor!</h1>
<p>
<h2>Cool channel</h2>
<button onClick={() => share()}>Share now!</button>
</p>
</div>
);
}
export default App;
Come accennato prima, quando installi nuovi plugin devi eseguire un'operazione di sync e poi ridistribuire l'app sul dispositivo. Per farlo, esegui questo comando:
npx cap sync
Dopo aver cliccato il pulsante, potrai vedere il bellissimo dialog di condivisione nativo in azione!
Successivamente, puoi rendere l'app più nativa su iOS e Android con navigazione e transizioni Capgo, e risolvere i comuni problemi di layout su iOS che causano overflow orizzontale o safe area tagliate.
UI dall'aspetto nativo con Capgo Native Navigation and Transitions
Ho lavorato per anni con Ionic per creare applicazioni cross-platform, ma integrarlo con React è un workaround e raramente vale la pena quando hai già Tailwind CSS.
Per un'esperienza mobile nativa in un'app React + Capacitor, usa i plugin Capgo invece di UI kit solo web come Konsta UI:
- @capgo/capacitor-native-navigation — navbar nativa, tab bar Liquid Glass su iOS e stile tab bar sfocato su Android. Il tuo router React gestisce lo stato delle route; il plugin gestisce la chrome nativa.
- @capgo/capacitor-transitions — transizioni di pagina in stile Ionic e swipe-back dal bordo su iOS nel layer WebView, senza adottare Ionic UI.
Installa entrambi:
bun add @capgo/capacitor-native-navigation @capgo/capacitor-transitions
bunx cap sync
Configura la navigazione nativa con la modalità CSS inset, così il contenuto web rispetta le barre native:
import { NativeNavigation } from '@capgo/capacitor-native-navigation';
await NativeNavigation.configure({
contentInsetMode: 'css',
animationDuration: 360,
glass: {
effect: 'liquidGlass',
},
});
Renderizza una tab bar Liquid Glass (iOS usa il rendering di sistema; Android usa un backdrop WebView sfocato):
await NativeNavigation.setTabbar({
selectedId: 'home',
labelVisibilityMode: 'labeled',
icons: true,
colors: { dynamic: true },
tabs: [
{ id: 'home', title: 'Home', icon: { svg: '...' } },
{ id: 'settings', title: 'Settings', icon: { svg: '...' } },
],
});
await NativeNavigation.addListener('tabSelect', ({ id }) => {
navigate(`/${id}`);
});
Aggiungi transizioni di pagina native nell'app shell:
import { useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import '@capgo/capacitor-transitions';
import { initTransitions, setDirection, setupRouterOutlet } from '@capgo/capacitor-transitions/react';
initTransitions({ platform: 'auto' });
export function AppShell() {
const navigate = useNavigate();
const outletRef = useRef<HTMLElement>(null);
useEffect(() => {
if (outletRef.current) {
setupRouterOutlet(outletRef.current, { platform: 'auto', swipeGesture: 'auto' });
}
}, []);
const openSettings = () => {
setDirection('forward');
navigate('/settings');
};
return <cap-router-outlet ref={outletRef}>{/* routes */}</cap-router-outlet>;
}
Avvolgi le pagine del router in cap-router-outlet, cap-page, e cap-content, e chiama setDirection('forward') oppure setDirection('back') prima di navigare. Non duplicare header o footer web quando la navigazione nativa gestisce quelle superfici.
Guarda le guide complete: Usando @capgo/capacitor-native-navigation e Uso di @capgo/capacitor-transitions.
Safe areas con Tailwind
Per le safe area dei dispositivi in Tailwind CSS, usa @capgo/tailwind-capacitor (pubblicato come tailwind-capacitor su npm). Offre safe-areas utility e altri plugin Tailwind compatibili con Capacitor:
bun add -D tailwind-capacitor
In src/index.css:
@import 'tailwindcss';
@plugin "@capgo/tailwind-capacitor/platform";
@plugin "@capgo/tailwind-capacitor/safe-areas";
Usa utility come pt-safe, pb-safe, e px-safe invece di sparpagliare env(safe-area-inset-*) a mano. Il progetto è in costante sviluppo — se manca qualcosa per il tuo setup React, apri una PR su GitHub.
Risolvere problemi di layout su iOS (Viewport, Safe Area e overflow orizzontale)
Se i contenuti appaiono tagliati, spostati o con scroll orizzontale su iOS, aggiungere più overflow-x: hidden o modificare il tag viewport di solito non basta. Segui questi controlli in ordine.
Assicurati che il meta tag viewport sia applicato correttamente
Aggiungi il meta tag viewport in index.html dentro <head>:
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
Gestisci la safe area di iOS da un unico wrapper root
Crea un unico app shell e applica lì il padding per la safe area — non in più componenti annidati:
html,
body,
#root {
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);
}
Racchiudi tutto il contenuto della pagina in .app-shell. Il padding della safe-area duplicato in header, modal e wrapper del layout spesso fa apparire l'interfaccia tagliata o troppo grande.
Con @capgo/tailwind-capacitor, puoi applicare lo stesso padding con utility come pt-safe pb-safe px-safe su quella singola shell.
Imposta Capacitor iOS contentInset a never per primo
In capacitor.config.ts, preferisci disabilitare i native inset e lascia che il CSS (o quella contentInsetMode: 'css'di Native Navigation) gestisca la safe area:
const config: CapacitorConfig = {
appId: 'com.example.myapp',
appName: 'my-app',
webDir: 'dist',
ios: {
contentInset: 'never',
},
};
Mescolare l'automatic content inset di Capacitor con il CSS env(safe-area-inset-*) il padding è una causa comune di spaziature doppie.
Trova il vero elemento che causa l'overflow
Il colpevole di solito è un elemento che usa 100vw, Tailwind w-screen, una larghezza fissa in pixel o un grande min-width.
In Safari Web Inspector, esegui:
[...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,
}));
Con Tailwind, sostituisci w-screen con w-full quando possibile. Molti problemi di overflow orizzontale derivano da 100vw / w-screen, padding della safe-area duplicato o un contenitore a larghezza fissa — non dal meta tag viewport stesso."
Conclusione
Capacitor è un'ottima opzione per creare applicazioni native partendo da un progetto web esistente, offrendo un modo semplice per condividere code e mantenere una UI coerente.
E con l'aggiunta di Capgo, è ancora più facile aggiungere live updates alla tua app, così i tuoi utenti hanno sempre accesso alle ultime funzionalità e ai bug fix.
Se vuoi imparare come aggiungere Capgo alla tua app React, dai un'occhiata al prossimo articolo:
Continua con Building Mobile Apps with React e Capacitor
Se stai usando Sviluppare app mobile con React e Capacitor per pianificare l'automazione CI/CD e connetterla a CI/CD con Capgo per il workflow del prodotto in Capgo CI/CD, Capgo Native Build per il workflow di prodotto in Capgo Native Builds, Integrazioni Capgo per il workflow di prodotto in Integrazioni Capgo, Integrazione CI/CD per i dettagli implementativi in CI/CD Integration, e Integrazione GitHub Actions per i dettagli implementativi in GitHub Actions Integration.