React
React components for the Uppy UI plugins and hooks.
Install
- NPM
- Yarn
npm install @uppy/react
yarn add @uppy/react
You may also need to install peer dependencies, for instance @uppy/dashboard
.
Use
Uppy offers three ways to build user interfaces:
- Pre-composed, plug-and-play components. Mainly
<Dashboard />
and<DragDrop />
. The downside is that you can’t customize the UI. - Headless components. Smaller componentes, easier to override the styles or compose them together with your own components.
- Hooks. Attach our logic to your own components, no restrictions, create a tailor-made UI.
Components
Pre-composed, plug-and-play components:
<Dashboard />
renders@uppy/dashboard
<DashboardModal />
renders@uppy/dashboard
as a modal<DragDrop />
renders@uppy/drag-drop
<ProgressBar />
renders@uppy/progress-bar
<StatusBar />
renders@uppy/status-bar
Headless Components
Play around with the React headless components in StackBlitz
All components must be placed within a UppyContextProvider
.
Uppy’s headless components are smaller and more flexible. All components must be
placed within a UppyContextProvider
. They come styled as well but you can
override the styles with data attributes, for example with UploadButton
:
button[data-uppy-element='upload-button'][data-state='uploading'] {
background-color: lightblue;
}
UppyContextProvider
Provides Uppy context to child components and manages global upload status/progress.
import { useContext, useState } from 'react';
import { UppyContextProvider, UppyContext } from '@uppy/react';
function YourComponent() {
const { uppy } = useContext(UppyContext);
}
export function App() {
const [uppy] = useState(() => new Uppy());
return (
<UppyContextProvider uppy={uppy}>
<YourComponent />
{/* and/or Uppy components */}
</UppyContextProvider>
);
}
Context Value:
{
uppy: Uppy | undefined;
status: 'init' | 'ready' | 'uploading' | 'paused' | 'error' | 'complete';
progress: number;
}
<UploadButton />
Attributes
data-uppy-element="upload-button"
data-state="init" | "uploading" | "paused" | "error" | "complete"
<Thumbnail />
Component for displaying image thumbnails and/or SVG icons.
Props:
file: UppyFile<Meta, Body>
width?: string
height?: string
images?: boolean
<ProviderIcon />
Common icons, such as a webcam, device, Dropbox, Google Drive, OneDrive, etc.
Props:
provider
:'device' | 'camera' | 'screen-capture' | 'audio' | 'dropbox' | 'facebook' | 'instagram' | 'onedrive' | 'googlephotos' | 'googledrive'
fill?: string
Device
Camera
Screen Capture
Audio
Dropbox
OneDrive
Google Photos
Google Drive
<FilesList />
Component for displaying a list view of selected files.
Props:
editFile?: (file: UppyFile<Meta, Body>) => void
<FilesGrid />
Grid layout component for displaying files in a grid format.
Props:
editFile?: (file: UppyFile<Meta, Body>) => void
columns?: number
<Dropzone />
Drag-and-drop zone component for file uploads.
Props:
width?: string
height?: string
note?: string
noClick?: boolean
Hooks
Play around with the React headless hooks in StackBlitz
All hooks, except useUppyState
and useUppyEvent
, must be used within a
UppyContextProvider
.
useUppyState(uppy, selector)
Use this hook when you need to access Uppy’s state reactively.
// IMPORTANT: passing an initializer function to prevent Uppy from being reinstantiated on every render.
const [uppy] = useState(() => new Uppy());
const files = useUppyState(uppy, (state) => state.files);
const totalProgress = useUppyState(uppy, (state) => state.totalProgress);
// We can also get specific plugin state.
// Note that the value on `plugins` depends on the `id` of the plugin.
const metaFields = useUppyState(
uppy,
(state) => state.plugins?.Dashboard?.metaFields,
);
You can see all the values you can access on the
State
type. If you are accessing plugin state, you would have to look at the types of
the plugin.
useUppyEvent(uppy, event, callback)
Listen to Uppy events in a React component.
The first item in the array is an array of results from the event. Depending on the event, that can be empty or have up to three values. The second item is a function to clear the results. Values remain in state until the next event (if that ever comes). Depending on your use case, you may want to keep the values in state or clear the state after something else happenend.
// IMPORTANT: passing an initializer function to prevent Uppy from being reinstantiated on every render.
const [uppy] = useState(() => new Uppy());
const [results, clearResults] = useUppyEvent(uppy, 'transloadit:result');
const [stepName, result, assembly] = results; // strongly typed
useUppyEvent(uppy, 'cancel-all', clearResults);
useDropzone(options?)
Creates a dropzone for drag-and-drop file uploads using Uppy.
Arguments:
noClick?: boolean
onDragOver?: (event: Event) => void
onDragEnter?: (event: Event) => void
onDragLeave?: (event: Event) => void
onDrop?: (files: File[]) => void
onFileInputChange?: (files: File[]) => void
Returns:
getRootProps: Function
getInputProps: Function
useFileInput(props?)
Creates a file input for programmatic file selection using Uppy.
Arguments:
multiple?: boolean
accept?: string
Returns:
getInputProps: Function
getButtonProps: Function
useRemoteSource(sourceId)
Manages remote source connections (cloud storage providers).
Arguments:
sourceId: 'Box' | 'Dropbox' | 'Facebook' | 'GoogleDrive' | 'Instagram' | 'OneDrive' | 'Unsplash' | 'Url' | 'Zoom'
Returns:
{
state: {
authenticated: boolean | undefined
didFirstRender: boolean
searchString: string
loading: boolean | string
partialTree: PartialTree
currentFolderId: PartialTreeId
username: string | null
breadcrumbs: PartialTreeFolder[]
selectedAmount: number
error: string | null
}
login: Function
logout: Function
open: Function
checkbox: Function
done: Function
cancel: Function
}
useWebcam({ onSubmit })
Manages webcam functionality for capturing photos/videos.
Arguments:
onSubmit: () => void
Returns:
{
state: {
hasCamera: boolean
cameraReady: boolean
cameraError: null | Error
recordingLengthSeconds: number
videoSources: MediaDeviceInfo[]
currentDeviceId: string | MediaStreamTrack | null | undefined
recordedVideo: null | string
isRecording: boolean
status: 'init' | 'ready' | 'recording' | 'captured' | 'error'
}
stop: Function
start: Function
getVideoProps: Function
getSnapshotButtonProps: Function
getRecordButtonProps: Function
getStopRecordingButtonProps: Function
getSubmitButtonProps: Function
getDiscardButtonProps: Function
}
Examples
Example: basic component
Here we have a basic component which ties Uppy’s state to the component. This means you can render multiple instances. But be aware that as your component unmounts, for instance because the user navigates to a different page, Uppy’s state will be lost and uploads will stop.
If you render multiple instances of Uppy, make sure to give each instance a
unique id
.
import React, { useEffect, useState } from 'react';
import Uppy from '@uppy/core';
import Webcam from '@uppy/webcam';
import { Dashboard } from '@uppy/react';
import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';
import '@uppy/webcam/dist/style.min.css';
function Component() {
// IMPORTANT: passing an initializer function to prevent Uppy from being reinstantiated on every render.
const [uppy] = useState(() => new Uppy().use(Webcam));
return <Dashboard uppy={uppy} />;
}
Example: keep Uppy state and uploads while navigating between pages
When you want Uppy’s state to persist and keep uploads running between pages, you can lift the state up.
import React, { useState, useEffect } from 'react';
import Uppy from '@uppy/core';
import { Dashboard } from '@uppy/react';
function Page1() {
// ...
}
function Page2({ uppy }) {
return (
<>
<p>{totalProgress}</p>
<Dashboard id="dashboard" uppy={uppy} />
</>
);
}
export default function App() {
// keeping the uppy instance alive above the pages the user can switch during uploading
const [uppy] = useState(() => new Uppy());
return (
// Add your router here
<>
<Page1 />
<Page2 uppy={uppy} />
</>
);
}
Example: updating Uppy’s options dynamically based on props
// ...
function Component(props) {
// IMPORTANT: passing an initializer function to prevent the state from recreating.
const [uppy] = useState(() => new Uppy().use(Webcam));
useEffect(() => {
uppy.setOptions({ restrictions: props.restrictions });
}, [props.restrictions]);
useEffect(() => {
uppy.getPlugin('Webcam').setOptions({ modes: props.webcamModes });
}, [props.webcamModes]);
return <Dashboard uppy={uppy} />;
}
Example: dynamic params and signature for Transloadit
When you go to production always make sure to set the signature
. Not using
Signature Authentication
can be a security risk. Signature Authentication is a security measure that
can prevent outsiders from tampering with your Assembly Instructions.
Generating a signature should be done on the server to avoid leaking secrets. In
React, this could get awkward with a fetch
in a useEffect
and setting it to
useState
. Instead, it’s easier to use the
assemblyOptions
option to fetch
the
params.
// ...
function createUppy() {
const uppy = new Uppy();
uppy.use(Transloadit, {
async assemblyOptions() {
// You can send meta data along for use in your template.
// https://transloadit.com/docs/topics/assembly-instructions/#form-fields-in-instructions
const { meta } = uppy.getState();
const body = JSON.stringify({ userId: meta.userId });
const res = await fetch('/transloadit-params', { method: 'POST', body });
return response.json();
},
});
return uppy;
}
function Component({ userId }) {
// IMPORTANT: passing an initializer function to prevent Uppy from being reinstantiated on every render.
const [uppy] = useState(createUppy);
useEffect(() => {
if (userId) {
// Adding to global `meta` will add it to every file.
uppy.setOptions({ meta: { userId } });
}
}, [uppy, userId]);
}