npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@powersync/attachments-storage-react-native

v0.1.1

Published

React Native file system storage adapters for PowerSync attachments

Readme

@powersync/attachments-storage-react-native

[!NOTE] Attachment helpers are currently in an alpha state, intended strictly for testing. Expect breaking changes and instability as development continues.

Do not rely on this package for production use.

React Native storage and transport adapters for PowerSync attachments.

This package provides:

  • Local storage adapters (LocalStorageAdapter) — persist attachment files on device.
  • Streaming transport adapters (AttachmentTransportAdapter) — move attachment bytes directly between the local file and remote storage using native APIs, without buffering the whole file in JS memory. Recommended for large files (recordings, videos) on lower-end devices.

Installation

npm install @powersync/attachments-storage-react-native
# or
pnpm add @powersync/attachments-storage-react-native
# or
yarn add @powersync/attachments-storage-react-native

You'll also need to install one of the supported file system libraries. The same library powers both the local storage adapter and the native transport adapter for that platform.

For Expo projects

[!IMPORTANT] Requires Expo 54+

npx expo install expo-file-system

For bare React Native projects

npm install @dr.pogodin/react-native-fs

Local storage adapters

The local storage adapter handles file persistence on the device. Pass it to the queue as localStorage.

With Expo File System

import { ExpoFileSystemStorageAdapter } from '@powersync/attachments-storage-react-native';
import { AttachmentQueue } from '@powersync/react-native';

const localStorage = new ExpoFileSystemStorageAdapter();

const attachmentQueue = new AttachmentQueue({
  db,
  localStorage,
  remoteStorage, // your RemoteStorageAdapter (buffered upload/download/delete)
  watchAttachments
});

With React Native FS

import { ReactNativeFileSystemStorageAdapter } from '@powersync/attachments-storage-react-native';
import { AttachmentQueue } from '@powersync/react-native';

const localStorage = new ReactNativeFileSystemStorageAdapter();

const attachmentQueue = new AttachmentQueue({
  db,
  localStorage,
  remoteStorage,
  watchAttachments
});

Custom storage directory

Both local adapters accept an optional storageDirectory parameter:

const localStorage = new ExpoFileSystemStorageAdapter('/custom/path/to/attachments/');

Streaming transport adapters

A transport adapter owns all remote operations (upload / download / delete) and transfers bytes natively — the file never enters the JS heap. Implement remote delete via the deleteFile callback.

The queue takes exactly one remote mechanism: either remoteStorage or transportAdapter. A transportAdapter replaces remoteStorage entirely, so none is needed alongside it.

Both transports are backend-agnostic: you supply resolver callbacks that map an attachment to a request (typically a presigned URL from your backend).

With Expo File System (File.upload / File.downloadFileAsync)

[!NOTE] The Expo streaming transport requires Expo SDK 56+ (expo-file-system >=56). The ExpoFileSystemStorageAdapter itself still works on Expo SDK 54+.

import { ExpoFileSystemStorageAdapter } from '@powersync/attachments-storage-react-native';
import { AttachmentQueue } from '@powersync/react-native';

const localStorage = new ExpoFileSystemStorageAdapter();

const transportAdapter = localStorage.createTransportAdapter({
  resolveUpload: async (attachment) => ({
    url: await getSignedUploadUrl(attachment.filename), // from your backend
    httpMethod: 'PUT',
    mimeType: attachment.mediaType ?? 'application/octet-stream'
  }),
  resolveDownload: async (attachment) => ({
    url: await getSignedDownloadUrl(attachment.filename)
  }),
  deleteFile: async (attachment) => {
    await deleteFromRemoteStorage(attachment.filename); // your SDK / DELETE call
  }
});

const attachmentQueue = new AttachmentQueue({
  db,
  localStorage,
  transportAdapter, // owns upload/download/delete — no remoteStorage needed
  watchAttachments
});

With React Native FS (uploadFiles / downloadFile)

Identical options shape. The upload is sent as a raw binary PUT (binaryStreamOnly), suitable for presigned S3/Supabase URLs.

import { ReactNativeFileSystemStorageAdapter } from '@powersync/attachments-storage-react-native';

const localStorage = new ReactNativeFileSystemStorageAdapter();

const transportAdapter = localStorage.createTransportAdapter({
  resolveUpload: async (attachment) => ({
    url: await getSignedUploadUrl(attachment.filename),
    httpMethod: 'PUT',
    mimeType: attachment.mediaType ?? 'application/octet-stream'
  }),
  resolveDownload: async (attachment) => ({
    url: await getSignedDownloadUrl(attachment.filename)
  }),
  deleteFile: async (attachment) => {
    await deleteFromRemoteStorage(attachment.filename);
  }
});

Registering an on-disk file (buffer-free)

For files already written to disk (recordings, camera/picker output), use AttachmentQueue.saveFileFromUri to register them without reading the bytes into memory — the local adapter's moveFile relocates the file into managed storage.

await attachmentQueue.saveFileFromUri({
  localUri, // path to the existing file
  fileExtension: 'm4a',
  mediaType: 'audio/m4a'
});

API

Local storage adapters

Implement the LocalStorageAdapter interface from @powersync/common:

  • initialize() - Create the storage directory if it doesn't exist
  • clear() - Remove all files from the storage directory
  • getLocalUri(filename) - Get the full path for a filename
  • saveFile(filePath, data, options?) - Save data to a file
  • readFile(filePath, options?) - Read a file as ArrayBuffer
  • moveFile(sourceUri, targetUri) - Move a file into managed storage without buffering (enables saveFileFromUri)
  • deleteFile(filePath) - Delete a file
  • fileExists(filePath) - Check if a file exists
  • makeDir(path) - Create a directory
  • rmDir(path) - Remove a directory

Transport adapters

Implement the AttachmentTransportAdapter interface from @powersync/common:

  • upload(attachment) - Transfer the local file to remote storage
  • download(attachment) - Transfer the remote file into attachment.localUri
  • delete(attachment) - Delete the file from remote storage

Supported Versions

| Adapter | Library | Supported Versions | | ------------------------------------- | ----------------------------- | -------------------------------- | | ExpoFileSystemStorageAdapter | expo-file-system | >=19.0.0 (Expo 54+) | | ReactNativeFileSystemStorageAdapter | @dr.pogodin/react-native-fs | ^2.25.0 |

Streaming transports are created via localStorage.createTransportAdapter(...). The Expo streaming transport additionally requires expo-file-system >=56 (Expo SDK 56+).

License

Apache-2.0