Amplify UI

Storage Manager

The Storage Manager lets your users upload and manage files to the cloud.

Drop files here or

Basic Usage

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
    />
  );
};

Props

NameDescriptionType
accessLevelAccess level for files in Storage. See https://docs.amplify.aws/lib/storage/configureaccess/q/platform/js/
'guest' | 'protected' | 'private'
acceptedFileTypes?List of accepted file types, values of `['*']` or undefined allow any files
string[]
autoUpload?Determines if the upload will automatically start after a file is selected. The default value is `true`
boolean
maxFileCount
integer
maxFileSize?
integer
onUploadStart?Called when a file starts uploading
(file: {key: string}) => void;
onUploadSuccess?Called when a file successfully uploads
(file: {key: string}) => void;
onUploadError?Called when a error happens uploading a file
(error: string, file: {key: string}) => void;
onFileRemove?Called when a file is removed
(file: {key: string}) => void;
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.
(params: {key: string, file: Blob}) => Promise<{key: string, file: Blob} & Record<string, any>> | {key: string, file: Blob} & Record<string, string>;
path?Path in s3 to put the file under
string
defaultFiles?An array of files that already exist in the cloud.
Array<{s3key: string}>
displayText?Text to override in the component.
StorageManagerDisplayText
components?.Container?The container the StorageManager is wrapped in.
React.ComponentType<ContainerProps>
components?.DropZone?The dropzone element which contains the FilePicker
React.ComponentType<DropZoneProps>
components?.FilePicker?The button that opens the file picker menu.
React.ComponentType<FilePickerProps>
components?.FileList?The list of files that is being uploaded.
React.ComponentType<FileListProps>
components?.FileListHeader?The heading above the list of files
React.ComponentType<FileListHeaderProps>
components?.FileListFooter?The footer below the list of files
React.ComponentType<FileListFooterProps>
ref?Forward ref prop exposing StorageManager imperative methods.
React.ForwardedRef<StorageManagerHandle>

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 HTML accept 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:

  1. Performing file optimizations like removing unnecessary metadata.
  2. 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;
      })}
    </>
  );
};

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

NameDescriptionType
getFilesUploadedText?
(count: number) => string
getFileSizeErrorText?
(sizeText: string) => string
getRemainingFilesText?
(count: number) => string
getUploadingText?
(percentage: number) => string
getUploadButtonText?
(count: number) => string
getMaxFilesErrorText?
(count: number) => string
getErrorText?
(message: string) => string
getPausedText?
(percentage: string) => string
doneButtonText?Default: "Done"
string
clearAllButtonText?Default: "Clear all"
string
extensionNotAllowedText?Default: "Extension not allowed"
string
browseFilesText?Default: "Browse files"
string
dropFilesText?Default: "Drop files here or"
string
pauseButtonText?Default: "Pause"
string
resumeButtonText?Default: "Resume"
string
uploadSuccessfulText?Default: "Uploaded successfully"
string

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

NameDescriptionType
children
React.ReactNode
onClick
React.MouseEventHandler<HTMLButtonElement>

DropZone props

NameDescriptionType
acceptedFileTypesList of accepted file types
string[]
children
React.ReactNode
displayTextTest strings that are used in the component
StorageManagerDisplayText
isLoading?
boolean
onDragStart
(event: React.DragEvent<HTMLDivElement>) => void
onDragEnter
(event: React.DragEvent<HTMLDivElement>) => void
onDragLeave
(event: React.DragEvent<HTMLDivElement>) => void
onDragOver
(event: React.DragEvent<HTMLDivElement>) => void
onDrop
(event: React.DragEvent<HTMLDivElement>) => void
inDropZone
boolean

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.

ClassDescription
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

Amplify open source software, documentation and community are supported by Amazon Web Services.

© 2024 Amazon Web Services, Inc. and its affiliates. All rights reserved. View the site terms and privacy policy.

Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC.