Beginning with @aws-amplify/ui-react-storage version 3.3.0, the StorageManager
component has been renamed to FileUploader
. To migrate, upgrade to the
latest version of @aws-amplify/ui-react-storage and replace StorageManager
related imports with their corresponding FileUploader
equivalents.
StorageManager
will continue to receive official support until the next major
version release of @aws-amplify/ui-react-storage, at which point StorageManager
will be replaced by FileUploader
.
Drop files here or
Basic Usage
Did you follow the quick start instructions to set up the storage and auth services?
Next.js 13.4+ introduces App Router with the usage of Server Components. Amplify UI components are interactive and designed to work on the client side. To use them inside of Server Components you must wrap them in a Client Component with "use client"
. For more info, visit Next.js third party package documentation.
If you are using Next.js Pages Router, no changes are required to use Amplify UI components.
To use the FileUploader component import it into your React application with the included styles.
npm install @aws-amplify/ui-react-storage aws-amplify
yarn add @aws-amplify/ui-react-storage aws-amplify
import { FileUploader } from '@aws-amplify/ui-react-storage';
import '@aws-amplify/ui-react/styles.css';
At a minimum you must include the path
and maxFileCount
props. path
refers to the S3 image path that will be prefixed to each file key. It is either a string
or a callback function that accepts the current user's Cognito identityId
and returns a string
. See upload files
Using @aws-amplify/ui-react-storage
version 3.0.18 or below and looking for the accessLevel
prop? See Deprecated Props
Drop files here or
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const App = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
path="public/"
maxFileCount={1}
isResumable
/>
);
};
Private or Protected Buckets
When uploading to private or protected S3 buckets, you'll need to wrap your app in the Authenticator
, allowing the FileUploader
component to infer the Cognito identityId
of the currently signed-in user. This can be done directly with the Authenticator
component or with withAuthenticator
, as shown in Add the Authenticator.
The example below shows configuring the FileUploader to upload to the protected
folder under the users' identity id.
<FileUploader
acceptedFileTypes={['image/*']}
path={({ identityId }) => `protected/${identityId}/`}
maxFileCount={1}
isResumable
/>
Deprecated props
Using @aws-amplify/ui-react-storage
version 3.0.18 or below?
@aws-amplify/ui-react-storage
version 3.0.18 or below?Versions 3.0.18 and earlier use accessLevel
and an optional path
prop in place of the required path
. accessLevel
refers to the Amplify Storage access level, which is 'guest' | 'protected' | 'private'
, and path
is a string
.
<StorageManager
accessLevel="guest"
path="images/"
maxFileCount={1}
/>
<StorageManager
accessLevel="private"
maxFileCount={1}
/>
To migrate to a newer version, replace accessLevel
with path
. If you were using path
already, append public
, private
or protected
to the beginning of the path.
<StorageManager
- accessLevel="guest"
+ path="public/"
maxFileCount={1}
/>
<StorageManager
- accessLevel="private"
- path="images/"
+ path={({ identityId }) => `private/${identityId}/images/`}
maxFileCount={1}
/>
The uploading capabilities in these examples are stubbed out so they don't actually upload files anywhere!
Props
Name | Description | Type |
---|---|---|
path | S3 bucket key prefixed to each upload target file `key`, allows either a `string` or a callback provided the value of the current user's `identityId`. |
|
path? | Optional when `accessLevel` is provided. S3 bucket key prefixed to each upload target file `key` |
|
accessLevel | Deprecated in favor of `path`. S3 access level of upload target files. See https://docs.amplify.aws/gen1/react/build-a-backend/storage/configure-access/ |
|
acceptedFileTypes? | List of accepted file types, values of `['*']` or undefined allow any files |
|
autoUpload? | Determines if the upload will automatically start after a file is selected. The default value is `true` |
|
maxFileCount |
| |
maxFileSize? |
| |
onUploadStart? | Called when a file starts uploading |
|
onUploadSuccess? | Called when a file successfully uploads |
|
onUploadError? | Called when a error happens uploading a file |
|
onFileRemove? | Called when a file is removed |
|
processFile? | Called immediately before uploading a file to allow you to edit the key or the file itself. The function can return synchronously or return a promise. |
|
defaultFiles? | An array of files that already exist in the cloud. |
|
displayText? | Text to override in the component. |
|
useAccelerateEndpoint? | Use the accelerated S3 endpoint to upload files. |
|
components?.Container? | The container the FileUploader is wrapped in. |
|
components?.DropZone? | The dropzone element which contains the FilePicker |
|
components?.FilePicker? | The button that opens the file picker menu. |
|
components?.FileList? | The list of files that is being uploaded. |
|
components?.FileListHeader? | The heading above the list of files |
|
components?.FileListFooter? | The footer below the list of files |
|
ref? | Forward ref prop exposing FileUploader imperative methods. |
|
Manually Upload
The default behavior of the File Uploader component is to automatically start the upload after a file is selected. If you wish to change that, set the value of the autoUpload
prop to false.
Drop files here or
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const App = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
path="public/"
autoUpload={false}
maxFileCount={1}
isResumable
/>
);
};
Setting Limits
You can limit what users upload with these 3 props:
maxFileSize
: sets a maximum file size the uploader will accept in bytes. The default is unlimited.maxFileCount
: accepts how many files at one time you can select to upload.acceptedFileTypes
: an array of file type strings that follow the HTMLaccept
attribute.
Drop files here or
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const App = () => {
return (
<StorageManager
acceptedFileTypes={[
// you can list file extensions:
'.gif',
'.bmp',
'.doc',
'.jpeg',
'.jpg',
// or MIME types:
'image/png',
'video/*',
]}
path="public/"
maxFileCount={5}
// Size is in bytes
maxFileSize={10000}
/>
);
};
Pausable / Resumable Uploads
A resumable upload will upload the file in chunks. This allows users to pause an upload and resume it at a later time. You will typically want to do this only when the expected files are larger than the chunk size, which is 5MB.
Drop files here or
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const App = () => {
return (
<StorageManager
acceptedFileTypes={['image/*', '.zip', '.mp4']}
path="public/"
maxFileCount={10}
isResumable
/>
);
};
Pre-upload Processing
You might want to process or modify the file(s) and/or file name(s) before they are uploaded. One common situation is you may want to ensure files uploaded are at unique keys by hashing the file contents and using that as the key rather than the filename.
You can pass a processFile
function to the FileUploader which accepts an object with file
: File, and key
: string, and should return an object with file, key, and any other Storage configurations. The processFile
can either return synchronously or return a Promise. This example uses a Promise to read the contents of the file and create a hash for the key.
Drop files here or
import { StorageManager } from '@aws-amplify/ui-react-storage';
const processFile = async ({ file }) => {
const fileExtension = file.name.split('.').pop();
return file
.arrayBuffer()
.then((filebuffer) => window.crypto.subtle.digest('SHA-1', filebuffer))
.then((hashBuffer) => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((a) => a.toString(16).padStart(2, '0'))
.join('');
return { file, key: `${hashHex}.${fileExtension}` };
});
};
export const App = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
path="public/"
maxFileCount={1}
processFile={processFile}
/>
);
};
Other uses-cases for processing the file before upload:
- Performing file optimizations like removing unnecessary metadata.
- Performing custom file validations like reading the contents of a file to ensure it is in the proper structure.
You can also add any other Amplify Storage options by adding them to the return object of processFile
Event Handling
The FileUploader component has several event handlers: onUploadStart
, onUploadSuccess
, onUploadError
, and onFileRemove
Drop files here or
import * as React from 'react';
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const App = () => {
const [files, setFiles] = React.useState({});
return (
<>
<StorageManager
acceptedFileTypes={['image/*']}
path={({ identityId }) => `private/${identityId}/`}
maxFileCount={3}
onFileRemove={({ key }) => {
setFiles((prevFiles) => {
return {
...prevFiles,
[key]: undefined,
};
});
}}
onUploadError={(error, { key }) => {
setFiles((prevFiles) => {
return {
...prevFiles,
[key]: {
status: 'error',
},
};
});
}}
onUploadSuccess={({ key }) => {
setFiles((prevFiles) => {
return {
...prevFiles,
[key]: {
status: 'success',
},
};
});
}}
onUploadStart={({ key }) => {
setFiles((prevFiles) => {
return {
...prevFiles,
[key]: {
status: 'uploading',
},
};
});
}}
/>
{Object.keys(files).map((key) => {
return files[key] ? (
<div>
{key}: {files[key].status}
</div>
) : null;
})}
</>
);
};
Be careful setting state in the onUploadSuccess
because that function is bound when the upload starts. Make sure to use the previous state argument rather than the current state in the component.
path
Usage
The path
prop of the FileUploader
is prepended to the key
value (resolved from either the file itself or the returned key
of processFile
) submitted to S3. Using a '/'
as the last character of path
allows uploading to a specific folder inside the provided accessLevel
folder.
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const App = () => {
return (
<StorageManager
path="public/images/"
acceptedFileTypes={['image/*']}
maxFileCount={1}
/>
);
};
Adding metadata
Metadata is added as an object with string key-value pairs. It is sent as custom HTTP headers with the name x-amz-meta-[key]
. For example, if your metadata for a file was {mode: 'night'}
, it would set the x-amz-meta-mode
HTTP header to night
.
You can add metadata by adding a metadata
object in the return object of processFile
.
Drop files here or
import { StorageManager } from '@aws-amplify/ui-react-storage';
const processFile = ({ file, key }) => {
return {
file,
key,
metadata: {
id: key,
},
};
};
export function App() {
return (
<StorageManager
acceptedFileTypes={['image/*']}
path="public/"
maxFileCount={3}
showThumbnails={true}
processFile={processFile}
/>
);
}
Accelerate Endpoint
Amazon S3 transfer acceleration optimizes transfer speeds from around the world into S3 buckets. When you use Transfer Acceleration, additional data transfer charges might apply. For more information about pricing, see Amazon S3 pricing.
To use transfer acceleration you first need to enable it on your S3 bucket. Then add useAccelerateEndpoint
on the <FileUploader />
component. By default transfer acceleration is off.
<FileUploader
acceptedFileTypes={['image/*']}
maxFileCount={10}
useAccelerateEndpoint
/>
You can also choose whether or not to use transfer acceleration at the file level by returning useAccelerateEndpoint
from the processFile
function.
<FileUploader
acceptedFileTypes={['image/*']}
maxFileCount={10}
processFile={({ file, key }) => {
return {
file,
key,
useAccelerateEndpoint: file.size > 10000 ? true : false,
};
}}
/>
Customization
Text and labels
All text in the FileUploader component is customizable with the displayText
prop.
drag-and-drop here
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const App = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
path="public/"
maxFileCount={1}
displayText={{
// some text are plain strings
dropFilesText: 'drag-and-drop here',
browseFilesText: 'Open file picker',
// others are functions that take an argument
getFilesUploadedText(count) {
return `${count} images uploaded`;
},
}}
/>
);
};
Display text props
Name | Description | Type |
---|---|---|
getFilesUploadedText? |
| |
getFileSizeErrorText? |
| |
getRemainingFilesText? |
| |
getUploadingText? |
| |
getUploadButtonText? |
| |
getMaxFilesErrorText? |
| |
getErrorText? |
| |
getPausedText? |
| |
doneButtonText? | Default: "Done" |
|
clearAllButtonText? | Default: "Clear all" |
|
extensionNotAllowedText? | Default: "Extension not allowed" |
|
browseFilesText? | Default: "Browse files" |
|
dropFilesText? | Default: "Drop files here or" |
|
pauseButtonText? | Default: "Pause" |
|
resumeButtonText? | Default: "Resume" |
|
uploadSuccessfulText? | Default: "Uploaded successfully" |
|
Internationalization
You can use the displayText
prop to also support different languages. Use an open source library like i18next, react-intl, or make your own:
Drop files here or
import * as React from 'react';
import { ToggleButtonGroup, ToggleButton } from '@aws-amplify/ui-react';
import { StorageManager } from '@aws-amplify/ui-react-storage';
const dictionary = {
// use default strings for english
en: null,
es: {
getFilesUploadedText(count) {
return `${count} ${
count === 1 ? 'archivo cargado' : 'archivos cargados'
}`;
},
getFileSizeErrorText(sizeText) {
return `El tamaño del archivo debe ser menor a ${sizeText}`;
},
getRemainingFilesText(count) {
return `${count} ${count === 1 ? 'archivo' : 'archivos'} subiendo`;
},
getUploadingText(percentage) {
return `Subiendo${percentage > 0 ? `: ${percentage}%` : ''}`;
},
getUploadButtonText(count) {
return `Cargar ${count} ${count === 1 ? 'archivo' : 'archivos'}`;
},
getMaxFilesErrorText(count) {
return `No se pueden seleccionar más de ${count} ${
count === 1 ? 'archivo' : 'archivos'
}. Elimine archivos antes de actualizar.`;
},
getErrorText(message) {
return message;
},
doneButtonText: 'Listo',
clearAllButtonText: 'Limpiar todo',
extensionNotAllowedText: 'Extensión no permitida',
browseFilesText: 'Buscar archivos',
dropFilesText: 'Arrastre los archivos aquà o',
pauseButtonText: 'Pausa',
resumeButtonText: 'Reanudar',
uploadSuccessfulText: 'Carga exitosa',
getPausedText(percentage) {
return `Pausado: ${percentage}%`;
},
},
};
export const App = () => {
const [language, setLanguage] = React.useState('en');
return (
<>
<ToggleButtonGroup
value={language}
isExclusive
onChange={(value) => setLanguage(value)}
>
<ToggleButton value="en">En</ToggleButton>
<ToggleButton value="es">Es</ToggleButton>
</ToggleButtonGroup>
<StorageManager
acceptedFileTypes={['image/*']}
path="public/"
maxFileCount={1}
displayText={dictionary[language]}
/>
</>
);
};
Component overrides
Don't like how things look? Use your own components inside the FileUploader! You can pass your own components with the components
prop. The available components to override are: Container
, FileList
, FileListHeader
, FileListFooter
, DropZone
, and FilePicker
.
You can even use a completely different UI kit like MUI, Chakra, or your own design system!
Drop files here
import * as React from 'react';
import {
Card,
Button,
Flex,
Text,
Divider,
Image,
Loader,
Icon,
} from '@aws-amplify/ui-react';
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const App = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
path="public/"
maxFileCount={100}
components={{
Container({ children }) {
return <Card variation="elevated">{children}</Card>;
},
DropZone({ children, displayText, inDropZone, ...rest }) {
return (
<Flex
alignItems="center"
direction="column"
padding="medium"
backgroundColor={inDropZone ? 'primary.10' : ''}
{...rest}
>
<Text>Drop files here</Text>
<Divider size="small" label="or" maxWidth="10rem" />
{children}
</Flex>
);
},
FilePicker({ onClick }) {
return (
<Button variation="primary" onClick={onClick}>
Browse Files
</Button>
);
},
FileList({ files, onCancelUpload, onDeleteUpload }) {
return (
<Flex direction="row">
{files.map(({ file, key, progress, id, status, uploadTask }) => (
<Flex
key={key}
justifyContent="center"
alignItems="center"
width="5rem"
height="5rem"
position="relative"
>
<Image
borderRadius="small"
height="100%"
objectFit="cover"
src={URL.createObjectURL(file)}
alt={key}
/>
{progress < 100 ? (
<Loader
position="absolute"
size="large"
percentage={progress}
isDeterminate
isPercentageTextHidden
/>
) : null}
<Button
opacity="50"
borderRadius="xxl"
backgroundColor="background.primary"
position="absolute"
variation="link"
size="small"
onClick={() => {
if (status === 'uploading') {
onCancelUpload({ id, uploadTask });
} else {
onDeleteUpload({ id });
}
}}
>
<Icon
fontSize="large"
color="font.error"
viewBox={{ width: 512, height: 512 }}
paths={[
{
d: 'M448 256c0-106-86-192-192-192S64 150 64 256s86 192 192 192 192-86 192-192z',
strokeWidth: '32',
fill: 'none',
strokeMiterlimit: '10',
stroke: 'currentColor',
},
{
d: 'M320 320L192 192m0 128l128-128',
strokeWidth: '32',
fill: 'none',
strokeLinecap: 'round',
stroke: 'currentColor',
},
]}
/>
</Button>
</Flex>
))}
</Flex>
);
},
}}
/>
);
};
FilePicker props
Name | Description | Type |
---|---|---|
children |
| |
onClick |
|
DropZone props
Name | Description | Type |
---|---|---|
acceptedFileTypes | List of accepted file types |
|
children |
| |
displayText | Test strings that are used in the component |
|
isLoading? |
| |
onDragStart |
| |
onDragEnter |
| |
onDragLeave |
| |
onDragOver |
| |
onDrop |
| |
inDropZone |
|
Imperative handles
The files
state is managed within the FileUploader
component itself. To allow for clearing the internal files
state, FileUploader
exposes a custom ref handle to the parent component with a clearFiles
method.
Drop files here or
import * as React from 'react';
import { Button } from '@aws-amplify/ui-react';
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const App = () => {
const ref = React.useRef(null);
return (
<>
<StorageManager
acceptedFileTypes={['image/*']}
path="public/"
maxFileCount={3}
ref={ref}
/>
<Button onClick={() => ref.current.clearFiles()}>Clear Files</Button>
</>
);
};
Theming
Drop files here or
import { ThemeProvider } from '@aws-amplify/ui-react';
import { StorageManager } from '@aws-amplify/ui-react-storage';
const theme = {
name: 'my-theme',
tokens: {
borderWidths: {
small: '2px',
},
components: {
storagemanager: {
dropzone: {
borderColor: '{colors.primary.60}',
},
},
},
},
};
export const App = () => {
return (
<ThemeProvider theme={theme}>
<StorageManager
acceptedFileTypes={['image/*']}
path="public/"
maxFileCount={5}
/>
</ThemeProvider>
);
};
Target Classes
If you like, you can target classes directly or use CSS variables to make changes to the look and feel of the File Uploader.
Class | Description |
---|---|
amplify-fileuploader | |
amplify-fileuploader__dropzone | |
amplify-fileuploader__dropzone__icon | |
amplify-fileuploader__dropzone__text | |
amplify-fileuploader__file__picker | |
amplify-fileuploader__file | |
amplify-fileuploader__file__wrapper | |
amplify-fileuploader__file__list | |
amplify-fileuploader__file__name | |
amplify-fileuploader__loader | |
amplify-fileuploader__file__size | |
amplify-fileuploader__file__info | |
amplify-fileuploader__file__image | |
amplify-fileuploader__file__main | |
amplify-fileuploader__file__status | |
amplify-fileuploader__previewer | |
amplify-fileuploader__previewer__text | |
amplify-fileuploader__previewer__actions | |
amplify-fileuploader__previewer__footer |