Agriculture Design System
Beta
Design System for the Export Service

File upload

The File upload component allows users to attach one or multiple files to a form via drag-and-drop or by browsing their device.

View in FigmaView in StorybookView in Github
import { ... } from '@ag.ds-next/react/file-upload';
Open in Playroom, opens in a new tab
() => {
	const [value, setValue] = React.useState();
	return (
		<FileUpload label="Upload documents" value={value} onChange={setValue} />
	);
};

File upload is used in forms to enable users to upload files. Unlike File input it can be used to upload multiple files, restrict the number, types and size of files, plus much more. There are 2 ways to upload – via drag and drop or by browsing the device.

FileUpload is a controlled component.

Do

  • upload selected files instantly as they are selected or on submission
  • specify accepted file types, maximum file size and whether the user can upload one file at a time or several at once
  • include hint text to provide more general information about the field/file
  • indicate file uploading state
  • Make use of the ‘instant upload’ pattern if your backend supports it
  • Consider the ‘simple download’ pattern for small screens

Don’t

  • add a standalone Submit file button inside a form
  • include other unrelated fields on the page
  • use in modals or page alerts
  • change the default behaviour of the component by adding a standalone upload file button.

Multiple files

Selecting multiple files is also supported with FileUpload. Simply add multiple={true}, and you can select as many files as you want. You can also set maxFiles to limit how many files can be selected.

() => {
	const [value, setValue] = React.useState();
	return (
		<FileUpload
			label="Upload files"
			hint="Upload a maximum of three files"
			value={value}
			onChange={setValue}
			multiple={true}
			maxFiles={2}
			maxSize={200}
			accept={['image/jpeg', 'image/png']}
		/>
	);
};

Maximum file size

Use the maxSize prop to set a maximum size for each file. This value is measured in kilobytes (kB).

<FileUpload maxSize={200} /> // 200kB
<FileUpload maxSize={20000} /> // 20MB

Accepted files

Use the accept prop to specify what file types are allowed to be selected.

For a complete list allowed file formats, please refer to the source code.

<FileUpload label="Upload image" accept={['image/jpeg', 'image/png']} />

Custom accepted file types

If the file type you need to support isn’t in the list of allowed file formats, you can pass in your own file type to the accept prop.

This can be done by passing an object instead of a string with mimeType and extensions as keys. Optionally, a label key can be used for the summary inside of the component. By default, the label will be generated using the file extension.

<FileUpload
label="Upload JavaScript file"
accept={[{ mimeType: 'text/javascript', extensions: ['.js'] }]}
/>

Hiding thumbnails

By default, the FileUpload component will render a preview of every selected file.

To disable this behaviour, set the hideThumbnails prop to true.

<FileUpload hideThumbnails={true} />

Indicating upload status

When using FileUpload, you need to consider how to communicate to the user that the upload is in progress. Otherwise the page will appear ‘frozen’ as the operation happens in the background.

Uploading on submission

In this example, we are submitting a file as part of a typical form submission to an API.

We recommend using a LoadingBlanket component as the form submits to indicate to the user that any delays are being caused by a potentially larger file being uploaded. If your backend system supports it, you can also use this UI to indicate upload status as a percentage.

As normal, we indicate that the form is submitting by adding a loading={true} to the submit button. This will show animated dots to signify that the user has to wait for the operation to complete.

<FormStack>
	<div style={{ position: 'relative' }}>
		<FileUpload label="Upload documents" multiple={false} />
		<LoadingBlanket label="Uploading file (53%)" />
	</div>
	<div>
		<Button loading={true}>Submit</Button>
	</div>
</FormStack>

Uploading files instantly

In this example, the file is instantly uploaded to a file hosting service, and the URL of the uploaded asset would be added to the form for submission. This means the form should not be allowed to submit until all assets are uploaded, but that the submission should be very quick as it’s only text content being submitted.

FileUpload component allows you to indicate the status of an upload via a file.status property.

() => {
	const uploadedFile = createExampleImageFile({ status: 'success' });
	const uploadingFile = createExampleFile({ status: 'uploading' });

	const [value, setValue] = React.useState([uploadedFile, uploadingFile]);

	function onChange(files) {
		setValue(
			files.map((file) => {
				file.status = file.status || 'uploading';
				return file;
			})
		);
		// Show uploaded state after simulated network request
		setTimeout(() => {
			setValue(
				files.map((file) => {
					if (file.status == 'uploading') {
						file.status = 'success';
					}
					return file;
				})
			);
		}, 1500);
	}

	return (
		<FileUpload
			label="Upload documents"
			multiple={true}
			value={value}
			onChange={onChange}
		/>
	);
};

Existing files

You can display a list of files that have already been uploaded by using the existingFiles and onRemoveExistingFile props.

Unlike the value prop, which expects an array of File objects, existingFiles expects an array of objects with a name, size and thumbnail url.

If a user attempts to delete an existing file, a destructive confirmation modal should be displayed. If confirmed, the file should be deleted on the server.

Image files should be displayed with thumbnails which are 72px by 72px. Passing full-size images to thumbnailSrc prop will result with long load times.

() => {
	const [existingFiles, setExistingFiles] = useState([
		{
			name: 'example.png',
			size: 123456,
			thumbnailSrc: 'https://via.placeholder.com/150',
			href: '#',
			// Use the meta key to keep track of any extra file info
			// This can be useful info when deleting the file
			meta: { uid: 'abc-def', bucketId: '123-456' },
		},
		{
			name: 'example-no-thumbnail.doc',
			size: 654321,
			href: '#',
			// Use the meta key to keep track of any extra file info
			// This can be useful info when deleting the file
			meta: { uid: 'fed=cba', bucketId: '654-321' },
		},
	]);

	function onRemoveExistingFile(fileToRemove) {
		setExistingFiles((existingFiles) =>
			existingFiles.filter((file) => file.meta.uid !== fileToRemove.meta.uid)
		);
	}

	const [value, setValue] = React.useState();

	return (
		<FileUpload
			label="Upload documents"
			multiple={true}
			value={value}
			onChange={setValue}
			existingFiles={existingFiles}
			onRemoveExistingFile={onRemoveExistingFile}
		/>
	);
};

Required

The required prop can be used to indicate that user input is required on the field before a form can be submitted.

Required fields do not append ‘(optional)’ to the label and also use aria-required to indicate to screen readers that the field is required.

Hide optional label

The hideOptionalLabel prop can be used in situations where you want to indicate to screen reader users that a field is optional but don’t want to show the ‘(optional)’ label.

Invalid

Use the invalid prop to indicate if the user input is invalid.

() => {
	const [value, setValue] = React.useState([]);
	return (
		<FileUpload
			label="Upload a file"
			value={value}
			onChange={setValue}
			invalid={value.length === 0}
			required={true}
			message="Select a file"
		/>
	);
};
  • File input The File input component allows users to attach one file to a form field by browsing their device.