Build Android/iOS Mobile Apps using Ionic 8, React (latest) & Capacitor

by Didin J. on Jun 18, 2025 Build Android/iOS Mobile Apps using Ionic 8, React (latest) & Capacitor

Build Android & iOS apps using Ionic 8, React 19+, and Capacitor 7 with REST API integration, React Hooks, and modern mobile tooling.

A comprehensive step-by-step tutorial on building cross-platform mobile apps (Android & iOS) using Ionic 8, the latest React.js, and Capacitor. You'll learn to create a modern React app with React Hooks, fetch data from a REST API, display nested JSON, and build native mobile apps using Capacitor—all updated for the current Ionic ecosystem

Updated tech stack:

  • Node.js (v18+ recommended)

  • Ionic CLI v8+

  • Ionic 8 Framework

  • React.js (v18+)

  • React Hooks

  • Axios

  • Capacitor 7

  • Android Studio / Xcode

  • REST API endpoint (e.g., openfootball JSON)


1. Create Ionic 8 + React App

Install or update the latest Ionic CLI:

sudo npm install -g @ionic/cli

Next, create a new Ionic application with the type of React using this command.

ionic start ionic-react tabs --type=react

If you have a problem installing the modules using NPM, you can use Yarn for that. Go to the newly created Ionic React Application, then type this command to install the module.

cd ./ionic-react
yarn install

Next, run Ionic React for the first time using this command.

ionic serve

Then the browser will open automatically to display this Ionic React Screen.

Build Android/iOS Mobile Apps using Ionic 8, React (latest) & Capacitor - Ionic React Home


2. Fetch & Display Nested JSON

As mentioned above, we will use Axios to fetch data from the REST API. To install the Axios library/module, simply run this command.

npm install --save axios

Fetching data and displaying it as a list will be done in Ionic React Tab 1. For that, open and edit `src/pages/Tab1.tsx` then replace all imports with these Ionic React IonList, IonItem, IonContent, IonHeader, IonTitle, IonToolbar, IonLabel, IonAvatar, IonLoading, React useState, useEffect, and Axios.

import {
  IonAvatar,
  IonContent,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonLoading,
  IonPage,
  IonTitle,
  IonToolbar
} from "@ionic/react";
import "./Tab1.css";
import { useEffect, useState } from "react";
import axios from "axios";

One of the problems while using React Hooks is that JSON data fetched from the REST API cannot be easily displayed to the Ionic React component. It must return a type that we will create an interface to handle. Next, add these lines of interface code before the `React.FunctionComponent`.

interface ITeam {
  name: string;
  code: string;
}

interface IMatches {
  num: number;
  date: string;
  time: string;
  team1: ITeam;
  team2: ITeam;
}

interface IRounds {
  name: string;
  matches: Array<IMatches>;
}

As you see, three interfaces are related to each other because we will fetch the nested arrays from the REST API. Next, add the `props` parameter to the Tab1 `React.FunctionComponent`.

const Tab1: React.FC = (props) => {
...
}

Next, declare the constant variables to handle data fetched from the RESTful API and handle the loading spinner by adding these lines at the top of Tab1's React.FunctionComponent`.

const [data, setData] = useState<IRounds[]>([]);
const [showLoading, setShowLoading] = useState(true);

To fetch data from the RESTful API using Axios, we have to do that by calling the `useEffect` function that comes with React Hooks. Add these lines of the `useEffect` function after the constant variable.

useEffect(() => {
  const fetchData = async () => {
    const result = await axios(
      'https://raw.githubusercontent.com/openfootball/world-cup.json/master/2018/worldcup.json',
    );
    setData(result.data.rounds);
    setShowLoading(false);
  };

  fetchData();
}, []);

Add this function to go to the details page, including with parameter from this Tab1 page.

const showDetail = (data: any) => {
  let prop: any = props;
  prop.history.push({
    pathname: '/tab1/details/' + JSON.stringify(data)
  })
}

As you see, the parameter data is all selected data from the list that has been converted to a string. Next, modify the Ionic React component to display the list of data that contains the nested JSON arrays.

return (
  <>
    <IonHeader>
      <IonToolbar>
        <IonTitle>World Cup 2018</IonTitle>
      </IonToolbar>
    </IonHeader>
    <IonContent>
      <IonLoading
        isOpen={showLoading}
        onDidDismiss={() => setShowLoading(false)}
        message={'Loading...'}
      />
      <IonList>
        {data.map((round, idx) => (
          <IonItem key={idx} onClick={() => { showDetail(round) }}>
            <IonAvatar slot="start">
              <img src="assets/imgs/ball.png" alt="ball" />
            </IonAvatar>
            <IonLabel>
              <h2>{round.name}</h2>
              {round.matches.map((im, idx2) => (
                <p key={idx2}>
                  <span>{im.date} {im.time}: {im.team1.name} vs {im.team2.name}</span>
                </p>
              ))}
            </IonLabel>
          </IonItem>
        ))}
      </IonList>
    </IonContent>
  </>
);


3. Create Details View

We will use the existing Details page that is linked to Tab2. For that, we have to modify the Details page and Tabs to move the Details page linked to Tab1. Open and edit `src/App.tsx`, then modify the Ionic React `IonRouterOutlet` to be like this.

        <IonRouterOutlet>
          <Route exact path="/tab1">
            <Tab1 />
          </Route>
          <Route exact path="/tab1/details/:data">
            <Details />
          </Route>
          <Route exact path="/tab2">
            <Tab2 />
          </Route>
          <Route path="/tab3">
            <Tab3 />
          </Route>
          <Route exact path="/">
            <Redirect to="/tab1" />
          </Route>
        </IonRouterOutlet>

