OpenCart Mobile App Developer Guide, Part 2: Tools and Libraries, Why Expo, and the Development Environment
The tools and libraries behind the OpenCart mobile app, why Expo and React Native were chosen, and how to set up the local development environment.

This is part 2 of 5 of the OpenCart Mobile App Developer Guide. New to the project? Start with Part 1: Overview, Architecture, Repository Layout and Languages.
In this post
5. Tools and libraries, and why each was chosen
5.1 Mobile app
| Package | Purpose | Why this one |
|---|---|---|
| expo (SDK 58) | Toolchain, native runtime, dev server, build service | See §6. |
| react 19.3 / react-native 0.88 | UI framework | One code base for iOS and Android with truly native views (not a web page in a wrapper). React is the most widely known UI model, so new developers ramp up fast. The versions are the ones SDK 58 pins; keep them in step with Expo (see §17). |
| @tanstack/react-query v5 | Server-state cache: fetching, caching, refetching, pagination, mutations | Almost all app data is server state (catalog, cart, orders). React Query gives caching, deduplication, loading and error flags, pull-to-refresh, infinite scroll and cache updates after mutations, with no hand-written reducers. It replaces Redux for this kind of app. |
| @react-navigation/native, native-stack, bottom-tabs v7 | Navigation | The standard for React Native. native-stack uses the platform’s real navigation controllers (native swipe-back and modal sheets). Navigation parameters are fully typed. |
| react-native-screens | Native screen containers | Required by native-stack. Keeps hidden screens out of the render tree to save memory. |
| react-native-safe-area-context | Notch and home-indicator insets | Correct padding on every device. Required by React Navigation. |
| expo-secure-store | Encrypted key-value storage | Holds the session token, which is effectively a login credential. It uses the iOS Keychain and Android Keystore, unlike AsyncStorage, which is plain text. |
| react-native-webview | Embedded browser | Used for (1) store HTML such as product descriptions and information pages, and (2) the online payment page, so any OpenCart payment gateway works without writing native code for each one. |
| @expo/vector-icons | Icon font (Ionicons) | Bundled with Expo. No image assets to manage. |
| expo-status-bar | Status bar style | Switches light or dark icons to match the header colour. |
| expo-font, expo-asset | Font and asset loading | Needed by the vector icons under SDK 58. |
| typescript, @types/react | Type checking | See §4. npm run typecheck runs tsc --noEmit. |
| @babel/core | Transpiler used by Metro | Required by Expo’s bundler. |
Deliberately not used:
- Redux / Zustand. React Query plus one small Context covers it. Adding a global store would duplicate server state and invite stale data.
- Axios.
fetchis built in. All HTTP lives in one 150-line file (client.ts), so a library adds nothing. - A UI kit (NativeBase, Paper…). A small set of components in
components/ui.tsxkeeps the bundle small and the look fully under the store’s control, including the admin-set colours. - Form libraries. Forms are small.
useStateplus field errors from the server is enough, and the server is the source of truth for validation.
5.2 Backend and dev environment
| Tool | Purpose | Why |
|---|---|---|
| OpenCart 4.1.0.4 extension system | Packaging and install | Installs through the admin UI with no core file edits, so OpenCart upgrades don’t overwrite it. Uninstalling removes it cleanly. |
| OpenCart startup actions | Run code on every catalog request | The supported hook point for swapping the session before controllers run (see §8.3). No core edits. |
| Docker Compose (php:8.3-apache, mariadb:11.4) | Local store | The same environment on every machine. The store runs on port 8090 because 8080, 8081 and 3307 were already taken on the original dev machine. |
zip (build.sh) | Packaging | OpenCart’s installer expects a .ocmod.zip whose contents sit at the root. The zip’s file name becomes the extension code. |
6. Why Expo
Expo is a framework and toolchain on top of React Native. For this project it was the right choice for these reasons:
- No native toolchain to start. You can develop on a real phone with only Node.js and the free Expo Go app: run
npx expo startand scan the QR code. No Xcode or Android Studio is needed until you make a store build. Backend developers who mostly know PHP can still run and debug the app. - One code base, both platforms, plus web. The same TypeScript runs on iOS and Android.
npx expo start --webruns it in a browser, which is handy for quick API debugging.client.tsandPaymentScreeninclude small web fallbacks. - Versioned, tested native modules. Each SDK release pins versions of React Native and its modules (SecureStore, WebView, screens…) that are tested together.
npx expo install <pkg>picks the matching version, which avoids the usual native dependency conflicts. - Cloud builds and store submission (EAS).
eas buildproduces signed.ipaand.aabfiles on Expo’s servers, so you don’t need a Mac for iOS builds.eas submituploads them to the stores. - Over-the-air updates (EAS Update). You can push JavaScript-only fixes, such as a text change or a bug in a screen, to installed apps without a store review. Native changes still need a new build.
- Config plugins instead of editing native projects. Native settings, such as iOS App Transport Security (
NSAllowsLocalNetworking) or the SecureStore setup, are declared inapp.json. Expo generates the iOS and Android projects (expo prebuild), so there are noios/orandroid/folders to maintain. - Fast iteration. Fast Refresh keeps app state while you edit.
npx expo start -cclears the bundler cache when things get confused.
Trade-offs to know about:
- Expo Go supports only the newest SDK. That is why the project was upgraded to SDK 58: the App Store version of Expo Go showed “No usable data” for an older SDK QR code. If you add a library with custom native code that isn’t in Expo Go, switch to a development build (
npx expo run:iosoreas build --profile development). - You follow Expo’s upgrade cadence (about three SDKs a year). See §17 for how to upgrade.
7. Development environment
7.1 Prerequisites
- Node.js 20 or newer (the project has been run with Node 26).
- Docker Desktop, for the local store.
- Expo Go on your phone, or Xcode (iOS Simulator) or Android Studio (emulator).
- The phone and computer on the same Wi-Fi network.
7.2 Start the store
cd opencart-mobile-app
docker compose up -d # web on :8090, db internal only
open http://localhost:8090/ # storefront
open http://localhost:8090/admin/ # admin (credentials in .env)
store/config.php and store/admin/config.php build HTTP_SERVER from the request’s Host header. The same store therefore works at localhost:8090 on your computer and at <your LAN IP>:8090 from the phone, and image URLs always point at an address the client can reach.
Port 8090 is published on all interfaces ("8090:80"), so anyone on your Wi-Fi can open the dev store and its admin. Don’t do this on a public network. To restrict it to your computer, change it to "127.0.0.1:8090:80". Phones will then no longer reach it.
7.3 Start the app
cd mobile
npm install
npx expo start -c # -c clears the Metro cache; use it after changing config or dependencies
Scan the QR code with the iPhone Camera (it opens Expo Go) or from inside Expo Go on Android.
In src/config.ts, set baseUrl to your computer’s LAN IP. Find it with ipconfig getifaddr en0 on macOS. localhost on a phone means the phone itself. For the Android emulator use http://10.0.2.2:8090/.
7.4 Useful commands
| Command | Where | What it does |
|---|---|---|
npm run typecheck | mobile/ | Type-checks the whole app |
npx expo start -c | mobile/ | Dev server with a clean cache |
npx expo export --platform ios | mobile/ | Builds a production JS bundle; a quick way to catch bundling errors |
npx expo-doctor | mobile/ | Checks dependency versions against the SDK |
sh opencart-extension/build.sh | root | Rebuilds mobile_api.ocmod.zip |
NODE_PATH=/tmp/oc-docs/node_modules node docs/build-html.cjs | root | Rebuilds the HTML guide (install marked first, see the script header) |
NODE_PATH=/tmp/oc-docs/node_modules node docs/build-wordpress.cjs | root | Rebuilds the WordPress import (needs Chrome for the diagrams) |
docker compose exec web php -l extension/mobile_api/<file> | root | PHP syntax check inside the container |
docker compose logs -f web | root | Apache and PHP errors |
7.5 Editing the extension locally
The source of truth is opencart-extension/mobile_api/. The running store uses its installed copy in store/extension/mobile_api/. After editing, either copy the changed file across:
cp opencart-extension/mobile_api/catalog/controller/api/cart.php store/extension/mobile_api/catalog/controller/api/cart.php
…or rebuild the zip and reinstall it through the admin. Reinstalling resets the module settings, including the API key, so the copy approach is faster during development. Always commit the source folder, not the store/ copy.
← Previous: Part 1: Overview, Architecture, Repository Layout and Languages
Next: Part 3: The OpenCart Extension and the Mobile App →
The series
- Part 1: Overview, Architecture, Repository Layout and Languages
- Part 2: Tools and Libraries, Why Expo, and the Development Environment (you are reading this part)
- Part 3: The OpenCart Extension and the Mobile App
- Part 4: Design Patterns, Hooks and End-to-End Flows
- Part 5: Configuration, Security, Extending, Testing, Releasing and Troubleshooting




