---
title: Announcing the Capacitor File Manager Plugin
description: Our new Capacitor File Manager plugin adds persisted folder access, directory operations with progress and cancellation, checksums, and real web support.
date:
  created: 2026-09-21
  updated: 2026-09-21
authors:
  - robingenz
categories:
  - Announcements
  - Capacitor
  - SDKs
links:
  - Capacitor File Manager: sdks/capacitor/file-manager.md
faq: true
---

# Announcing the Capacitor File Manager Plugin

Sooner or later an app has to work outside its own sandbox. The user wants the export in a folder they picked themselves, a recursive copy needs a progress bar and a cancel button, and a downloaded file should be verified before anyone opens it. The official `@capacitor/filesystem` plugin covers none of that. Today we're announcing the [Capacitor File Manager plugin](../../sdks/capacitor/file-manager.md) by Capawesome, a URI-based API for files and directories that keeps access to user-picked folders across app launches, reports progress for directory operations, calculates MD5, SHA-1 and SHA-256 checksums, and stores web files in the Origin Private File System. It's available to all Capawesome [Insiders](../../insiders/index.md).

<!-- more -->

<div class="capawesome-z29o10a">
  <a href="https://capawesome.io/" target="_blank">
    <img alt="Build and deploy your Capacitor app with Capawesome Cloud" src="https://capawesome.io/assets/banners/cloud-build-and-deploy-capacitor-apps.png?t=1" />
  </a>
</div>

**Key takeaways:**

- `@capawesome-team/capacitor-file-manager` replaces the sandbox operations of `@capacitor/filesystem` with a URI-based API, requires Capacitor 8 or later, and is part of the Capawesome Insiders subscription.
- `persistDirectoryAccess(...)` keeps read and write access to a folder the user picked across app launches on Android and iOS, and the plugin needs no storage permissions.
- `copyDirectory(...)`, `moveDirectory(...)` and `deleteDirectory(...)` emit `operationProgress` events and can be stopped with `cancelOperationById(...)`, which rejects the operation with `OPERATION_CANCELED`.
- `getFileChecksum(...)` streams MD5, SHA-1 and SHA-256 checksums with constant memory usage on Android and iOS, and `readFileAsBlob(...)` reads large files without base64 overhead.
- Web files live in the Origin Private File System with real directories and random access, while persisted directories and checksums stay Android and iOS only.

## Beyond the sandbox

The official `@capacitor/filesystem` plugin reads and writes files inside your app's own directories, and that is where it stops. Its `Directory.ExternalStorage` member is documented as inaccessible on Android 11 and newer, and `Directory.Documents` only shows files your own app created there. The legacy storage permissions do not bring those paths back, which [Android scoped storage in Capacitor apps](./android-scoped-storage-in-capacitor-apps.md) explains in detail.

Three more gaps show up once files get bigger and folders get deeper. Its `copy(...)` is fire and forget, with no progress events and no way to stop a copy the user started by accident. Its `readFile(...)` returns a string on native, so a whole large file lands base64-encoded in memory, which is why the documentation suggests `android:largeHeap="true"`. And there is no checksum, no directory size, no free-space query and no existence check.

The Capacitor File Manager plugin adds four groups of capabilities on top of that layer:

- **Persisted folder access**: durable read and write access to folders the user picked, on Android and iOS.
- **Directory operations**: recursive copy, move, delete and clear with progress events and cancellation.
- **Constant-memory file I/O**: random-access reads and writes, appends, truncation and `Blob` reads.
- **Inspection**: metadata, recursive directory sizes, checksums, and device and app storage usage.

If you are mapping out file handling in a Capacitor app more broadly, the [Capacitor file handling guide](./capacitor-file-handling-guide.md) covers the surrounding pieces, from reading and displaying files to sharing, compressing and uploading them.

## Installation

