Releasely Docs
Upload your app

The .zip of a React Native project

What your React Native archive must contain, what Releasely downloads for you during the build, and what you can leave out.

In short: zip your project folder with its native ios/ and android/ folders, your package.json and your lockfile, and any private dependencies — but without node_modules/, native caches, or build folders.

The quick check

Open your .zip: if you see package.json right at the root, next to the ios/ and android/ folders, the archive is well formed. If they're buried inside one or more folders, re-zip the contents of the project folder rather than the folder itself.

Expo managed project? It's handled

If your project has no ios/ and android/ folders (Expo managed), Releasely runs expo prebuild for you during the build. Just make sure your Expo config (app.json or app.config.js) is complete, in particular the Android package (android.package) and the iOS bundle identifier (ios.bundleIdentifier). You can also commit ios/ and android/ yourself (the "bare" workflow); they're used as-is when present.

What the archive must contain

  • package.json — the project's manifest, at the root of the archive.
  • Your lockfilepackage-lock.json, yarn.lock, pnpm-lock.yaml, or bun.lockb: it pins the exact versions of your dependencies, so the cloud build matches what you test locally.
  • ios/ and android/ — the platform folders (Podfile, Xcode project, build.gradle, permissions…). The build needs them, even if you never touched them.
  • Your codeApp.tsx, src/, index.js, etc.
  • Your assets — images, fonts, and other files your app references.
  • Any configuration files your app uses, if applicable: google-services.json (Firebase Android), GoogleService-Info.plist (Firebase iOS), .env files read at build time, etc.

Pin your Node version

Because Releasely reproduces your toolchain, add a .nvmrc (or .node-version, or the engines.node / volta field in package.json) to build with the same Node version as on your machine. The package manager (yarn, pnpm, npm…) is inferred from your lockfile.

A well-formed archive looks like this:

my-app.zip
├── package.json
├── yarn.lock            ← or package-lock.json / pnpm-lock.yaml
├── .nvmrc              ← recommended: the Node version
├── App.tsx
├── src/
├── ios/
├── android/
└── assets/

Local and private dependencies: include them

We build only what's in the archive. The build machine has no access to your computer, your private git repositories, or your private npm registries. So:

  • Local dependencies (file:../my-package) must be included in the .zip, with relative paths that stay valid inside the archive. If a path points outside the archive (../my-package), move the package into the project and update the path.
  • Dependencies from a private git or a private npm registry must be copied into the project (or vendored), because the build cannot connect to them.

What Releasely downloads for you

No need to put these in the archive — they're fetched automatically during the build:

  • the npm packages (npm ci / yarn install / pnpm install is run for you, based on your lockfile);
  • the iOS CocoaPods (pod install runs on the build Mac);
  • the Node version you pinned (installed on demand, never frozen server-side).

What you can leave out

These folders are regenerated at build time — including them only bloats the upload:

  • node_modules/ — reinstalled from your lockfile (often the largest folder: exclude it first);
  • ios/Pods/ and android/.gradle/ — the native dependency caches;
  • ios/build/, android/app/build/ — compilation artifacts;
  • .git/ — your repository history;
  • .idea/, *.iml, .vscode/ — your editor configuration.

On macOS or Linux, this command creates a clean archive from the project root:

zip -r my-app.zip . -x "node_modules/*" "ios/Pods/*" "ios/build/*" "android/.gradle/*" "android/app/build/*" ".git/*" ".idea/*" ".vscode/*"

Signing: never in the .zip

Secrets go in Credentials

Do not put any keystore, certificate, or API key in the archive. Android signing (a keystore, if your app needs one) and the App Store connection are configured in your project's Credentials screen — Releasely takes care of signing the builds.

Size and build number

  • 2 GB maximum per archive — plenty for a project without node_modules or build folders.
  • The build number is incremented automatically from the last known build; a field on the Upload screen lets you force it if you need to catch up to a number already used on the stores. It is imposed on the build without editing your build.gradle or your Xcode project.

What's next?

Once the archive is uploaded, Releasely starts the cloud build (iOS and/or Android depending on your project) and the produced binary joins your next version automatically.

On this page