NativeUI Primitives

InputFile

Cross-platform file input component for selecting images and files. Uses expo-image-picker on native platforms and native file input on web.


Installation

npm install @native-ui-org/primitives
pnpm add @native-ui-org/primitives
yarn add @native-ui-org/primitives
bun add @native-ui-org/primitives

Overview

InputFile provides a consistent API for file selection across iOS, Android, and Web. It handles platform-specific implementations automatically, so you can use the same component everywhere.

FeatureDescriptionPlatforms
Image SelectionSelect images from gallery or cameraiOS, Android, Web
File SelectionSelect any file type (web only)Web
PreviewAutomatic image preview after selectioniOS, Android, Web
PermissionsAutomatic permission handling on nativeiOS, Android
CustomizableControl media types, quality, editingiOS, Android, Web

Setup & Usage Guide

1. Install and Import

Install from npm:

npm install @native-ui-org/primitives

For Native Platforms (iOS/Android):

You also need to install expo-image-picker:

npx expo install expo-image-picker

Then import from the package:

import { InputFile } from "@native-ui-org/primitives";

2. Basic Usage

import { InputFile } from "@native-ui-org/primitives";
import { useState } from "react";

function MyForm() {
  const [file, setFile] = useState<string | File | null>(null);
  
  return (
    <InputFile 
      placeholder="Select image"
      onFileSelect={(selectedFile) => {
        setFile(selectedFile);
        console.log("Selected file:", selectedFile);
      }}
    />
  );
}

3. Controlled Component

Use the value prop to control the selected file:

import { InputFile } from "@native-ui-org/primitives";

function MyForm() {
  const [fileUri, setFileUri] = useState<string | null>(null);
  
  return (
    <InputFile 
      value={fileUri}
      onFileSelect={setFileUri}
      placeholder="Choose image"
    />
  );
}

4. Web-Specific Props

On web, you can control file types and multiple selection:

<InputFile 
  accept="image/*"           // Accept only images
  multiple={false}            // Single file selection
  onFileSelect={(file) => {
    if (file instanceof File) {
      console.log("File name:", file.name);
      console.log("File size:", file.size);
      console.log("File type:", file.type);
    }
  }}
/>
<InputFile 
  accept=".pdf,.doc,.docx"   // Accept specific file types
  multiple={true}             // Multiple file selection
  onFileSelect={(files) => {
    // Handle multiple files
  }}
/>

5. Native-Specific Props

On native platforms, you can control media types, editing, and quality:

<InputFile 
  mediaTypes="images"         // "images" | "videos" | "all"
  allowsEditing={true}         // Allow cropping/editing
  aspect={[16, 9]}            // Aspect ratio for editing
  quality={0.8}                // Image quality (0-1)
  onFileSelect={(uri) => {
    console.log("Image URI:", uri);
  }}
/>

6. Using with Forms

InputFile works well with form libraries:

import { useForm, Controller } from "react-hook-form";
import { InputFile } from "@native-ui-org/primitives";

function MyForm() {
  const { control } = useForm();
  
  return (
    <Controller
      control={control}
      name="avatar"
      render={({ field: { onChange, value } }) => (
        <InputFile
          value={value}
          onFileSelect={onChange}
          placeholder="Upload avatar"
        />
      )}
    />
  );
}

7. Custom Styling with asChild

Use asChild to customize the button appearance:

import { InputFile, Button, Text } from "@native-ui-org/primitives";

<InputFile 
  asChild
  onFileSelect={(file) => console.log(file)}
>
  <Button style={{ backgroundColor: "#007AFF" }}>
    <Text style={{ color: "white" }}>Custom Upload Button</Text>
  </Button>
</InputFile>

Mobile Setup (iOS/Android)

Dependencies

InputFile requires expo-image-picker on native platforms. Install it:

npx expo install expo-image-picker

iOS Configuration

Add the following to your app.json or app.config.js:

{
  "expo": {
    "plugins": [
      [
        "expo-image-picker",
        {
          "photosPermission": "This app needs access to your photo library to let you select and upload images.",
          "cameraPermission": "This app needs access to your camera to take photos."
        }
      ]
    ],
    "ios": {
      "infoPlist": {
        "NSPhotoLibraryUsageDescription": "This app needs access to your photo library to let you select and upload images.",
        "NSPhotoLibraryAddUsageDescription": "This app needs access to save photos to your photo library."
      }
    }
  }
}