To install the Capacitor File Manager plugin, please refer to the [Installation](../../sdks/capacitor/file-manager.md#installation) section in the plugin documentation. It's published to the Capawesome npm registry, so installation requires the license key that comes with a [Capawesome Insiders](../../insiders/index.md) subscription.

The platform setup is short. The plugin requires no storage permissions on Android, because sandbox directories need none and user-visible folders are granted by the user in the system file picker. There is no configuration to add on either platform.

## Usage

Every method of the plugin takes a URI, so the usual flow is to build a URI once and then pass it around.

### Address files by URI

[`getUri(...)`](../../sdks/capacitor/file-manager.md#geturi) constructs the URI of a file or directory that does not have to exist yet. Pass a `path` together with either `directory` for a well-known sandbox directory or `parentUri` for any directory URI you already hold:

```typescript
import { Directory, Encoding, FileManager } from '@capawesome-team/capacitor-file-manager';

const writeAndReadFile = async () => {
  const { uri } = await FileManager.getUri({
    path: 'notes/todo.txt',
    directory: Directory.Data,
  });
  await FileManager.writeFile({
    uri,
    data: 'Hello, World!',
    encoding: Encoding.Utf8,
    recursive: true,
  });
  const { data } = await FileManager.readFile({ uri, encoding: Encoding.Utf8 });
  return data;
};
```

`getUri(...)` resolves with a `uri` string, [`writeFile(...)`](../../sdks/capacitor/file-manager.md#writefile) resolves with the URI of the written file, and [`readFile(...)`](../../sdks/capacitor/file-manager.md#readfile) resolves with the contents as `data` in the requested encoding. Use the URI that `writeFile(...)`, [`copyFile(...)`](../../sdks/capacitor/file-manager.md#copyfile) and [`moveFile(...)`](../../sdks/capacitor/file-manager.md#movefile) return rather than the one you passed in, since a document provider may rename a file to avoid a collision.

### Keep folder access

Persisted access starts in the [Capacitor File Picker plugin](../../sdks/capacitor/file-picker.md). The user picks a folder with [`pickDirectory()`](../../sdks/capacitor/file-picker.md#pickdirectory), you hand the returned `path` and `bookmark` to [`persistDirectoryAccess(...)`](../../sdks/capacitor/file-manager.md#persistdirectoryaccess), and the folder stays readable and writable after the next app start. The `bookmark` is the iOS security-scoped bookmark and requires File Picker `8.1.0` or later:

```typescript
import { FileManager } from '@capawesome-team/capacitor-file-manager';
import { FilePicker } from '@capawesome/capacitor-file-picker';

const persistDirectoryAccess = async () => {
  const result = await FilePicker.pickDirectory();
  const { directory } = await FileManager.persistDirectoryAccess({
    uri: result.path,
    bookmark: result.bookmark,
  });
  return directory;
};

const getPersistedDirectories = async () => {
  // Call this on app start and use the returned URIs
  // instead of storing them yourself.
  const { directories } = await FileManager.getPersistedDirectories();
  return directories;
};
```

A persisted URI can change between app launches, so read the current list with [`getPersistedDirectories()`](../../sdks/capacitor/file-manager.md#getpersisteddirectories) on start instead of keeping your own copy. That call also refreshes stale entries and drops folders whose document is gone. [`releaseDirectoryAccess(...)`](../../sdks/capacitor/file-manager.md#releasedirectoryaccess) gives a folder back when the user revokes it in your settings screen, and [`clearDirectory(...)`](../../sdks/capacitor/file-manager.md#cleardirectory) empties a folder without destroying the grant. The Android and iOS mechanics behind these methods, including the tree URIs and the picker's own restrictions, are covered in [Android scoped storage in Capacitor apps](./android-scoped-storage-in-capacitor-apps.md#persisted-directories).

### Copy with progress

[`copyDirectory(...)`](../../sdks/capacitor/file-manager.md#copydirectory), [`moveDirectory(...)`](../../sdks/capacitor/file-manager.md#movedirectory) and [`deleteDirectory(...)`](../../sdks/capacitor/file-manager.md#deletedirectory) work recursively and report how far along they are. Give the call an `id`, subscribe to [`operationProgress`](../../sdks/capacitor/file-manager.md#addlisteneroperationprogress-), and stop the operation with [`cancelOperationById(...)`](../../sdks/capacitor/file-manager.md#canceloperationbyid):

```typescript
import { FileManager } from '@capawesome-team/capacitor-file-manager';

const copyDirectoryWithProgress = async (uri: string, toUri: string) => {
  await FileManager.addListener('operationProgress', (event) => {
    console.log(`Processed ${event.processedFiles} of ${event.totalFiles} files`);
  });
  await FileManager.copyDirectory({ uri, toUri, id: 'my-copy-operation' });
};

const cancelCopy = async () => {
  await FileManager.cancelOperationById({ id: 'my-copy-operation' });
};
```

The event carries `processedFiles`, `totalFiles`, `processedBytes`, `totalBytes`, `operationType` and the `id` you passed, so a single listener can drive several progress bars at once. Both totals are `null` until the plugin knows them. A canceled operation rejects with the `OPERATION_CANCELED` error code, and the files it already processed stay where they are, so a canceled copy leaves partial results behind for you to clean up. Deleting a directory with `recursive: false` rejects with `NOT_EMPTY` when the folder still has contents.

### Read large files

[`readFileAsBlob(...)`](../../sdks/capacitor/file-manager.md#readfileasblob) is the way to get a whole large file into your web code. The file is streamed into a `Blob` without base64 encoding in between, so memory usage stays flat no matter how big it is:

```typescript
import { FileManager } from '@capawesome-team/capacitor-file-manager';

const readLargeFile = async (uri: string) => {
  // Streams the file without base64 overhead.
  const { blob } = await FileManager.readFileAsBlob({ uri });
  return blob;
};
```

`readFile(...)` covers the other case. It reads the whole file into memory and encodes it as base64, and its `offset` and `length` options are meant for targeted reads such as a file header or a byte range. Do not walk a large file with a growing `offset` in a loop, because some storage providers have to skip to the offset again on every call, which makes each iteration slower than the last. Writing works the same way. `writeFile(...)` takes a `position` to overwrite a byte range while preserving everything outside it, [`appendFile(...)`](../../sdks/capacitor/file-manager.md#appendfile) adds to the end, and [`truncateFile(...)`](../../sdks/capacitor/file-manager.md#truncatefile) cuts a file down to a given size.

### Verify with checksums

[`getFileChecksum(...)`](../../sdks/capacitor/file-manager.md#getfilechecksum) computes an MD5, SHA-1 or SHA-256 checksum over a file of any size. The file is processed in a streaming manner, so memory usage does not grow with it:

```typescript
import { ChecksumAlgorithm, FileManager } from '@capawesome-team/capacitor-file-manager';

const verifyFile = async (uri: string, expectedChecksum: string) => {
  const { checksum } = await FileManager.getFileChecksum({
    uri,
    algorithm: ChecksumAlgorithm.Sha256,
  });
  return checksum === expectedChecksum;
};
```

The result is a lowercase hexadecimal string, which compares directly against the checksum your backend sent along with the download. Checksums are available on Android and iOS only.

### Check storage space

[`getDeviceStorageInfo()`](../../sdks/capacitor/file-manager.md#getdevicestorageinfo) returns `totalBytes`, `freeBytes` and `allocatableBytes`, the last being the space the system could still free up for your app by deleting caches. A large export can check it first and fail early instead of halfway through:

```typescript
import { FileManager } from '@capawesome-team/capacitor-file-manager';

const checkStorage = async () => {
  const { freeBytes } = await FileManager.getDeviceStorageInfo();
  const { cacheBytes } = await FileManager.getAppStorageInfo();
  if (cacheBytes > 100_000_000) {
    await FileManager.clearCache();
  }
  return freeBytes;
};
```

[`getAppStorageInfo()`](../../sdks/capacitor/file-manager.md#getappstorageinfo) reports how much space your own app occupies as `cacheBytes` and `dataBytes`, and [`clearCache()`](../../sdks/capacitor/file-manager.md#clearcache) empties the cache directory behind a settings toggle. For a single folder, [`getDirectorySize(...)`](../../sdks/capacitor/file-manager.md#getdirectorysize) returns the recursive size in bytes. Values the platform cannot provide come back as `null` rather than zero.

## Real web support

On the web, the plugin stores files in the [Origin Private File System](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system){:target="_blank"}, a per-origin storage endpoint that MDN lists as available across browsers since March 2023. It requires a secure context, needs no permission prompt, and gives the plugin real directories, streaming and random access. The plugin's README contrasts that with the usual web fallback of base64 blobs in IndexedDB.

Every `Directory` member resolves to an `opfs://` URI on the web, so `Directory.Data` becomes `opfs://data`. Those URIs are internal to the plugin, so pass data to other web APIs with `readFileAsBlob(...)` rather than handing the URI along. Persisted directories and checksums are not available on the web.

The Origin Private File System lives under the browser's storage quota and disappears when the user clears site data. Chromium-based browsers and Firefox only evict it under storage pressure, but Safari deletes all script-writable storage after seven days without user interaction when the app runs in a browser tab. Web apps added to the home screen are exempt from that rule. Calling [`navigator.storage.persist()`](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persist){:target="_blank"} at a moment of real engagement, such as after sign-in, lowers the risk. Browsers grant persistence at their own discretion, so it lowers the risk without removing it.

## Migrating from @capacitor/filesystem

The plugin is a replacement for the sandbox operations of `@capacitor/filesystem`, and most of the migration is mechanical. Instead of passing `path` and `directory` to every method, construct the URI once and pass it along:

```typescript
// Before (@capacitor/filesystem)
const result = await Filesystem.readFile({ path: 'text.txt', directory: Directory.Data });

// After (@capawesome-team/capacitor-file-manager)
const { uri } = await FileManager.getUri({ path: 'text.txt', directory: Directory.Data });
const result = await FileManager.readFile({ uri });
```

The `Directory` enum keeps the same member names and values, minus `ExternalStorage`, which has no successor because direct access to shared storage is blocked on Android 11 and newer anyway. Persisted directories take its place. `Encoding.ASCII` and `Encoding.UTF16` are dropped as well, leaving `Encoding.Utf8` and `Encoding.Base64`.

| `@capacitor/filesystem` | `@capawesome-team/capacitor-file-manager` |
| --- | --- |
| `readFile(...)` | `getUri(...)` + `readFile(...)` |
| `writeFile(...)` | `getUri(...)` + `writeFile(...)` |
| `readdir(...)` | `getUri(...)` + `readDirectory(...)` |
| `mkdir(...)` / `rmdir(...)` | `getUri(...)` + `createDirectory(...)` / `deleteDirectory(...)` |
| `copy(...)` | `getUri(...)` + `copyFile(...)` or `copyDirectory(...)` |
| `rename(...)` | `getUri(...)` + `moveFile(...)` or `moveDirectory(...)` |
| `stat(...)` | `getUri(...)` + `getMetadata(...)` |
| `readFileInChunks(...)` | `readFileAsBlob(...)` |
| `downloadFile(...)` | Capacitor File Transfer plugin |
| `checkPermissions()` / `requestPermissions()` | Not needed. Persisted directories replace the legacy storage permissions. |

Errors change shape too. Where `@capacitor/filesystem` rejects with numbered codes such as `OS-PLUG-FILE-0004`, this plugin rejects with named codes you can branch on, among them `OPERATION_CANCELED`, `WRITE_FAILED`, `NOT_EMPTY` and `OPERATION_FAILED`. The [migration section](../../sdks/capacitor/file-manager.md#migration-from-capacitorfilesystem) of the documentation carries the full method mapping.

## Companion plugins

Four other Capawesome plugins get files in and out of the app. The [Capacitor File Picker plugin](../../sdks/capacitor/file-picker.md) lets the user select files and folders, the [Capacitor File Opener plugin](../../sdks/capacitor/file-opener.md) hands a file to the app that can display it, the [Capacitor File Transfer plugin](../../sdks/capacitor/file-transfer.md) downloads and uploads files in the background, and the [Capacitor Zip plugin](../../sdks/capacitor/zip.md) compresses and extracts archives.

Sandbox `file://` URIs pass into all of them wherever a path is expected. Persisted directory URIs do not, since they are `content://` URIs on Android and security-scoped `file://` URIs on iOS. Copy such a file into `Directory.Cache` with `copyFile(...)` first, work on it there, and copy the result back.

## FAQ

### Is there a Capacitor plugin for managing files and folders beyond @capacitor/filesystem?

Yes. The [Capacitor File Manager plugin](../../sdks/capacitor/file-manager.md) by Capawesome covers the operations `@capacitor/filesystem` leaves out, including persisted access to folders the user picked, recursive copy, move and delete with progress and cancellation, MD5, SHA-1 and SHA-256 checksums, random-access reads and writes, recursive directory sizes, and device and app storage usage. It runs on Android, iOS and the web and requires Capacitor 8 or later.

### Does it replace @capacitor/filesystem?

For sandbox file operations, yes. The plugin has its own `readFile(...)`, `writeFile(...)`, `copyFile(...)`, `moveFile(...)`, `readDirectory(...)` and `getMetadata(...)`, and the documentation maps every `@capacitor/filesystem` method to its counterpart. Downloads are the exception, since `downloadFile(...)` is covered by the [Capacitor File Transfer plugin](../../sdks/capacitor/file-transfer.md) instead.

### Does the plugin need storage permissions?

No. Sandbox directories are accessible without any permission, and user-visible folders are granted by the user in the system file picker rather than through a runtime permission. `Directory.Documents` therefore works on Android without `READ_EXTERNAL_STORAGE` or `WRITE_EXTERNAL_STORAGE`, because it maps to the app-specific documents directory.

### Does the plugin work on the web?

Yes, with two exceptions. Files are stored in the Origin Private File System, so directories, streaming and random access behave as they do on native. Persisted directories and checksums are available on Android and iOS only.

### How do I read a large file without running out of memory?

Use [`readFileAsBlob(...)`](../../sdks/capacitor/file-manager.md#readfileasblob), which streams the file into a `Blob` with no base64 encoding in between. Keep `readFile(...)` with `offset` and `length` for targeted reads such as a file header. Reading a large file by calling `readFile(...)` in a loop with a growing offset gets slower with every iteration on storage providers that have to skip to the offset again.

### Is the plugin free?

No. The Capacitor File Manager plugin is part of [Capawesome Insiders](../../insiders/index.md), a paid subscription that covers every Insiders plugin. The [Capacitor File Picker plugin](../../sdks/capacitor/file-picker.md) used to pick a folder is not an Insiders plugin.

### Can I use the plugin with Ionic, React, Vue, or Angular?

Yes. The plugin is framework-agnostic and works in any Capacitor app, including Ionic with Angular, React, or Vue, as well as plain JavaScript projects.

## Availability

The [Capacitor File Manager plugin](../../sdks/capacitor/file-manager.md) runs on Android, iOS and the web, requires Capacitor 8 or later, and is part of [Capawesome Insiders](../../insiders/index.md). One subscription covers every other Insiders plugin as well, so there's no separate license to buy.

New plugins and notable releases go out in the Capawesome newsletter first.

[Subscribe to the Capawesome Newsletter](https://capawesome.io/newsletter/){ .md-button .md-button--primary }

## Conclusion

Stay on `@capacitor/filesystem` while your app reads and writes inside its own sandbox and every file is small enough to hold in memory. Switch to the [Capacitor File Manager plugin](../../sdks/capacitor/file-manager.md) as soon as a ticket mentions a folder the user picks and expects to keep, an operation long enough to need a progress bar and a cancel button, or a file whose size or integrity you have to handle.

**Further reading:**

- [The Capacitor file handling guide](./capacitor-file-handling-guide.md) for the wider picture of files in Capacitor apps
- [Android Scoped Storage in Capacitor Apps, Explained](./android-scoped-storage-in-capacitor-apps.md#which-directory-to-use) for choosing a directory on Android
- [API Reference](../../sdks/capacitor/file-manager.md#api) for every method, option and event

**Missing a feature?** [Create a feature request](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose){:target="_blank"} in our GitHub repository.

If you have any questions, join us on the [Capawesome Discord server](https://discord.gg/VCXxSVjefW){:target="_blank"}. To stay updated on the latest news, subscribe to the [Capawesome newsletter](https://capawesome.io/newsletter/){:target="_blank"}.
