---
description: Store user files in a Capacitor app with Firebase Cloud Storage, covering bucket setup, uploads and downloads, security rules, and troubleshooting.
title: Upload & Manage Files with Firebase Storage in Capacitor - Capawesome
image: https://capawesome.io/docs/assets/images/social/blog/capacitor-firebase-cloud-storage-guide.png
---

<!doctype html> 

[Skip to content ](#upload-manage-files-with-firebase-storage-in-capacitor) 

[📲 Introducing **Build Sharing** — get your builds onto testers' devices with a link & QR code. No account required. ](/blog/share-mobile-app-builds-with-testers/) 

* [ SDKs ](/docs/sdks/)
* [ Formbricks ](/docs/sdks/capacitor/formbricks/)
* [ Geocoder ](/docs/sdks/capacitor/geocoder/)
* [ Google Sign-In ](/docs/sdks/capacitor/google-sign-in/)
* [ Grafana Faro ](/docs/sdks/capacitor/grafana-faro/)
* [ Gyroscope ](/docs/sdks/capacitor/gyroscope/)
* [ Haptics ](/docs/sdks/capacitor/haptics/)
* [ Home Indicator ](/docs/sdks/capacitor/home-indicator/)
* [ In-App Browser ](/docs/sdks/capacitor/in-app-browser/)
* [ Install Referrer ](/docs/sdks/capacitor/install-referrer/)
* [ Intercom ](/docs/sdks/capacitor/intercom/)
* [ Intune ](/docs/sdks/capacitor/intune/)
* [ Keep Awake ](/docs/sdks/capacitor/keep-awake/)
* [ libSQL ](/docs/sdks/capacitor/libsql/)
* [ Light Sensor ](/docs/sdks/capacitor/light-sensor/)
* [ Live Update ](/docs/sdks/capacitor/live-update/)
* [ Localization ](/docs/sdks/capacitor/localization/)
* [ Mail Composer ](/docs/sdks/capacitor/mail-composer/)
* [ Managed Configurations ](/docs/sdks/capacitor/managed-configurations/)
* [ Maps Launcher ](/docs/sdks/capacitor/maps-launcher/)
* [ Media Session ](/docs/sdks/capacitor/media-session/)
* [ ML Kit ](/docs/sdks/capacitor/mlkit/)
* [ Navigation Bar ](/docs/sdks/capacitor/navigation-bar/)
* [ Network ](/docs/sdks/capacitor/network/)
* [ NFC ](/docs/sdks/capacitor/nfc/)
* [ Node.js ](/docs/sdks/capacitor/nodejs/)
* [ OAuth ](/docs/sdks/capacitor/oauth/)
* [ Passkeys ](/docs/sdks/capacitor/passkeys/)
* [ Password Autofill ](/docs/sdks/capacitor/password-autofill/)
* [ PDF Generator ](/docs/sdks/capacitor/pdf-generator/)
* [ PDF Viewer ](/docs/sdks/capacitor/pdf-viewer/)
* [ Pedometer ](/docs/sdks/capacitor/pedometer/)
* [ Permissions ](/docs/sdks/capacitor/permissions/)
* [ Phone Dialer ](/docs/sdks/capacitor/phone-dialer/)
* [ Photo Editor ](/docs/sdks/capacitor/photo-editor/)
* [ Photo Manipulator ](/docs/sdks/capacitor/photo-manipulator/)
* [ PixLive ](/docs/sdks/capacitor/pixlive/)
* [ PostHog ](/docs/sdks/capacitor/posthog/)
* [ Printer ](/docs/sdks/capacitor/printer/)
* [ Privacy Screen ](/docs/sdks/capacitor/privacy-screen/)
* [ Proximity Sensor ](/docs/sdks/capacitor/proximity-sensor/)
* [ Purchases ](/docs/sdks/capacitor/purchases/)
* [ RealtimeKit ](/docs/sdks/capacitor/realtimekit/)
* [ Root Detection ](/docs/sdks/capacitor/root-detection/)
* [ Screen Brightness ](/docs/sdks/capacitor/screen-brightness/)
* [ Screen Orientation ](/docs/sdks/capacitor/screen-orientation/)
* [ Screen Reader ](/docs/sdks/capacitor/screen-reader/)
* [ Screenshot ](/docs/sdks/capacitor/screenshot/)
* [ Secure Preferences ](/docs/sdks/capacitor/secure-preferences/)
* [ Settings Launcher ](/docs/sdks/capacitor/settings-launcher/)
* [ Shake ](/docs/sdks/capacitor/shake/)
* [ Silent Mode ](/docs/sdks/capacitor/silent-mode/)
* [ SIM ](/docs/sdks/capacitor/sim/)
* [ SMS Composer ](/docs/sdks/capacitor/sms-composer/)
* [ Speech Recognition ](/docs/sdks/capacitor/speech-recognition/)
* [ Speech Synthesis ](/docs/sdks/capacitor/speech-synthesis/)
* [ Share Target ](/docs/sdks/capacitor/share-target/)
* [ Square Mobile Payments ](/docs/sdks/capacitor/square-mobile-payments/)
* [ SQLite ](/docs/sdks/capacitor/sqlite/)
* [ Superwall ](/docs/sdks/capacitor/superwall/)
* [ System WebView ](/docs/sdks/capacitor/system-webview/)
* [ Tauri ](/docs/sdks/capacitor/tauri/)
* [ Text Interaction ](/docs/sdks/capacitor/text-interaction/)
* [ Text Zoom ](/docs/sdks/capacitor/text-zoom/)
* [ Thermal State ](/docs/sdks/capacitor/thermal-state/)
* [ Toast ](/docs/sdks/capacitor/toast/)
* [ Torch ](/docs/sdks/capacitor/torch/)
* [ Vault ](/docs/sdks/capacitor/vault/)
* [ Volume ](/docs/sdks/capacitor/volume/)
* [ Wallet ](/docs/sdks/capacitor/wallet/)
* [ Wifi ](/docs/sdks/capacitor/wifi/)
* [ YouTube Player ](/docs/sdks/capacitor/youtube-player/)
* [ Zip ](/docs/sdks/capacitor/zip/)
* [ Cordova ](/docs/sdks/cordova/)
* [ Cloud ](/docs/cloud/)
* [ Integrations ](/docs/cloud/live-updates/integrations/)
* Concepts
* Reference
* [ Troubleshooting ](/docs/cloud/live-updates/troubleshooting/)
* [ FAQ ](/docs/cloud/live-updates/faq/)
* [ Native Builds ](/docs/cloud/native-builds/)
* [ Set Up Environments ](/docs/cloud/native-builds/environments/)
* [ Overwrite Native Configurations ](/docs/cloud/native-builds/native-configurations/)
* [ Auto-Increment Build Numbers ](/docs/cloud/native-builds/auto-incrementing-build-numbers/)
* [ Configure the Web Build Script ](/docs/cloud/native-builds/web-build-script/)
* [ Build from a Monorepo ](/docs/cloud/native-builds/monorepo/)
* [ Use pnpm, Yarn, or bun ](/docs/cloud/native-builds/package-managers/)
* [ Install Private npm Packages ](/docs/cloud/native-builds/npm-private-registry/)
* [ Override the Java Version ](/docs/cloud/native-builds/override-java-version/)
* [ Custom iOS Provisioning Profiles ](/docs/cloud/native-builds/custom-ios-provisioning-profiles/)
* [ Build without Git ](/docs/cloud/native-builds/build-without-git/)
* [ Access Git Behind a Firewall ](/docs/cloud/native-builds/firewall-access/)
* [ Integrations ](/docs/cloud/native-builds/integrations/)
* Reference
* [ Troubleshooting ](/docs/cloud/native-builds/troubleshooting/)
* [ FAQ ](/docs/cloud/native-builds/faq/)
* [ App Store Publishing ](/docs/cloud/app-store-publishing/)
* [ Submit a Build ](/docs/cloud/app-store-publishing/submit-a-build/)
* [ Submit Automatically After a Build ](/docs/cloud/app-store-publishing/submit-automatically/)
* [ Troubleshooting ](/docs/cloud/app-store-publishing/troubleshooting/)
* [ FAQ ](/docs/cloud/app-store-publishing/faq/)
* [ Automations ](/docs/cloud/automations/)
* [ Reference ](/docs/cloud/automations/reference/)
* [ Troubleshooting ](/docs/cloud/automations/troubleshooting/)
* [ FAQ ](/docs/cloud/automations/faq/)
* [ Assist ](/docs/cloud/assist/)
* [ CLI ](/docs/cloud/cli/)
* APIs and SDKs
* [ Webhooks ](/docs/cloud/webhooks/)
* [ Integrations ](/docs/cloud/integrations/)
* Notifications
* Account
* [ Organization ](/docs/cloud/organizations/)
* [ Two-Factor Enforcement ](/docs/cloud/organizations/two-factor-authentication/)
* [ Audit Logs ](/docs/cloud/organizations/audit-logs/)
* [ Billing ](/docs/cloud/organizations/billing/)
* [ License Keys ](/docs/cloud/license-keys/)
* [ AI ](/docs/ai/)
* [ Insiders ](/docs/insiders/)
* [ Billing & Plans ](/docs/insiders/billing-and-plans/)
* [ FAQ ](/docs/insiders/faq/)
* [ License ](https://capawesome.io/legal/eula/)
* [ Support ](/docs/support/)
* [ Contributing ](/docs/contributing/)
* Contributing code
* [ Code of Conduct ](/docs/contributing/code-of-conduct/)
* [ Questions ](https://docs.github.com/en/discussions/collaborating-with-your-community-using-discussions/participating-in-a-discussion#creating-a-discussion)
* [ Blog ](/blog/)
* Categories

* [ Why Use Firebase Storage in a Capacitor App? ](#why-use-firebase-storage-in-a-capacitor-app)
* [ Before You Start ](#before-you-start)
* [ Step 1: Create the Storage Bucket ](#step-1-create-the-storage-bucket)
* [ Step 2: Install the Plugin ](#step-2-install-the-plugin)
* [ Step 3: Add Firebase to Your Native Apps ](#step-3-add-firebase-to-your-native-apps)
* [ Tracking Upload Progress in Angular, React, or Vue ](#tracking-upload-progress-in-angular-react-or-vue)
* [ Run on a Device and Verify ](#run-on-a-device-and-verify)
* [ Firebase Cloud Storage Best Practices ](#firebase-cloud-storage-best-practices)
* [ Limitations ](#limitations)
* [ Common Errors and Troubleshooting ](#common-errors-and-troubleshooting)
* [ FAQ ](#faq)
* [ Ship Upload Fixes Without a Full Release ](#ship-upload-fixes-without-a-full-release)
* [ Conclusion ](#conclusion)

* Related links

# Upload & Manage Files with Firebase Storage in Capacitor[¶](#upload-manage-files-with-firebase-storage-in-capacitor "Permanent link")

Profile pictures, user-uploaded documents, generated PDFs — every app eventually needs somewhere to put files that isn't the device itself. The [Capacitor Firebase Cloud Storage plugin](/docs/sdks/capacitor/firebase/cloud-storage/) gives you native upload/download SDKs for Android and iOS, plus the Firebase JS SDK on web, behind one shared API.

This guide walks the whole thing end to end — creating the storage bucket, installing and configuring the plugin, uploading and downloading files — plus the platform differences that catch teams off guard and the production practices worth knowing before you ship.

[ ![Build and deploy your Capacitor app with Capawesome Cloud](https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1) ](/) 

## How to Use Firebase Cloud Storage in a Capacitor App[¶](#how-to-use-firebase-cloud-storage-in-a-capacitor-app "Permanent link")

Adding Firebase Cloud Storage to a Capacitor app takes four steps:

1. **Create a Storage bucket** in your Firebase project and set its security rules.
2. **Install** `@capacitor-firebase/storage` and sync the native projects.
3. **Add the native config files** (`google-services.json` on Android, `GoogleService-Info.plist` on iOS).
4. **Upload and download files** with `uploadFile()` and `downloadFile()`, and get a shareable link with `getDownloadUrl()`.

The rest of this guide covers each step in detail, plus the platform gaps, best practices, and troubleshooting.

## What Is Cloud Storage?[¶](#what-is-cloud-storage "Permanent link")

[Cloud Storage for Firebase](https://firebase.google.com/docs/storage) is Google's object storage service for user-generated content: images, videos, documents, anything you'd otherwise store as a file. The [Capacitor Firebase Cloud Storage plugin](/docs/sdks/capacitor/firebase/cloud-storage/) exposes the native Android and iOS Storage SDKs, plus the Firebase JS SDK on web, behind one shared TypeScript API.

A working example can be found here: [capawesome-team/capacitor-firebase-plugin-demo](https://github.com/capawesome-team/capacitor-firebase-plugin-demo).

### Why Not Just Use the Firebase JS SDK?[¶](#why-not-just-use-the-firebase-js-sdk "Permanent link")

Uploading and downloading through the JS SDK inside a WebView means routing file data through the WebView's JavaScript bridge, which gets expensive for anything beyond small files, large uploads and downloads risk timeouts or memory pressure that the native SDKs handle more gracefully. The native SDKs also integrate directly with each platform's file system: on Android and iOS you pass a file `uri` straight to [uploadFile(...)](/docs/sdks/capacitor/firebase/cloud-storage/#uploadfile) or [downloadFile(...)](/docs/sdks/capacitor/firebase/cloud-storage/#downloadfile), no intermediate `Blob` conversion required, which only exists as a concept on web.

Advanced Operations

This guide covers the operations most apps need. For every method with code samples, see the [Usage section of the plugin docs](https://capawesome.io/docs/sdks/capacitor/firebase/cloud-storage/#usage).

## Why Use Firebase Storage in a Capacitor App?[¶](#why-use-firebase-storage-in-a-capacitor-app "Permanent link")

The plugin's own use cases map to four real scenarios:

**User-generated content.** Upload photos or other files created by your users with [uploadFile(...)](/docs/sdks/capacitor/firebase/cloud-storage/#uploadfile). Pass a `uri` on Android/iOS or a `blob` on web:

`[](#%5F%5Fcodelineno-0-1)import { FirebaseStorage } from '@capacitor-firebase/storage';
[](#%5F%5Fcodelineno-0-2)
[](#%5F%5Fcodelineno-0-3)const uploadFile = async () => {
[](#%5F%5Fcodelineno-0-4)  return new Promise((resolve, reject) => {
[](#%5F%5Fcodelineno-0-5)    FirebaseStorage.uploadFile(
[](#%5F%5Fcodelineno-0-6)      { path: 'images/mountains.png', uri: 'file:///.../mountains.png' },
[](#%5F%5Fcodelineno-0-7)      (event, error) => {
[](#%5F%5Fcodelineno-0-8)        if (error) {
[](#%5F%5Fcodelineno-0-9)          reject(error);
[](#%5F%5Fcodelineno-0-10)        } else if (event?.completed) {
[](#%5F%5Fcodelineno-0-11)          resolve();
[](#%5F%5Fcodelineno-0-12)        }
[](#%5F%5Fcodelineno-0-13)      },
[](#%5F%5Fcodelineno-0-14)    );
[](#%5F%5Fcodelineno-0-15)  });
[](#%5F%5Fcodelineno-0-16)};
`

**File downloads.** Download files to the local file system on Android/iOS, or as a `Blob` on web, with [downloadFile(...)](/docs/sdks/capacitor/firebase/cloud-storage/#downloadfile).

**Content delivery.** Retrieve a file's download URL with [getDownloadUrl(...)](/docs/sdks/capacitor/firebase/cloud-storage/#getdownloadurl) to display or share it:

`[](#%5F%5Fcodelineno-1-1)import { FirebaseStorage } from '@capacitor-firebase/storage';
[](#%5F%5Fcodelineno-1-2)
[](#%5F%5Fcodelineno-1-3)const getDownloadUrl = async () => {
[](#%5F%5Fcodelineno-1-4)  const { downloadUrl } = await FirebaseStorage.getDownloadUrl({
[](#%5F%5Fcodelineno-1-5)    path: 'images/mountains.png',
[](#%5F%5Fcodelineno-1-6)  });
[](#%5F%5Fcodelineno-1-7)  return downloadUrl;
[](#%5F%5Fcodelineno-1-8)};
`

**File management.** List files in a directory with [listFiles(...)](/docs/sdks/capacitor/firebase/cloud-storage/#listfiles), read/update metadata, or delete files:

`[](#%5F%5Fcodelineno-2-1)import { FirebaseStorage } from '@capacitor-firebase/storage';
[](#%5F%5Fcodelineno-2-2)
[](#%5F%5Fcodelineno-2-3)const listFiles = async () => {
[](#%5F%5Fcodelineno-2-4)  const { items } = await FirebaseStorage.listFiles({ path: 'images' });
[](#%5F%5Fcodelineno-2-5)  return items;
[](#%5F%5Fcodelineno-2-6)};
`

## Before You Start[¶](#before-you-start "Permanent link")

This guide assumes you already have a Capacitor app with the `android` and/or `ios` platforms added, and a Firebase project (create one in the [Firebase console](https://console.firebase.google.com/) if you haven't). Everything specific to Cloud Storage we'll set up below.

## Step 1: Create the Storage Bucket[¶](#step-1-create-the-storage-bucket "Permanent link")

1. In the [Firebase console](https://console.firebase.google.com/), open **Build → Storage** and click **Get started**.
2. Choose a starting mode for the security rules — **production mode** (locked to authenticated users) is the safe default; test mode allows open access for a short window and is only for prototyping — then pick your bucket location.
3. Under the **Rules** tab, set rules that match how your app authenticates. Firebase's default already requires a signed-in user:  
`[](#%5F%5Fcodelineno-3-1)rules_version = '2';  
[](#%5F%5Fcodelineno-3-2)service firebase.storage {  
[](#%5F%5Fcodelineno-3-3)  match /b/{bucket}/o {  
[](#%5F%5Fcodelineno-3-4)    match /{allPaths=**} {  
[](#%5F%5Fcodelineno-3-5)      allow read, write: if request.auth != null;  
[](#%5F%5Fcodelineno-3-6)    }  
[](#%5F%5Fcodelineno-3-7)  }  
[](#%5F%5Fcodelineno-3-8)}  
`  
Because those rules check `request.auth`, uploads and downloads fail until the user is signed in — see the [Capacitor Firebase Authentication guide](/blog/capacitor-firebase-authentication-guide/).

## Step 2: Install the Plugin[¶](#step-2-install-the-plugin "Permanent link")

`[](#%5F%5Fcodelineno-4-1)npm install @capacitor-firebase/storage
[](#%5F%5Fcodelineno-4-2)npx cap sync
`

## Step 3: Add Firebase to Your Native Apps[¶](#step-3-add-firebase-to-your-native-apps "Permanent link")

The plugin uses the native config files Firebase generates ([full reference](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md)):

* **Android** — register an Android app, download `google-services.json`, and place it in `android/app/google-services.json`.
* **iOS** — register an iOS app, download `GoogleService-Info.plist`, move it to `ios/App/App/GoogleService-Info.plist`, and drag it into the Xcode project (add it to all targets).

On **iOS with Swift Package Manager**, add this to `capacitor.config.ts` to avoid a package identity collision (requires Capacitor CLI 8.4.0+):

`[](#%5F%5Fcodelineno-5-1){
[](#%5F%5Fcodelineno-5-2)  "experimental": {
[](#%5F%5Fcodelineno-5-3)    "ios": {
[](#%5F%5Fcodelineno-5-4)      "spm": {
[](#%5F%5Fcodelineno-5-5)        "packageOptions": {
[](#%5F%5Fcodelineno-5-6)          "@capacitor-firebase/storage": { "symlink": true }
[](#%5F%5Fcodelineno-5-7)        }
[](#%5F%5Fcodelineno-5-8)      }
[](#%5F%5Fcodelineno-5-9)    }
[](#%5F%5Fcodelineno-5-10)  }
[](#%5F%5Fcodelineno-5-11)}
`

The plugin itself needs no additional configuration.

## Tracking Upload Progress in Angular, React, or Vue[¶](#tracking-upload-progress-in-angular-react-or-vue "Permanent link")

`uploadFile(...)` reports progress through its callback, which you'll usually surface as a progress bar. The wiring differs by framework — and in Angular the callback fires outside the change-detection zone, so update state inside `NgZone.run(...)`:

AngularReactVue

`[](#%5F%5Fcodelineno-6-1)import { Injectable, NgZone, signal } from '@angular/core';
[](#%5F%5Fcodelineno-6-2)import { FirebaseStorage } from '@capacitor-firebase/storage';
[](#%5F%5Fcodelineno-6-3)
[](#%5F%5Fcodelineno-6-4)@Injectable({ providedIn: 'root' })
[](#%5F%5Fcodelineno-6-5)export class UploadService {
[](#%5F%5Fcodelineno-6-6)  readonly progress = signal(0);
[](#%5F%5Fcodelineno-6-7)
[](#%5F%5Fcodelineno-6-8)  constructor(private readonly zone: NgZone) {}
[](#%5F%5Fcodelineno-6-9)
[](#%5F%5Fcodelineno-6-10)  upload(path: string, uri: string) {
[](#%5F%5Fcodelineno-6-11)    FirebaseStorage.uploadFile({ path, uri }, (event, error) => {
[](#%5F%5Fcodelineno-6-12)      if (!error && event) {
[](#%5F%5Fcodelineno-6-13)        this.zone.run(() => this.progress.set(event.progress ?? 0));
[](#%5F%5Fcodelineno-6-14)      }
[](#%5F%5Fcodelineno-6-15)    });
[](#%5F%5Fcodelineno-6-16)  }
[](#%5F%5Fcodelineno-6-17)}
`

`[](#%5F%5Fcodelineno-7-1)import { useState } from 'react';
[](#%5F%5Fcodelineno-7-2)import { FirebaseStorage } from '@capacitor-firebase/storage';
[](#%5F%5Fcodelineno-7-3)
[](#%5F%5Fcodelineno-7-4)export const useUpload = () => {
[](#%5F%5Fcodelineno-7-5)  const [progress, setProgress] = useState(0);
[](#%5F%5Fcodelineno-7-6)  const upload = (path: string, uri: string) => {
[](#%5F%5Fcodelineno-7-7)    FirebaseStorage.uploadFile({ path, uri }, (event, error) => {
[](#%5F%5Fcodelineno-7-8)      if (!error && event) setProgress(event.progress ?? 0);
[](#%5F%5Fcodelineno-7-9)    });
[](#%5F%5Fcodelineno-7-10)  };
[](#%5F%5Fcodelineno-7-11)  return { progress, upload };
[](#%5F%5Fcodelineno-7-12)};
`

`[](#%5F%5Fcodelineno-8-1)import { ref } from 'vue';
[](#%5F%5Fcodelineno-8-2)import { FirebaseStorage } from '@capacitor-firebase/storage';
[](#%5F%5Fcodelineno-8-3)
[](#%5F%5Fcodelineno-8-4)export const useUpload = () => {
[](#%5F%5Fcodelineno-8-5)  const progress = ref(0);
[](#%5F%5Fcodelineno-8-6)  const upload = (path: string, uri: string) => {
[](#%5F%5Fcodelineno-8-7)    FirebaseStorage.uploadFile({ path, uri }, (event, error) => {
[](#%5F%5Fcodelineno-8-8)      if (!error && event) progress.value = event.progress ?? 0;
[](#%5F%5Fcodelineno-8-9)    });
[](#%5F%5Fcodelineno-8-10)  };
[](#%5F%5Fcodelineno-8-11)  return { progress, upload };
[](#%5F%5Fcodelineno-8-12)};
`

## Run on a Device and Verify[¶](#run-on-a-device-and-verify "Permanent link")

Build and launch on a device or emulator so the native Storage SDKs are used:

`[](#%5F%5Fcodelineno-9-1)npx cap sync
[](#%5F%5Fcodelineno-9-2)npx cap run android   # or: npx cap run ios
`

Upload a file from your UI, then confirm it landed:

* **In the Firebase console**, open **Storage → Files** — your uploaded file should appear at the path you used.
* **In your app**, call `getDownloadUrl(...)` for that path and confirm you get back a working URL.

A permission error here almost always means your security rules (Step 1) rejected an unauthenticated request, not a bug in the upload call.

## Firebase Cloud Storage Best Practices[¶](#firebase-cloud-storage-best-practices "Permanent link")

### Prefer the Filesystem Plugin for Files You Need to Keep Around[¶](#prefer-the-filesystem-plugin-for-files-you-need-to-keep-around "Permanent link")

`downloadFile(...)` works, but for anything you need to reference again later, resuming a paused download, showing a locally cached copy, it's often cleaner to fetch just the [getDownloadUrl(...)](/docs/sdks/capacitor/firebase/cloud-storage/#getdownloadurl) and hand it to the official [Capacitor Filesystem](https://capacitorjs.com/docs/apis/filesystem) plugin, which gives you full control over the destination directory and file naming:

`[](#%5F%5Fcodelineno-10-1)import { FirebaseStorage } from '@capacitor-firebase/storage';
[](#%5F%5Fcodelineno-10-2)import { Filesystem, Directory } from '@capacitor/filesystem';
[](#%5F%5Fcodelineno-10-3)
[](#%5F%5Fcodelineno-10-4)const downloadWithFilesystem = async () => {
[](#%5F%5Fcodelineno-10-5)  const { downloadUrl } = await FirebaseStorage.getDownloadUrl({
[](#%5F%5Fcodelineno-10-6)    path: 'images/mountains.png',
[](#%5F%5Fcodelineno-10-7)  });
[](#%5F%5Fcodelineno-10-8)  const { path } = await Filesystem.downloadFile({
[](#%5F%5Fcodelineno-10-9)    url: downloadUrl,
[](#%5F%5Fcodelineno-10-10)    path: 'mountains.png',
[](#%5F%5Fcodelineno-10-11)    directory: Directory.Cache,
[](#%5F%5Fcodelineno-10-12)  });
[](#%5F%5Fcodelineno-10-13)  return path;
[](#%5F%5Fcodelineno-10-14)};
`

For a deeper look at managing files locally without running into memory issues, see [Capacitor File Handling: The Complete Guide](/blog/capacitor-file-handling-guide/). If the goal is letting the user open the downloaded file (a PDF receipt, a downloaded document) rather than just caching it, the [Capacitor File Opener plugin](/docs/sdks/capacitor/file-opener/) hands it off to the device's default app for that file type in one call.

### Track Progress Correctly, Including the Platform Gap[¶](#track-progress-correctly-including-the-platform-gap "Permanent link")

Both `uploadFile(...)` and `downloadFile(...)` report `progress` as a fraction between 0 and 1 on every platform, but `bytesTransferred` and `totalBytes` are **only available on Android and Web**, not iOS. If you're building a progress bar that shows "12 MB of 40 MB", it needs a fallback (percentage only) for iOS rather than assuming those two fields will always be there.

### Paginate Large Directories Explicitly[¶](#paginate-large-directories-explicitly "Permanent link")

[listFiles(...)](/docs/sdks/capacitor/firebase/cloud-storage/#listfiles) defaults to a `maxResults` of 1000\. If a directory can realistically grow past that (a shared uploads folder, a growing media library), don't assume you got everything back on the first call, check whether a `pageToken` was returned and keep paginating instead of silently truncating your file list.

### Understand the Default Security Rules Before You Get "Permission Denied"[¶](#understand-the-default-security-rules-before-you-get-permission-denied "Permanent link")

Cloud Storage ships with one of two rule modes when you set it up: **locked mode**, Firebase's default, which only allows access to **authenticated users**, or test mode, which allows anyone. If uploads or downloads fail with a permission error the moment you test on a real project, check your Storage Security Rules before assuming the plugin is broken; it's very often that the current user isn't authenticated at all. See the [Capacitor Firebase Authentication guide](/blog/capacitor-firebase-authentication-guide/) if you haven't wired up sign-in yet.

### Set `contentType` When Uploading[¶](#set-contenttype-when-uploading "Permanent link")

Without an explicit `contentType` in the upload metadata, some clients (particularly browsers) may not render or handle the file the way you expect, an image might download instead of displaying inline, for example. Set it explicitly for anything you intend to display directly rather than relying on Firebase's default inference:

`[](#%5F%5Fcodelineno-11-1)import { FirebaseStorage } from '@capacitor-firebase/storage';
[](#%5F%5Fcodelineno-11-2)
[](#%5F%5Fcodelineno-11-3)const uploadWithMetadata = async () => {
[](#%5F%5Fcodelineno-11-4)  return new Promise((resolve, reject) => {
[](#%5F%5Fcodelineno-11-5)    FirebaseStorage.uploadFile(
[](#%5F%5Fcodelineno-11-6)      {
[](#%5F%5Fcodelineno-11-7)        path: 'images/mountains.png',
[](#%5F%5Fcodelineno-11-8)        uri: 'file:///.../mountains.png',
[](#%5F%5Fcodelineno-11-9)        metadata: { contentType: 'image/png' },
[](#%5F%5Fcodelineno-11-10)      },
[](#%5F%5Fcodelineno-11-11)      (event, error) => {
[](#%5F%5Fcodelineno-11-12)        if (error) {
[](#%5F%5Fcodelineno-11-13)          reject(error);
[](#%5F%5Fcodelineno-11-14)        } else if (event?.completed) {
[](#%5F%5Fcodelineno-11-15)          resolve();
[](#%5F%5Fcodelineno-11-16)        }
[](#%5F%5Fcodelineno-11-17)      },
[](#%5F%5Fcodelineno-11-18)    );
[](#%5F%5Fcodelineno-11-19)  });
[](#%5F%5Fcodelineno-11-20)};
`

### Let Users Pick the File, Don't Build Your Own File Browser[¶](#let-users-pick-the-file-dont-build-your-own-file-browser "Permanent link")

For letting users choose what to upload, pair Storage with the [Capacitor File Picker plugin](/docs/sdks/capacitor/file-picker/) instead of building custom file-selection UI. Its result already gives you exactly what `uploadFile(...)` needs: a `path` on Android/iOS and a `blob` on Web.

`` [](#%5F%5Fcodelineno-12-1)import { FilePicker } from '@capawesome/capacitor-file-picker';
[](#%5F%5Fcodelineno-12-2)import { FirebaseStorage } from '@capacitor-firebase/storage';
[](#%5F%5Fcodelineno-12-3)
[](#%5F%5Fcodelineno-12-4)const pickAndUpload = async () => {
[](#%5F%5Fcodelineno-12-5)  const { files } = await FilePicker.pickFiles();
[](#%5F%5Fcodelineno-12-6)  const file = files[0];
[](#%5F%5Fcodelineno-12-7)  return new Promise((resolve, reject) => {
[](#%5F%5Fcodelineno-12-8)    FirebaseStorage.uploadFile(
[](#%5F%5Fcodelineno-12-9)      { path: `uploads/${file.name}`, uri: file.path, blob: file.blob },
[](#%5F%5Fcodelineno-12-10)      (event, error) => {
[](#%5F%5Fcodelineno-12-11)        if (error) {
[](#%5F%5Fcodelineno-12-12)          reject(error);
[](#%5F%5Fcodelineno-12-13)        } else if (event?.completed) {
[](#%5F%5Fcodelineno-12-14)          resolve();
[](#%5F%5Fcodelineno-12-15)        }
[](#%5F%5Fcodelineno-12-16)      },
[](#%5F%5Fcodelineno-12-17)    );
[](#%5F%5Fcodelineno-12-18)  });
[](#%5F%5Fcodelineno-12-19)};
 ``

### Compress Images Before Uploading[¶](#compress-images-before-uploading "Permanent link")

If you're uploading a lot of user-submitted photos, compressing them first saves both storage costs and upload time, especially on mobile networks. The [Capacitor File Compressor plugin](/docs/sdks/capacitor/file-compressor/), a [Capawesome Insiders](https://capawesome.io/insiders/) plugin, exists specifically for this and accepts the same `path`/`blob` shape Storage uses:

`[](#%5F%5Fcodelineno-13-1)import { FileCompressor } from '@capawesome-team/capacitor-file-compressor';
[](#%5F%5Fcodelineno-13-2)
[](#%5F%5Fcodelineno-13-3)const compressBeforeUpload = async (path: string) => {
[](#%5F%5Fcodelineno-13-4)  const { path: compressedPath } = await FileCompressor.compressImage({
[](#%5F%5Fcodelineno-13-5)    path,
[](#%5F%5Fcodelineno-13-6)    mimeType: 'image/jpeg',
[](#%5F%5Fcodelineno-13-7)    quality: 0.7,
[](#%5F%5Fcodelineno-13-8)  });
[](#%5F%5Fcodelineno-13-9)  return compressedPath;
[](#%5F%5Fcodelineno-13-10)};
`

This is exactly the pattern used in the [Trip Expenses demo app](/blog/how-to-wrap-an-angular-app-with-capacitor-and-firebase/): pick a receipt image, compress it, then upload it to Cloud Storage.

## Limitations[¶](#limitations "Permanent link")

* The `blob` option on `uploadFile(...)` is only available on Web; `uri` is only available on Android and iOS.
* The `uri` option on `downloadFile(...)` (where the file is saved) is only available on Android and iOS; on web the file comes back as a `Blob` in the callback instead.
* `bytesTransferred` and `totalBytes` on upload/download progress events are only available on Android and Web, not iOS.

## Common Errors and Troubleshooting[¶](#common-errors-and-troubleshooting "Permanent link")

* **`storage/unauthorized` or permission denied on upload/download.** Your Storage security rules are rejecting the request — usually an unauthenticated user hitting rules that require `request.auth != null`. Confirm sign-in and check the rules (Step 1).
* **App crashes on launch, or Storage isn't configured.** The `google-services.json` / `GoogleService-Info.plist` file is missing, misplaced, or (on iOS) wasn't added to the Xcode project. Re-check Step 3 and run `npx cap sync`.
* **`uri` is undefined on web, or `blob` is undefined on native.** `uri` is Android/iOS only and `blob` is web only — pass the right one per platform. A [File Picker](/docs/sdks/capacitor/file-picker/) result already gives you both.
* **A progress bar shows `NaN of NaN` on iOS.** `bytesTransferred` and `totalBytes` don't exist on iOS — fall back to the `progress` fraction (0–1) there.
* **`listFiles(...)` seems to miss files.** It returns at most `maxResults` (1000 by default) per call. Keep paginating with the returned `pageToken` until it's absent.

## FAQ[¶](#faq "Permanent link")

### How do I upload an image to Firebase Cloud Storage in a Capacitor app?[¶](#how-do-i-upload-an-image-to-firebase-cloud-storage-in-a-capacitor-app "Permanent link")

Pick the file with the [Capacitor File Picker plugin](/docs/sdks/capacitor/file-picker/), then pass its result to [uploadFile(...)](/docs/sdks/capacitor/firebase/cloud-storage/#uploadfile) — a `uri` on Android and iOS, or a `blob` on the web. The callback reports progress and sets `completed` to `true` when the upload finishes. Make sure the user is signed in first if your security rules require it.

### Is Cloud Storage free to use?[¶](#is-cloud-storage-free-to-use "Permanent link")

There's a free tier (a modest amount of stored data plus a daily download quota), then usage-based pricing for storage and bandwidth beyond that, unlike Firebase Analytics, which has no usage-based cost at all. Exact quotas differ depending on your bucket type and region, so check the current [Firebase pricing page](https://firebase.google.com/pricing) rather than assuming a fixed number.

### Why am I getting a permission-denied error on upload or download?[¶](#why-am-i-getting-a-permission-denied-error-on-upload-or-download "Permanent link")

Almost always the Storage Security Rules, not the plugin. Firebase's default "locked mode" rules only allow access to authenticated users, so an anonymous or signed-out user will get denied by design. Confirm the user is actually signed in, and check your project's Storage Rules in the Firebase console before assuming there's a bug in the upload/download call itself.

### What happens if a directory has more than 1000 files?[¶](#what-happens-if-a-directory-has-more-than-1000-files "Permanent link")

`listFiles(...)` only returns up to `maxResults` (1000 by default) per call. If the directory has more, the result includes a `pageToken` you pass into the next call to continue listing, rather than the method returning everything at once.

## Ship Upload Fixes Without a Full Release[¶](#ship-upload-fixes-without-a-full-release "Permanent link")

The code that picks, compresses, and uploads files runs in your app's web layer, so a fix to that flow doesn't have to wait on an app store review. [Capawesome Cloud](https://capawesome.io/) builds your iOS and Android apps in the cloud and pushes web-layer changes straight to users with live updates — handy when a file-handling edge case only shows up in production — and automates App Store and Play Store submission when you do ship a native release.

[Book a Capawesome Cloud Demo](https://cal.com/team/capawesome/cloud-demo)

## Conclusion[¶](#conclusion "Permanent link")

Cloud Storage's role in a Capacitor app is straightforward: get files in and out reliably across Android, iOS, and web. The parts worth planning for up front are the platform gaps: `blob` vs. `uri`, progress fields that don't exist on iOS, pagination past 1000 files, and Security Rules that deny anonymous access by default.

If you want to go deeper from here:

* [Firebase Authentication in Capacitor: Setup & Best Practices](/blog/capacitor-firebase-authentication-guide/) — needed if your Storage rules require a signed-in user.
* [Capacitor Firestore: Real-Time Data & Offline Sync](/blog/capacitor-firebase-cloud-firestore-guide/) — often paired with Storage to save file references alongside structured data.
* [Track App Events with Firebase Analytics in Capacitor](/blog/capacitor-firebase-analytics-guide/) — the sibling Firebase plugin for measuring how users engage with your app.
* [Capacitor File Handling: The Complete Guide](/blog/capacitor-file-handling-guide/) — for managing downloaded files locally without running into memory issues.

Questions or something you ran into that isn't covered here? Drop into the [Capawesome Discord server](https://discord.gg/VCXxSVjefW) — and subscribe to the [Capawesome newsletter](https://capawesome.io/newsletter/) if you want the next deep-dive in your inbox.

July 29, 2026 

Back to top

```json
{
      "@context": "https://schema.org",
      "@type": "BlogPosting",
      "headline": "Upload \u0026 Manage Files with Firebase Storage in Capacitor",
      "description": "Store user files in a Capacitor app with Firebase Cloud Storage, covering bucket setup, uploads and downloads, security rules, and troubleshooting.",
      "image": "https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png",
      "datePublished": "2026-07-31T00:00:00+00:00",
      "dateModified": "2026-07-31T00:00:00+00:00",
      "author": [
        {
          "@type": "Person",
          "name": "Dayana Jabif",
          "url": "https://github.com/djabif"
        }
      ],
      "publisher": {
        "@type": "Organization",
        "name": "Capawesome",
        "url": "https://capawesome.io",
        "logo": {
          "@type": "ImageObject",
          "url": "https://capawesome.io/assets/images/logo.svg"
        }
      },
      "articleSection": "Capacitor",
      "keywords": ["Capacitor", "Firebase", "Guides", "SDKs"],
      "isPartOf": {
        "@type": "Blog",
        "@id": "https://capawesome.io/blog/#blog"
      },
      "mainEntityOfPage": "https://capawesome.io/blog/capacitor-firebase-cloud-storage-guide/",
      "url": "https://capawesome.io/blog/capacitor-firebase-cloud-storage-guide/"
    }
{
      "@context": "https://schema.org",
      "@type": "BreadcrumbList",
      "itemListElement": [
        {
          "@type": "ListItem",
          "position": 1,
          "name": "Home",
          "item": "https://capawesome.io/"
        },
        {
          "@type": "ListItem",
          "position": 2,
          "name": "Blog",
          "item": "https://capawesome.io/blog/"
        },
        {
          "@type": "ListItem",
          "position": 3,
          "name": "Upload \u0026 Manage Files with Firebase Storage in Capacitor",
          "item": "https://capawesome.io/blog/capacitor-firebase-cloud-storage-guide/"
        }
      ]
    }
{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What Is Cloud Storage?", "acceptedAnswer": {"@type": "Answer", "text": "Cloud Storage for Firebase is Google's object storage service for user-generated content: images, videos, documents, anything you'd otherwise store as a file. The Capacitor Firebase Cloud Storage plugin exposes the native Android and iOS Storage SDKs, plus the Firebase JS SDK on web, behind one shared TypeScript API. A working example can be found here: capawesome-team/capacitor-firebase-plugin-demo."}}, {"@type": "Question", "name": "Why Not Just Use the Firebase JS SDK?", "acceptedAnswer": {"@type": "Answer", "text": "Uploading and downloading through the JS SDK inside a WebView means routing file data through the WebView's JavaScript bridge, which gets expensive for anything beyond small files, large uploads and downloads risk timeouts or memory pressure that the native SDKs handle more gracefully. The native SDKs also integrate directly with each platform's file system: on Android and iOS you pass a file uri straight to uploadFile(...) or downloadFile(...), no intermediate Blob conversion required, which only exists as a concept on web. Advanced Operations This guide covers the operations most apps need. For every method with code samples, see the Usage section of the plugin docs."}}, {"@type": "Question", "name": "Why Use Firebase Storage in a Capacitor App?", "acceptedAnswer": {"@type": "Answer", "text": "The plugin's own use cases map to four real scenarios: User-generated content. Upload photos or other files created by your users with uploadFile(...). Pass a uri on Android/iOS or a blob on web: import { FirebaseStorage } from '@capacitor-firebase/storage'; const uploadFile = async () => { return new Promise (( resolve, reject) => { FirebaseStorage. uploadFile ( { path: 'images/mountains.png', uri: 'file:///.../mountains.png' }, ( event, error) => { if ( error) { reject ( error); } else if ( event?. completed) { resolve (); } },); }); }; File downloads. Download files to the local file system on Android/iOS, or as a Blob on web, with downloadFile(...). Content delivery. Retrieve a file's download URL with getDownloadUrl(...) to display or share it: import { FirebaseStorage } from '@capacitor-firebase/storage'; const getDownloadUrl = async () => { const { downloadUrl } = await FirebaseStorage. getDownloadUrl ({ path: 'images/mountains.png', }); return downloadUrl; }; File management. List files in a directory with listFiles(...), read/update metadata, or delete files: import { FirebaseStorage } from '@capacitor-firebase/storage'; const listFiles = async () => { const { items } = await FirebaseStorage. listFiles ({ path: 'images' }); return items; };"}}, {"@type": "Question", "name": "How do I upload an image to Firebase Cloud Storage in a Capacitor app?", "acceptedAnswer": {"@type": "Answer", "text": "Pick the file with the Capacitor File Picker plugin, then pass its result to uploadFile(...) — a uri on Android and iOS, or a blob on the web. The callback reports progress and sets completed to true when the upload finishes. Make sure the user is signed in first if your security rules require it."}}, {"@type": "Question", "name": "Is Cloud Storage free to use?", "acceptedAnswer": {"@type": "Answer", "text": "There's a free tier (a modest amount of stored data plus a daily download quota), then usage-based pricing for storage and bandwidth beyond that, unlike Firebase Analytics, which has no usage-based cost at all. Exact quotas differ depending on your bucket type and region, so check the current Firebase pricing page rather than assuming a fixed number."}}, {"@type": "Question", "name": "Why am I getting a permission-denied error on upload or download?", "acceptedAnswer": {"@type": "Answer", "text": "Almost always the Storage Security Rules, not the plugin. Firebase's default \"locked mode\" rules only allow access to authenticated users, so an anonymous or signed-out user will get denied by design. Confirm the user is actually signed in, and check your project's Storage Rules in the Firebase console before assuming there's a bug in the upload/download call itself."}}, {"@type": "Question", "name": "What happens if a directory has more than 1000 files?", "acceptedAnswer": {"@type": "Answer", "text": "listFiles(...) only returns up to maxResults (1000 by default) per call. If the directory has more, the result includes a pageToken you pass into the next call to continue listing, rather than the method returning everything at once."}}], "url": "https://capawesome.io/blog/capacitor-firebase-cloud-storage-guide/"}
```
