@bitz/app-mobile (apps/app-mobile) is the mobile shell. It is an independent stack — it does not import @bitz/platform-sdk or @bitz/widgets; its dependencies are Taro 4, React 18, Jotai, and Capacitor 8. One Taro codebase is compiled to three web targets — H5, WeChat mini-program (weapp), and HarmonyOS Next hybrid (harmony-hybrid) — and the H5 output is then wrapped as native Android and iOS apps with Capacitor 8.
1. Why a separate, version-locked stack
Taro 4 currently targets React 18 and its own Vite-based build runner. To stay compatible, the mobile workspace is resolutions-locked:
| Concern | Web app (@bitz/app) | Mobile (@bitz/app-mobile) |
|---|---|---|
| React | 19 | 18.3.1 (locked) |
| Bundler | Vite 8 + rolldown | Taro’s Vite 4 runner (locked) |
| Babel | @babel/core 8 | @babel/core 7.28 (locked) |
| State | Jotai + TanStack Query | Jotai only |
| Backend bridge | @bitz/platform-sdk | none |
This is why the overview scopes its “React 19 / Vite 8” claims to the web app. The mobile workspace cannot follow those versions until Taro does.
2. Targets and how each is produced
| Target | Build command | Produces |
|---|---|---|
| H5 | yarn build:h5 (taro build --type h5) | a browser bundle in dist/ — also the web asset for Capacitor |
| WeChat mini-program | yarn build:weapp (taro build --type weapp) | a WeChat mini-program project (miniprogramRoot: dist) |
| HarmonyOS Next hybrid | yarn build:harmony (taro build --type harmony-hybrid) | Harmony hybrid assets, later synced into the native project |
| Android | yarn build:android | H5 build + cap sync android into the native Android project |
| iOS | yarn build:ios | H5 build + cap sync ios into the native iOS project |
Every Taro build raises the Node heap with NODE_OPTIONS=--max-old-space-size=8092; the scripts set this for you.
3. The web-asset-then-native pattern
Capacitor does not compile to native; it wraps an existing web bundle. So the native Android and iOS apps are produced in two steps, and the webDir coupling is load-bearing:
- Build the web asset the Capacitor shell will host → that is the H5 output (
taro build --type h5writes todist/). cap synccopies those web assets into the native project and updates plugins.
# Android: build web assets, then sync into the native projectyarn build:android # = yarn build:android:web && yarn cap:sync:android
# iOS: same two-stepyarn build:ios # = yarn build:ios:web && yarn cap:sync:iosBecause capacitor.config.ts sets webDir: 'dist', the H5 build must output to dist/ — Capacitor reads from there.
4. Capacitor configuration
| Field | Value |
|---|---|
appId | com.bitz.editor.mobile |
appName | Bitz Editor Mobile |
webDir | dist |
bundledWebRuntime | false |
android.path | android |
ios.path | ios |
5. First-time native project setup
The native android/ and ios/ directories are generated once, then synced thereafter:
yarn workspace @bitz/app-mobile cap:add:android # creates apps/app-mobile/androidyarn workspace @bitz/app-mobile cap:add:ios # creates apps/app-mobile/iosAfter that, day-to-day work is build:android / build:ios (web + sync). Open the native project in Android Studio or Xcode to configure signing, permissions, and store artifacts:
yarn workspace @bitz/app-mobile cap:open:android # Android Studioyarn workspace @bitz/app-mobile cap:open:ios # Xcode# or from the root: yarn mobile:android:open / yarn mobile:ios:open6. HarmonyOS Next hybrid
Harmony support uses Taro’s official @tarojs/plugin-platform-harmony-hybrid plugin (loaded only when TARO_ENV === 'harmony-hybrid'). The build outputs hybrid assets; a dedicated script then syncs them into the Harmony project:
# dev watchyarn dev:mobile:harmony # taro build --type harmony-hybrid --watch
# build assets, then sync into the native Harmony projectyarn build:mobile:harmony # = build:harmonyyarn workspace @bitz/app-mobile harmony:sync # node ./scripts/harmony-sync.mjsharmony-sync.mjs copies apps/app-mobile/dist → apps/app-mobile/harmony/container/entry/src/main/resources/rawfile/dist (validating that dist, harmony/container, and the rawfile dir exist first; it rm -rfs the target then cp -rs the source). The combined convenience script is build:harmony:app (build:harmony && harmony:sync).
7. WeChat mini-program
project.config.json configures the WeChat mini-program project: projectname: 'bitzeditor-mobile', compileType: 'miniprogram', miniprogramRoot: 'dist', url-check disabled, and appid: 'touristappid' — the tourist/test mode, not a registered AppID. Set a real AppID (via the WeChat DevTools or by editing project.config.json) before publishing. build:weapp emits the mini-program into dist/.
8. Dev watch commands
| Command | Watches |
|---|---|
yarn dev:mobile (dev:h5) | H5 |
yarn dev:mobile:weapp | WeChat mini-program |
yarn dev:mobile:harmony | HarmonyOS Next hybrid |
9. Taro build configuration
config/index.ts (defineConfig): framework: 'react', compiler: 'vite' (prebundle disabled), designWidth: 375 with deviceRatio {375:2, 750:1, 828:1.81}, alias { '@': src }. H5 publicPath is './' for harmony+production else '/'; h5.router.mode: 'hash'. config/dev.ts enables source maps; config/prod.ts enables mini.optimizeMainPackage. config/taro-vite-plugin.ts dedupes react/react-dom to the workspace’s React 18, excludes @swc/* and fsevents from optimizeDeps, and runs a dev server on port 10085 (harmony) / 10086 (else).
10. Toolchain notes
- Yarn 4.17.0 is pinned at the monorepo root via
packageManager; the mobile workspace participates in the same workspaces install. installConfig.hoistingLimits: 'workspaces'prevents Taro’s runtime from being hoisted out of the mobile workspace — without it,@tarojs/runtimecan resolve to the wrong copy and break multi-target builds.apps/app-mobileis excluded from roottsconfig.jsonproject references (it has its own tsconfig and the Taro toolchain); onlyapps/appand the packages are referenced.- Clean rebuild:
yarn workspace @bitz/app-mobile cleanremovesdistand the Vite cache.
11. Reviewing a mobile change
- Confirm the target you changed is the one you built (H5 vs
weappvsharmony-hybridvs Capacitor Android/iOS). - If you changed web assets consumed by Capacitor, confirm you ran
build:android/build:ios(web + sync), not justbuild:h5. - If you added a Capacitor plugin, confirm it is installed and
cap syncwas run for both platforms; also add it tocapacitor.config.tsif it needs config. - If you changed Harmony output, confirm
harmony:syncran so the native project picked up the new assets. - Run
yarn workspace @bitz/app-mobile lint && typecheckbefore commit.
12. Source review
# Confirm the independent stack — mobile deps are Taro + Jotai + Capacitor only.rg -n '"@tarojs/|"@capacitor/|"jotai"' frontend/apps/app-mobile/package.json# Confirm mobile does NOT depend on the shared SDK/widgets.rg -n '@bitz/platform-sdk|@bitz/widgets' frontend/apps/app-mobile/package.json# Expected: the second command returns no matches.
# Confirm the Capacitor config and the harmony sync target.cat frontend/apps/app-mobile/capacitor.config.tssed -n '1,60p' frontend/apps/app-mobile/scripts/harmony-sync.mjsBack to Frontend · Web admin app · Toolchain & contributor flow