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

@letsoft/quq-react-native

v0.2.0

Published

Native React Native / Expo file manager for QuqManager.

Readme

@letsoft/quq-react-native

A native file manager for React Native apps built with Expo, backed by your QuqManager project. It is a real native screen, not a WebView. Your users can:

  • browse folders, with paging and pull-to-refresh;
  • upload from the photo library, the camera and the Files app, with progress and cancel;
  • view images (pinch to zoom, swipe down to close), video and audio;
  • create folders, rename, move to trash and share files;
  • pick files for your app: the picker returns the chosen files to your code with the same onSelect(files) shape as the QuqManager web picker.

The package has no native code of its own. It uses standard Expo modules, which you install next to it in step 1.

Before you start

  • An Expo app on SDK 56 or 57 (React 19, React Native 0.85 or newer). To start a new one:

    npx create-expo-app@latest my-app --template default@sdk-57

    A React Native app without Expo needs the Expo modules system first: npx install-expo-modules@latest.

  • A QuqManager project with managed storage (Pro or Custom plan): in the dashboard, open the project and choose Managed R2 on the Storage tab. Self-hosted APIs don't accept mobile apps yet.

  • The project's API key, from the project's API Key tab.

1. Install the package

In your app's folder:

npx expo install @letsoft/quq-react-native expo-application expo-constants expo-file-system expo-image expo-image-picker expo-document-picker expo-sharing expo-video expo-audio react-native-svg react-native-safe-area-context

npx expo install picks the versions that match your Expo SDK, and adds the expo-image, expo-sharing, expo-video and expo-audio plugins to app.json for you.

2. Configure app.json

Add three things to your app.json, keeping everything that is already there:

  • ios.bundleIdentifier and android.package: your app's IDs. You list them in the QuqManager project in step 3.
  • An expo-image-picker entry in plugins, with the texts iOS shows when the app asks for the photo library, the camera and the microphone (the camera also records video). An app that only takes photos can leave the microphone out: see Photos only.
{
  "expo": {
    "ios": {
      "bundleIdentifier": "com.acme.app"
    },
    "android": {
      "package": "com.acme.app"
    },
    "plugins": [
      ["expo-image-picker", {
        "photosPermission": "Allow $(PRODUCT_NAME) to choose photos to upload.",
        "cameraPermission": "Allow $(PRODUCT_NAME) to take photos to upload.",
        "microphonePermission": "Allow $(PRODUCT_NAME) to record sound with videos you upload."
      }]
    ]
  }
}

Photos only, without the microphone

Video records sound, so by default the app declares the microphone: iOS asks for it with the text above, and Android lists Record audio among the app's permissions. If your app only takes photos, turn the microphone off in both plugins that add it, expo-image-picker and expo-audio (which npx expo install added in step 1):

{
  "expo": {
    "plugins": [
      ["expo-image-picker", {
        "photosPermission": "Allow $(PRODUCT_NAME) to choose photos to upload.",
        "cameraPermission": "Allow $(PRODUCT_NAME) to take photos to upload.",
        "microphonePermission": false
      }],
      ["expo-audio", {
        "microphonePermission": false,
        "recordAudioAndroid": false
      }]
    ]
  }
}

The file manager reads this from your app's config and adapts the camera:

  • With the microphone, the camera takes photos and videos (Take Photo or Video).
  • Without it, the camera takes only photos (Take Photo). On iOS, a camera that switched to video without the microphone declared would crash the app.

Videos already on the phone can still be uploaded from the photo library: that needs no microphone. Rebuild the app after changing the plugins.

3. Allow your app in the QuqManager project

Native apps send no Origin, so the project's allowed domains don't apply to them. In the dashboard, open the project's General tab:

  • Mobile apps: add your iOS bundle ID and Android package name, one per line, for example com.acme.app. A development build with its own ID (com.acme.app.dev) needs its own line. Mobile apps have the same plan limit as allowed domains, counted separately.
  • Allow localhost: turn it on to try the app in Expo Go. Expo Go has its own ID and cannot be listed under Mobile apps.

Save. Until the app is allowed, the file manager shows a screen that names the exact ID to add.

4. Add the file manager screen

In an Expo Router app, a screen is a file in the app folder. The SDK 57 template keeps it in src/app/. Create src/app/files.tsx:

import { useIsFocused } from 'expo-router'
import { SafeAreaView } from 'react-native-safe-area-context'
import { QuqFileManager } from '@letsoft/quq-react-native'

