FileUpload
Drag-and-drop file picker with accept/size/count validation, four trigger variants, and a built-in list with image thumbnails or file-type badges.
Wraps a native <input type="file">; the drop zone exposes role="button" with full keyboard activation (Enter/Space) and an aria-describedby hint when accept is set.
import { FileUpload } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ width: 420 }}>
<FileUpload multiple />
</div>
);
}
import { FileUpload } from "@arshad-shah/cynosure-react";
<FileUpload onFilesChange={(files) => console.log(files)} />The default render is FileUploadTrigger + FileUploadList — pass children to compose your own layout.
Variants
Section titled “Variants”default (the main affordance), card, compact, and minimal change the trigger’s visual treatment.
import { FileUpload, Stack } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<Stack gap="4" style={{ width: 420 }}>
<FileUpload variant="default" />
<FileUpload variant="card" />
<FileUpload variant="compact" />
<FileUpload variant="minimal" />
</Stack>
);
}
Multiple files
Section titled “Multiple files”multiple enables multi-select and cumulative drops. maxCount caps the list size.
import { FileUpload } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ width: 420 }}>
<FileUpload multiple maxCount={5} />
</div>
);
}
Accept and size limits
Section titled “Accept and size limits”Restrict by MIME type, extension, or wildcard via accept. Cap individual file bytes with maxSize; the onError callback fires for rejected files with a reason of "type", "size", or "count".
import { FileUpload } from '@arshad-shah/cynosure-react';
import { useState } from 'react';
export default function Example() {
const [error, setError] = useState<string | null>(null);
return (
<div style={{ width: 420 }}>
<FileUpload
multiple
accept="image/*,.pdf"
maxSize={5 * 1024 * 1024}
onError={(e) => setError(e.message)}
onFilesChange={() => setError(null)}
/>
{error ? (
<p style={{ marginTop: '0.5rem', color: 'var(--cynosure-color-danger-solid)' }}>{error}</p>
) : null}
</div>
);
}
Composition
Section titled “Composition”Drop in just the trigger or just the list when you need a custom layout — both read from the FileUpload context.
import { FileUpload, FileUploadList, FileUploadTrigger } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ width: 420 }}>
<FileUpload multiple variant="card">
<FileUploadTrigger />
<FileUploadList />
</FileUpload>
</div>
);
}
Disabled
Section titled “Disabled”import { FileUpload } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ width: 420 }}>
<FileUpload disabled />
</div>
);
}
Accessibility
Section titled “Accessibility”- The hidden
<input type="file">carries the canonicalaccept/multiple/disabledflags so OS file dialogs behave correctly. - The drop zone exposes
role="button"andtabIndex=0so it’s reachable by keyboard; Enter and Space open the file dialog. - When
acceptis set, the trigger announces the accepted types viaaria-describedbyso screen-reader users know the constraint up front. - The file list is a
<ul>witharia-live="polite"so additions and removals are announced without interrupting the user. - Each remove / preview action is an
IconButtonwith a per-filearia-label(“Remove avatar.png”) to keep the action unambiguous.
Recipes
Section titled “Recipes”- Use
variant="minimal"inside toolbars and chat composers where space is tight. - Pass
onErrorto surface validation feedback in your own toast orErrorText. - Provide
renderItemonFileUploadListfor fully-custom rows, oronPreviewto add a preview button before the remove button. - Pair with
nameto participate in a native<form>— the underlying input is submitted as a real file field.