Now, the Details page is part of Tab1 with the additional parameter of data that is sent from the Tab1 list of data. Next, make a little title and icon modification of the Tabs by modifying the Tabs' codes.

<IonTabBar slot="bottom">
  <IonTabButton tab="schedule" href="/tab1">
    <IonIcon icon={aperture} />
    <IonLabel>Matchdays</IonLabel>
  </IonTabButton>
  <IonTabButton tab="speakers" href="/tab2">
    <IonIcon icon={apps} />
    <IonLabel>Tab Two</IonLabel>
  </IonTabButton>
  <IonTabButton tab="map" href="/tab3">
    <IonIcon icon={send} />
    <IonLabel>Tab Three</IonLabel>
  </IonTabButton>
</IonTabBar>

Next, open and edit the `src/pages/Details.tsx`, then replace all imports with these.

import {
  IonBackButton,
  IonButtons,
  IonCol,
  IonContent,
  IonGrid,
  IonHeader,
  IonItem,
  IonLabel,
  IonList,
  IonRow,
  IonTitle,
  IonToolbar
} from "@ionic/react";

Add the `props` param to the Details `React.FunctionComponent`.

const Details: React.FC = (props) => {
...
}

export default Details;

Declare the variables to convert `props`, `match`, and `params.data` as the `any` type. This method is different than the previous method in the list of data that converts a JSON array to the interface.

let prop: any = props;
let match: any = prop.match;
let data: any = JSON.parse(match.params.data);

Next, we have to extract the data from the params to the Ionic React components.

return (
  <>
      <IonHeader>
        <IonToolbar>
          <IonButtons slot="start">
            <IonBackButton defaultHref="/tab1" />
          </IonButtons>
          <IonTitle>{data.name}</IonTitle>
        </IonToolbar>
      </IonHeader>
    <IonContent>
      {data.matches.map((m: any, idx: number) => (
        <IonList key={idx} lines="none">
          <IonItem>
            <IonLabel>
              <IonGrid>
                <IonRow>
                  <IonCol><p>{m.date}</p></IonCol>
                </IonRow>
                <IonRow>
                  <IonCol>{m.stadium.name}, {m.city}</IonCol>
                </IonRow>
                <IonRow>
                  <IonCol><h2>{m.group}</h2></IonCol>
                </IonRow>
                <IonRow>
                  <IonCol size="5"><b>{m.team1.name} ({m.score1})</b></IonCol>
                  <IonCol size="2">vs</IonCol>
                  <IonCol size="5"><b>({m.score2}) {m.team2.name}</b></IonCol>
                </IonRow>
                <IonRow>
                  <IonCol>
                    {m.goals1.map((g1: any, gidx1: number) => (
                      <p key={gidx1}>{g1.name} `{g1.minute}</p>
                    ))}
                  </IonCol>
                  <IonCol>&nbsp;</IonCol>
                  <IonCol>
                    {m.goals2.map((g2: any, gidx2: number) => (
                      <p key={gidx2}>{g2.name} `{g2.minute}</p>
                    ))}
                  </IonCol>
                </IonRow>
              </IonGrid>
            </IonLabel>
          </IonItem>
        </IonList>
      ))}
    </IonContent>
  </>
);


4. Build with Capacitor

Before installing the Capacitor, we have to build the Ionic React application first by typing this command.

ionic build

Next, add the Capacitor Android platform using this command.

ionic capacitor add android

Next, type this command to build and open Android Studio.

ionic capacitor run android

Now, you can run the Ionic React application on an Android Device using Android Studio. And here the Ionic React application looks like on an Android device.

Build Android/iOS Mobile Apps using Ionic 4 React.js Capacitor - List page
Build Android/iOS Mobile Apps using Ionic 4 React.js Capacitor - Details page
Build Android/iOS Mobile Apps using Ionic 4 React.js Capacitor - Tab 2
Build Android/iOS Mobile Apps using Ionic 4 React.js Capacitor - Tab 3

Next, to run on the iOS device, type this command first.

ionic capacitor add ios

If you get the error as below.

✖ Updating iOS native dependencies with "pod install" (may take several minutes):
✖ update ios:
[error] Error running update: [!] Unknown installation options: disable_input_output_paths.

To fix that error, update the Cocoapods first, then remove the `ios` folder before running again Capacitor platform add.

sudo gem install cocoapods -n /usr/local/bin
rm -rf ios/
ionic capacitor add ios

Next, open XCode using this command.

ionic capacitor open ios

After setting up your Apple Account, you can run the iOS application inside your XCode on your iPhone device. Here's what we have on our iPhone device.

Build Android/iOS Mobile Apps using Ionic 4 React.js Capacitor - iOS List
Build Android/iOS Mobile Apps using Ionic 4 React.js Capacitor - iOS Details
Build Android/iOS Mobile Apps using Ionic 4 React.js Capacitor - iOS Tab 2
Build Android/iOS Mobile Apps using Ionic 4 React.js Capacitor - iOS tab 3

5. Conclusion

  • Updated to Ionic 8 + React 19+ + Capacitor

  • Clean, modern React Hooks code

  • Fully functional build pipeline for Android and iOS

  • Seamlessly fetches nested REST API data and displays UI

You can find the full working source code on our GitHub.

We know that building beautifully designed Ionic apps from scratch can be frustrating and very time-consuming. Check Ionic - Full Starter App and save development and design time. Android, iOS, and PWA, 100+ Screens and Components, the most complete and advanced Ionic Template.

That's just the basics. If you need more deep learning about Ionic, Angular, and TypeScript, you can take the following cheap course:

Thanks!