Skip to content
bitzorcas
中EN

Reference

Mobile shell — Taro + Capacitor + Harmony

'@bitz/app-mobile is an independent Taro 4 + React 18 stack compiled to H5, WeChat mini-program, and HarmonyOS Next hybrid, then wrapped as native Android and iOS with Capacitor 8. It does not use platform-sdk or widgets.'

Last updated

@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:

ConcernWeb app (@bitz/app)Mobile (@bitz/app-mobile)
React1918.3.1 (locked)
BundlerVite 8 + rolldownTaro’s Vite 4 runner (locked)
Babel@babel/core 8@babel/core 7.28 (locked)
StateJotai + TanStack QueryJotai only
Backend bridge@bitz/platform-sdknone

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

TargetBuild commandProduces
H5yarn build:h5 (taro build --type h5)a browser bundle in dist/ — also the web asset for Capacitor
WeChat mini-programyarn build:weapp (taro build --type weapp)a WeChat mini-program project (miniprogramRoot: dist)
HarmonyOS Next hybridyarn build:harmony (taro build --type harmony-hybrid)Harmony hybrid assets, later synced into the native project
Androidyarn build:androidH5 build + cap sync android into the native Android project
iOSyarn build:iosH5 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:

  1. Build the web asset the Capacitor shell will host → that is the H5 output (taro build --type h5 writes to dist/).
  2. cap sync copies those web assets into the native project and updates plugins.
Terminal window
# Android: build web assets, then sync into the native project
yarn build:android # = yarn build:android:web && yarn cap:sync:android
# iOS: same two-step
yarn build:ios # = yarn build:ios:web && yarn cap:sync:ios

Because capacitor.config.ts sets webDir: 'dist', the H5 build must output to dist/ — Capacitor reads from there.

4. Capacitor configuration

FieldValue
appIdcom.bitz.editor.mobile
appNameBitz Editor Mobile
webDirdist
bundledWebRuntimefalse
android.pathandroid
ios.pathios

5. First-time native project setup

The native android/ and ios/ directories are generated once, then synced thereafter:

Terminal window
yarn workspace @bitz/app-mobile cap:add:android # creates apps/app-mobile/android
yarn workspace @bitz/app-mobile cap:add:ios # creates apps/app-mobile/ios

After 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:

Terminal window
yarn workspace @bitz/app-mobile cap:open:android # Android Studio
yarn workspace @bitz/app-mobile cap:open:ios # Xcode
# or from the root: yarn mobile:android:open / yarn mobile:ios:open

6. 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:

Terminal window
# dev watch
yarn dev:mobile:harmony # taro build --type harmony-hybrid --watch
# build assets, then sync into the native Harmony project
yarn build:mobile:harmony # = build:harmony
yarn workspace @bitz/app-mobile harmony:sync # node ./scripts/harmony-sync.mjs

harmony-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

CommandWatches
yarn dev:mobile (dev:h5)H5
yarn dev:mobile:weappWeChat mini-program
yarn dev:mobile:harmonyHarmonyOS 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/runtime can resolve to the wrong copy and break multi-target builds.
  • apps/app-mobile is excluded from root tsconfig.json project references (it has its own tsconfig and the Taro toolchain); only apps/app and the packages are referenced.
  • Clean rebuild: yarn workspace @bitz/app-mobile clean removes dist and the Vite cache.

11. Reviewing a mobile change

  1. Confirm the target you changed is the one you built (H5 vs weapp vs harmony-hybrid vs Capacitor Android/iOS).
  2. If you changed web assets consumed by Capacitor, confirm you ran build:android / build:ios (web + sync), not just build:h5.
  3. If you added a Capacitor plugin, confirm it is installed and cap sync was run for both platforms; also add it to capacitor.config.ts if it needs config.
  4. If you changed Harmony output, confirm harmony:sync ran so the native project picked up the new assets.
  5. Run yarn workspace @bitz/app-mobile lint && typecheck before commit.

12. Source review

Terminal window
# 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.ts
sed -n '1,60p' frontend/apps/app-mobile/scripts/harmony-sync.mjs

Back to Frontend · Web admin app · Toolchain & contributor flow

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%