Availity MUI File Selector component to be used with @availity/element design system.
This package extends the MUI File Selector component: MUI File Selector Docs
Live demo and documentation in our Storybook
Availity standards for design and usage can be found in the Availity Design Guide
npm install @availity/element
yarn add @availity/element
This package has a few peer dependencies. Add @mui/material
& @emotion/react
to your project if not already installed.
npm install @availity/mui-file-selector
yarn add @availity/mui-file-selector
<FileSelector />
and <FileSelector2 />
are two takes on the same functionality.
At a high level, the difference between the two is the logic for handling
the upload request. The <FileSelector />
component utilizes
@tanstack/react-query
to handle the upload request logic, while <FileSelector2 />
uses the form state to handle the upload request logic.
<FileSelector />
is a more established and battle-tested component. If you
don't need access to the Upload object, <FileSelector />
may be a good option
for you.
<FileSelector2 />
is slightly more experimental. It maintains feature parity
with <FileSelector />
, but can be considered a work in progress. <FileSelector2 />
more closely aligns with our future plans for the <FileSelector />
component.
In a future major release of @availity/element
, we will consolidate to a single File
Selector component. You can think of <FileSelector2 />
as a bridge to get
there. If you want to continue using the existing <FileSelector />
, we will
provide a simple migration path when the consolidation happens.
- Easier access to the Upload object
- Simplified internals
- Aligns with future development plans
import { FileSelector } from '@availity/element';
import { FileSelector } from '@availity/mui-file-selector';
The FileSelector
component must be used inside a FormProvider
from react-hook-form
and a QueryClientProvider
from @tanstack/react-query
. Each provider has its own state that is necessary for using the component. The FormProvider
stores the Files
that are selected while the QueryClientProvider
has the upload
response data.
import React from 'react';
import { FileSelector } from '@availity/mui-file-selector';
const MyComponent = () => {
const methods = useForm({
defaultValues: {
myFiles: [] as File[],
},
});
const client = useQueryClient();
const files = methods.watch(props.name);
const handleOnSubmit = (values: Record<string, File[]>) => {
if (values.myFiles.length === 0) return;
const queries = client.getQueriesData<Upload>(['upload']);
const uploads = [];
for (const [, data] of queries) {
if (data) uploads.push(data);
}
};
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(handleOnSubmit)}>
<FileSelector
name="myFiles"
bucketId="your-bucket-id"
customerId="your-customer-id"
clientId="your-client-id"
maxSize={5 * 1024 * 1024} // 5MB
maxFiles={3}
allowedFileTypes={['.pdf', '.doc', '.docx']}
/>
</form>
</FormProvider>
);
};
export default MyComponent;
Note: the following examples assume you have setup
react-hook-form
andreact-query
already
import React from 'react';
import { FileSelector } from '@availity/mui-file-selector';
const MyFileUploadComponent = () => {
const handleOnDrop = (acceptedFiles, fileRejections, event) => {
// Use this callback for interacting with the files before they are uploaded
};
const handleValidation = (file) => {
// Custom validation can be added with the `validator` prop.
// If an error fails validation here it should show up
// in the `fileRejections` array from `onDrop`.
//
// To return a custom error, return an object with a code
// and message.
// return { code: 'an-error', message: 'An error occurred' };
};
return (
<FileSelector
name="documentUpload"
bucketId="your-bucket-id"
customerId="your-customer-id"
clientId="your-client-id"
maxSize={10 * 1024 * 1024} // 10MB
allowedFileTypes={['.pdf', '.doc', '.docx']}
multiple={true}
maxFiles={5}
onDrop={handleOnDrop}
validator={handleValidation}
/>
);
};
export default MyFileUploadComponent;
It is possible to pass callbacks based on whether the upload finished successfully or there was an error.
import React from 'react';
import { FileSelector } from '@availity/mui-file-selector';
const MyFileUploadComponent = () => {
const handleOnSuccess = () => {
// Handle successful upload - e.g., show success message, update UI
};
const handleOnError = (error) => {
// Handle upload error - e.g., show error message, retry upload
};
const handleOnProgress = (error) => {
// Handle upload error - e.g., show error message, retry upload
};
return (
<FileSelector
name="documentUpload"
bucketId="your-bucket-id"
customerId="your-customer-id"
clientId="your-client-id"
maxSize={10 * 1024 * 1024} // 10MB
allowedFileTypes={['.pdf', '.doc', '.docx']}
multiple={true}
maxFiles={5}
uploadOptions={{
onSuccess: handleOnSuccess,
onError: handleOnError,
onProgress: handleOnProgress,
}}
/>
);
};
export default MyFileUploadComponent;
If you would like to show different information in each row then you are able to pass a custom FileRow
component. We recommend using the ListItem
component. The upload object from @availity/upload-core
, the options passed to its constructor, and the onRemoveFile
function will all be passed as props.
import React from 'react';
import { FileSelector } from '@availity/mui-file-selector';
import { ListItem } from '@availity/mui-list';
const FileRow = ({ upload, options, onRemoveFile }) => {
return <ListItem>Your code here</ListItem>;
};
const MyFileUploadComponent = () => {
return (
<FileSelector
name="documentUpload"
bucketId="your-bucket-id"
customerId="your-customer-id"
clientId="your-client-id"
maxSize={10 * 1024 * 1024} // 10MB
allowedFileTypes={['.pdf', '.doc', '.docx']}
multiple={true}
maxFiles={5}
customFileRow={FileRow}
/>
);
};
export default MyFileUploadComponent;
To provide custom help text, pass it as a child of the <FileSelector />
component. The help text should be formatted using the <Typography />
component with the 'caption'
variant.
import React from 'react';
import { FileSelector } from '@availity/mui-file-selector';
import { Typography } from '@availity/mui-typography';
const MyComponent = () => {
const methods = useForm({
defaultValues: {
myFiles: [] as File[],
},
});
const client = useQueryClient();
const files = methods.watch('myFiles);
const handleOnSubmit = (values: Record<string, File[]>) => {
if (values.myFiles.length === 0) return;
const queries = client.getQueriesData<Upload>(['upload']);
const uploads = [];
for (const [, data] of queries) {
if (data) uploads.push(data);
}
};
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(handleOnSubmit)}>
<FileSelector
name="myFiles"
bucketId="your-bucket-id"
customerId="your-customer-id"
clientId="your-client-id"
maxSize={5 * 1024 * 1024} // 5MB
maxFiles={3}
allowedFileTypes={['.pdf', '.doc', '.docx']}
>
<Typography component="div" variant="caption">Here is some help text.</Typography>
</FileSelector>
</form>
</FormProvider>
);
};
export default MyComponent;
import { FileSelector2 } from '@availity/element';
import { FileSelector2 } from '@availity/mui-file-selector';
The FileSelector2
component must be used inside a FormProvider
from react-hook-form
. Each provider has its own state that is necessary for using the component.
import React from 'react';
import { FileSelector2 } from '@availity/mui-file-selector';
const MyComponent = () => {
const methods = useForm({
defaultValues: {
myFiles: [] as File[],
},
});
const uploads = methods.watch(props.name);
const handleOnSubmit = (values: Record<string, File[]>) => {
if (values.myFiles.length === 0) return;
};
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(handleOnSubmit)}>
<FileSelector2
name="myFiles"
bucketId="your-bucket-id"
customerId="your-customer-id"
clientId="your-client-id"
maxSize={5 * 1024 * 1024} // 5MB
maxFiles={3}
allowedFileTypes={['.pdf', '.doc', '.docx']}
/>
</form>
</FormProvider>
);
};
export default MyComponent;