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 lockfile —
package-lock.json,yarn.lock,pnpm-lock.yaml, orbun.lockb: it pins the exact versions of your dependencies, so the cloud build matches what you test locally. ios/andandroid/— the platform folders (Podfile, Xcode project,build.gradle, permissions…). The build needs them, even if you never touched them.- Your code —
App.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),.envfiles 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 installis run for you, based on your lockfile); - the iOS CocoaPods (
pod installruns 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/andandroid/.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_modulesor 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.gradleor 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.