One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Document Picker

Pick arbitrary files through the system document picker

One.DocumentPicker presents the system file picker and returns the chosen files copied into the app cache. Reach for it for arbitrary documents, PDFs, archives, or anything outside the photo library; for camera and library images, the image picker is the better fit.

On iOS it presents UIDocumentPickerViewController opened as a copy. On Android it fires ACTION_OPEN_DOCUMENT and copies each result to cache. On web it opens a file dialog and returns blob URLs. No platform needs a permission for picking.

Usage

import { One } from 'one'
const picked = await One.DocumentPicker.getDocument({
type: ['application/pdf', 'image/*'],
multiple: false,
})
if (!picked.canceled) {
const [first] = picked.assets
console.log(first.uri, first.name, first.mimeType, first.size)
}

type is one mime type or a list, exact or wildcard, defaulting to */*. Backing out resolves { canceled: true, assets: null } instead of throwing.

How it works

Options resolve before any picker opens, identically on every platform: the type list lowercases and dedupes, multiple defaults to false, and anything that is not a mime-shaped string or a boolean rejects at once. On iOS each mime type maps to a UTType for the picker filter; on Android the list becomes the intent filter; on web it becomes the file input’s accept string, with */* mapping to an empty accept that offers every file.

Picked files always land in the app cache as new copies, so the returned file:// URIs stay readable after the picker closes and need no persisted security scope. Each asset carries the cached URI, the display name the provider reported, and the mime type and byte size when the platform provides them. Read a picked file with fetch(uri): arrayBuffer, blob, and text all work on both native platforms, so no file system call is needed to read one. On web the URIs are blob URLs for the dialog’s files.

One pick runs at a time; a second call while the picker is up rejects. Runtime failures reject with E_DOCUMENT_PICKER_FAILED.

API reference

MethodsTypeDescription
getDocument(options: DocumentPickerOptions) => Promise<DocumentPickerResult>Pick files. Options default to every file, one pick.
(options: DocumentPickerOptions) => Promise<DocumentPickerResult>Pick files. Options default to every file, one pick.
OptionsTypeDescription
typestring | string[]Mime types to offer, exact or wildcard. Default */*.
string | string[]Mime types to offer, exact or wildcard. Default */*.
multiplebooleanAllow more than one file. Default false.
booleanAllow more than one file. Default false.
Picked fileTypeDescription
uristringThe cached copy, as a file URI.
stringThe cached copy, as a file URI.
namestringThe display name the provider reported.
stringThe display name the provider reported.
mimeTypestringThe mime type, when the platform provides it.
stringThe mime type, when the platform provides it.
sizenumberSize in bytes of the cached file.
numberSize in bytes of the cached file.
Picked resultTypeDescription
canceledfalseAlways false on a pick.
falseAlways false on a pick.
assetsDocumentPickerAsset[]The picked files.
DocumentPickerAsset[]The picked files.
Canceled resultTypeDescription
canceledtrueAlways true on a cancel.
trueAlways true on a cancel.
assetsnullAlways null on a cancel.
nullAlways null on a cancel.

Platform differences

iOSAndroidWeb
PickerSystem document UISystem document UIFile dialog
ResultsCache file URIsCache file URIsBlob URLs
MetadataName, type, sizeName, type, sizeName, type, size
PermissionNone neededNone neededNone needed

Limits and gotchas

Examples

Attach a PDF to a form

import { useState } from 'react'
import { Button, Text } from 'react-native'
import { One } from 'one'
function PdfAttachment({ onAttach }: { onAttach: (uri: string) => void }) {
const [name, setName] = useState('')
async function attach() {
const picked = await One.DocumentPicker.getDocument({ type: 'application/pdf' })
if (picked.canceled) return
const [first] = picked.assets
if (!first) return
setName(first.name)
onAttach(first.uri)
}
return (
<>
<Button title="Attach a PDF" onPress={attach} />
{name ? <Text>Attached {name}</Text> : null}
</>
)
}

The mime filter shows only PDFs, and single-pick mode keeps the result shape simple. The cancel path returns quietly, leaving any previous attachment in place.

Read a picked text file

import { One } from 'one'
async function readTextFile(): Promise<string | undefined> {
const picked = await One.DocumentPicker.getDocument({ type: 'text/*' })
if (picked.canceled) return undefined
const [first] = picked.assets
if (!first) return undefined
return (await fetch(first.uri)).text()
}

The cached URI is fetchable directly, so small files need no file system API at all. Larger files should stream from the URI instead of loading the whole text at once.

Edit this page on GitHub.