After adding the configuration, rebuild your app:

npx expo prebuild --clean

Android Configuration

Android permissions are handled automatically by expo-image-picker. No additional configuration is needed.

Permissions

InputFile automatically requests media library permissions when needed. The component handles permission states internally, but you can check the permission status:

import * as ImagePicker from "expo-image-picker";

// Check permission status
const { status } = await ImagePicker.getMediaLibraryPermissionsAsync();
if (status !== "granted") {
  // Permission not granted
}

API Reference

InputFile

The file input component for all platforms.

PropTypeDefaultDescription
onFileSelect(file: string | File | null) => voidCallback when file is selected. On native, receives URI string. On web, receives File object.
valuestring | nullControlled value (file URI)
placeholderstring"Select file"Placeholder text for the button
asChildbooleanfalseRender child element without InputFile wrapper
acceptstring"image/*"Accepted file types (web only)
multiplebooleanfalseAllow multiple file selection (web only)
mediaTypes"images" | "videos" | "all""images"Media types for picker (native only)
allowsEditingbooleantrueAllow editing after selection (native only)
aspect[number, number][4, 3]Aspect ratio for editing (native only)
qualitynumber1Image quality 0-1 (native only)
...propsViewPropsAll React Native View props

Platform Behavior

PlatformImplementationCharacteristics
iOS / AndroidUses expo-image-pickerNative image picker, permission handling
WebUses native <input type="file">Browser file picker, File API
All PlatformsConsistent APISame props, platform-specific behavior

File Types

On Web

The accept prop controls which file types can be selected:

// Images only
<InputFile accept="image/*" />

// Specific image formats
<InputFile accept="image/png,image/jpeg" />

// Documents
<InputFile accept=".pdf,.doc,.docx" />

// All files
<InputFile accept="*/*" />

On Native

The mediaTypes prop controls which media types are available:

// Images only (default)
<InputFile mediaTypes="images" />

// Videos only
<InputFile mediaTypes="videos" />

// Both images and videos
<InputFile mediaTypes="all" />

Examples

Basic Image Upload

import { InputFile } from "@native-ui-org/primitives";
import { useState } from "react";

function AvatarUpload() {
  const [avatar, setAvatar] = useState<string | File | null>(null);
  
  return (
    <InputFile
      placeholder="Upload avatar"
      onFileSelect={setAvatar}
      mediaTypes="images"
      allowsEditing={true}
      aspect={[1, 1]}  // Square
      quality={0.8}
    />
  );
}

Multiple File Selection (Web)

import { InputFile } from "@native-ui-org/primitives";

function DocumentUpload() {
  const handleFiles = (files: File | string | null) => {
    if (files instanceof File) {
      console.log("Selected file:", files.name);
    }
  };
  
  return (
    <InputFile
      accept=".pdf,.doc,.docx"
      multiple={true}
      onFileSelect={handleFiles}
      placeholder="Select documents"
    />
  );
}

Custom Button

import { InputFile, Button, Text } from "@native-ui-org/primitives";

function CustomUpload() {
  return (
    <InputFile asChild onFileSelect={(file) => console.log(file)}>
      <Button style={{ 
        backgroundColor: "#007AFF",
        paddingHorizontal: 24,
        paddingVertical: 12,
        borderRadius: 8,
      }}>
        <Text style={{ color: "white", fontWeight: "600" }}>
          Choose File
        </Text>
      </Button>
    </InputFile>
  );
}

Accessibility

Web:

  • Proper button semantics
  • Keyboard accessible
  • Screen reader friendly

Mobile:

  • Standard React Native accessibility props
  • Works with VoiceOver and TalkBack

Troubleshooting

Image not showing on web

Make sure you're handling the File object correctly. On web, onFileSelect receives a File object, not a URI string.

Permission denied on iOS

Make sure you've added the required permissions to your app.json and rebuilt the app with npx expo prebuild --clean.

expo-image-picker not installed warning

Install expo-image-picker:

npx expo install expo-image-picker

Then rebuild your app.


Version History

VersionNotes
0.9.0Initial release — cross-platform file input with expo-image-picker integration.

Summary: InputFile provides a simple, consistent API for file selection across all platforms. On native, it uses expo-image-picker for a native experience. On web, it uses the browser's file picker.