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 StorageManager 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 { StorageManager } from '@aws-amplify/ui-react-storage';
import '@aws-amplify/ui-react/styles.css';
At a minimum you must include the accessLevel
and maxFileCount
props. accessLevel
refers to the Amplify Storage access level, which is 'guest' | 'protected' | 'private'
.
Drop files here or
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const DefaultStorageManagerExample = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
accessLevel="guest"
maxFileCount={1}
isResumable
/>
);
};
The uploading capabilities in these examples are stubbed out so they don't actually upload files anywhere!
Props
Name | Description | Type |
---|---|---|
accessLevel | Access level for files in Storage. See https://docs.amplify.aws/lib/storage/configureaccess/q/platform/js/ |
|
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. |
|
path? | Path in s3 to put the file under |
|
defaultFiles? | An array of files that already exist in the cloud. |
|
displayText? | Text to override in the component. |
|
components?.Container? | The container the StorageManager 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 StorageManager imperative methods. |
|
Manually Upload
The default behavior of the Storage Manager 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 StorageManagerUploadActionsExample = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
accessLevel="guest"
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 StorageManagerFileTypesExample = () => {
return (
<StorageManager
acceptedFileTypes={[
// you can list file extensions:
'.gif',
'.bmp',
'.doc',
'.jpeg',
'.jpg',
// or MIME types:
'image/png',
'video/*',
]}
accessLevel="guest"
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 StorageManagerResumableExample = () => {
return (
<StorageManager
acceptedFileTypes={['image/*', '.zip', '.mp4']}
accessLevel="guest"
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 StorageManager 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 StorageManagerHashExample = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
accessLevel="guest"
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 StorageManager 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 StorageManagerEventExample = () => {
const [files, setFiles] = React.useState({});
return (
<>
<StorageManager
acceptedFileTypes={['image/*']}
accessLevel="private"
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 StorageManager
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 StorageManagerPathPropExample = () => {
return (
<StorageManager
accessLevel="guest"
// uploaded files can be found in S3 inside `public/images/`
path="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 StorageManagerMetadataExample() {
return (
<StorageManager
acceptedFileTypes={['image/*']}
accessLevel="guest"
maxFileCount={3}
showThumbnails={true}
processFile={processFile}
/>
);
}
Customization
Text and labels
All text in the StorageManager component is customizable with the displayText
prop.
drag-and-drop here
import { StorageManager } from '@aws-amplify/ui-react-storage';
export const StorageManagerDisplayTextExample = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
accessLevel="guest"
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 StorageManageri18nExample = () => {
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/*']}
accessLevel="guest"
maxFileCount={1}
displayText={dictionary[language]}
/>
</>
);
};
Component overrides
Don't like how things look? Use your own components inside the StorageManager! 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 StorageManagerComponentOverridesExample = () => {
return (
<StorageManager
acceptedFileTypes={['image/*']}
accessLevel="guest"
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 StorageManager
component itself. To allow for clearing the internal files
state, StorageManager
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 StorageManagerHandleExample = () => {
const ref = React.useRef(null);
return (
<>
<StorageManager
acceptedFileTypes={['image/*']}
accessLevel="guest"
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 StorageManagerThemeExample = () => {
return (
<ThemeProvider theme={theme}>
<StorageManager
acceptedFileTypes={['image/*']}
accessLevel="guest"
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 Storage Manager.
Class | Description |
---|---|
amplify-storagemanager | |
amplify-storagemanager__dropzone | |
amplify-storagemanager__dropzone__icon | |
amplify-storagemanager__dropzone__text | |
amplify-storagemanager__file__picker | |
amplify-storagemanager__file | |
amplify-storagemanager__file__wrapper | |
amplify-storagemanager__file__list | |
amplify-storagemanager__file__name | |
amplify-storagemanager__loader | |
amplify-storagemanager__file__size | |
amplify-storagemanager__file__info | |
amplify-storagemanager__file__image | |
amplify-storagemanager__file__main | |
amplify-storagemanager__file__status | |
amplify-storagemanager__previewer | |
amplify-storagemanager__previewer__text | |
amplify-storagemanager__previewer__actions | |
amplify-storagemanager__previewer__footer |
--amplify-components-storagemanager-dropzone-active-background-color
--amplify-components-storagemanager-dropzone-active-border-color
--amplify-components-storagemanager-dropzone-active-border-radius
--amplify-components-storagemanager-dropzone-active-border-style
--amplify-components-storagemanager-dropzone-active-border-width
--amplify-components-storagemanager-dropzone-background-color
--amplify-components-storagemanager-dropzone-border-color
--amplify-components-storagemanager-dropzone-border-radius
--amplify-components-storagemanager-dropzone-border-style
--amplify-components-storagemanager-dropzone-border-width
--amplify-components-storagemanager-dropzone-gap
--amplify-components-storagemanager-dropzone-icon-color
--amplify-components-storagemanager-dropzone-icon-font-size
--amplify-components-storagemanager-dropzone-padding-block
--amplify-components-storagemanager-dropzone-padding-inline
--amplify-components-storagemanager-dropzone-text-align
--amplify-components-storagemanager-dropzone-text-color
--amplify-components-storagemanager-dropzone-text-font-size
--amplify-components-storagemanager-dropzone-text-font-weight
--amplify-components-storagemanager-file-align-items
--amplify-components-storagemanager-file-background-color
--amplify-components-storagemanager-file-border-color
--amplify-components-storagemanager-file-border-radius
--amplify-components-storagemanager-file-border-style
--amplify-components-storagemanager-file-border-width
--amplify-components-storagemanager-file-gap
--amplify-components-storagemanager-file-image-background-color
--amplify-components-storagemanager-file-image-border-radius
--amplify-components-storagemanager-file-image-color
--amplify-components-storagemanager-file-image-height
--amplify-components-storagemanager-file-image-width
--amplify-components-storagemanager-file-name-color
--amplify-components-storagemanager-file-name-font-size
--amplify-components-storagemanager-file-name-font-weight
--amplify-components-storagemanager-file-padding-block
--amplify-components-storagemanager-file-padding-inline
--amplify-components-storagemanager-file-size-color
--amplify-components-storagemanager-file-size-font-size
--amplify-components-storagemanager-file-size-font-weight
--amplify-components-storagemanager-filelist-flex-direction
--amplify-components-storagemanager-filelist-gap
--amplify-components-storagemanager-loader-stroke-empty
--amplify-components-storagemanager-loader-stroke-filled
--amplify-components-storagemanager-loader-stroke-linecap
--amplify-components-storagemanager-loader-stroke-width
--amplify-components-storagemanager-previewer-background-color
--amplify-components-storagemanager-previewer-body-gap
--amplify-components-storagemanager-previewer-body-padding-block
--amplify-components-storagemanager-previewer-body-padding-inline
--amplify-components-storagemanager-previewer-border-color
--amplify-components-storagemanager-previewer-border-radius
--amplify-components-storagemanager-previewer-border-style
--amplify-components-storagemanager-previewer-border-width
--amplify-components-storagemanager-previewer-footer-justify-content
--amplify-components-storagemanager-previewer-max-height
--amplify-components-storagemanager-previewer-max-width
--amplify-components-storagemanager-previewer-padding-block
--amplify-components-storagemanager-previewer-padding-inline
--amplify-components-storagemanager-previewer-text-color
--amplify-components-storagemanager-previewer-text-font-size
--amplify-components-storagemanager-previewer-text-font-weight