export default function FilesScreen() {
  // Android back goes up a folder only while this tab is the one on screen.
  const isFocused = useIsFocused()
  return (
    <SafeAreaView style={{ flex: 1 }} edges={['top']}>
      <QuqFileManager apiKey="qk_your_project_key" handleHardwareBack={isFocused} />
    </SafeAreaView>
  )
}

To show it as a tab in the SDK 57 template, add a trigger for it in src/components/app-tabs.tsx, inside <NativeTabs>:

<NativeTabs.Trigger name="files">
  <NativeTabs.Trigger.Label>Files</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>

With React Navigation instead of Expo Router, render the same QuqFileManager in your screen and take useIsFocused from @react-navigation/native.

5. Run the app

  • Expo Go: npx expo start, then open the app in Expo Go. Needs Allow localhost (step 3). Expo Go asks for photo and camera access with its own texts.
  • Your own build: npx expo run:ios or npx expo run:android, or an EAS build. Needs the app's ID under Mobile apps (step 3). Rebuild after any change to the plugins in app.json.

Open the Files tab: you see the project's files and can upload, view, rename, share and move files to trash.

Pick files for your app

QuqPickerModal opens the file manager as a sheet and hands the chosen files to your code:

import { useState } from 'react'
import { Button } from 'react-native'
import { QuqPickerModal, type QuqFile } from '@letsoft/quq-react-native'

export function AttachButton({ onAttach }: { onAttach: (files: QuqFile[]) => void }) {
  const [open, setOpen] = useState(false)
  return (
    <>
      <Button title="Attach files" onPress={() => setOpen(true)} />
      <QuqPickerModal
        visible={open}
        apiKey="qk_your_project_key"
        multiple
        accept={['image/*', 'application/pdf']}
        onSelect={onAttach}
        onClose={() => setOpen(false)}
      />
    </>
  )
}
  • Each opening starts at initialPath with nothing selected. After onSelect, it calls onClose.
  • With multiple, files get checkboxes and a Select bar. Without it, a tap picks at once.
  • Files that don't match accept stay visible but dimmed and can't be picked; a tap on one does nothing, as on the web, and its ⋮ menu has no Share. Pass unpickablePress="preview" to open it instead: images, video and audio in the viewer, other files in Share.
  • On Android, back goes up a folder, then closes the sheet. On iOS, swipe the sheet down to close.

Each QuqFile has the file's name, path, absolute url, mime, size and modified.

QuqFileManager also takes mode="pick" if you'd rather place the picker in your own screen. In the default browse mode, passing onSelect adds Select to a file's ⋮ menu (a long press opens the same menu), so a files screen can hand a file to your code too.

Props

| Prop | Type | | |---|---|---| | apiKey | string | Project API key from the dashboard. Required. | | apiUrl | string | API endpoint. Defaults to the managed QuqManager API. | | initialPath | string | Folder to open first. Default /. | | mode | 'browse' \| 'pick' | Default 'browse'. 'pick' requires onSelect. | | multiple | boolean | Pick mode: choose several files and confirm with Select. | | accept | string[] | Pick mode: MIME types or extensions that can be picked, e.g. image/*, .pdf. | | unpickablePress | 'none' \| 'preview' | Pick mode: what a tap on a file outside accept does. Default 'none': nothing, and no Share in ⋮. 'preview': the viewer, or Share for files it can't show. | | onSelect | (files: QuqFile[]) => void | The chosen files, with absolute urls. In browse mode it adds Select to the ⋮ menu. | | onClose | () => void | Shows a close button in the header. | | handleHardwareBack | boolean | Android back goes up a folder. Default true. Pass false, or useIsFocused(), when the screen is not focused. | | style | StyleProp<ViewStyle> | Style of the root view. | | ref | QuqFileManagerHandle | ref.current.goBack() goes up a folder and returns false at the root. |

QuqPickerModal takes the same props except mode, handleHardwareBack, style and ref, plus visible, onSelect and onClose, which are required.

Good to know

  • Read-only projects hide upload, new folder, rename and trash; viewing and sharing remain.
  • Trash asks for confirmation. Files can be restored from Recovery in the web file manager.
  • HEIC photos from the iOS photo library are uploaded as JPEG, so browsers can show them. Files picked from the Files app are uploaded as they are.
  • Audio files follow the iOS silent switch; the package sets no audio mode itself (videos use expo-video's playback session).
  • Strings are English for now.

License

MIT © LetSoft. See LICENSE.