File Uploader
A versatile uploader pattern for seamless file or image uploads within modals or expanders, ensuring a smooth, uninterrupted user experience in any application.
- Overview
- Specs
- Guidelines
Pattern
The uploader pattern is a user interface design approach that facilitates the seamless uploading of files within an application.
This pattern is particularly useful in scenarios where users need to upload content without navigating away from their current task, ensuring a smooth and uninterrupted experience.
Whether you’re submitting documents, or sharing files, the file uploader pattern simplifies the process. By embedding the uploader in a modal or expander, users can easily access it when needed, while keeping the interface clean and organized when it’s not in use.
- A: Modal Version;
- B: Section Variation;
- 1: Modal Title;
- 2: Close;
- 3: Text Button;
- 4: Primary Button;
- 5: Empty State;
Used for:
Document submission
Enable users to upload required documents during a form submission process, within an expandable section for a cleaner interface;
File attachments in messaging
Users can attach files or images within a chat interface, using an uploader embedded in a modal.
Don’t use for:
Complex file management
For scenarios requiring extensive file management, such as organizing, renaming, or categorizing files, the uploader pattern may be too limited. A full-fledged file management interface would be better suited;
Limited screen space
In very small or mobile interfaces where screen real estate is constrained, embedding an uploader within a modal or expander could overcrowd the UI, making the experience cumbersome;
Security-sensitive environments
In highly secure environments where file uploads need additional checks, monitoring, or user confirmations, a dedicated, more controlled process might be necessary instead of a simple embedded uploader.
Demo
Access the Figma file and inspect the element using Dev Mode.

Last Update
- Added Empty State;
Variations
Uploader – Modal
Enabled

.enabled {
modal-bg: var(--grey-1);
close: var(--regular-enabled);
dropZone: var(--enabled);
primaryButton: var(--primary-theme-regular);
textButton: var(--textButton-grey-regular);
}
Upload from device

.enabled {
modal-bg: var(--grey-1);
close: var(--regular-enabled);
dropZone: var(--enabled);
cardUpload: var(--enabled);
primaryButton: var(--primary-theme-regular);
textButton: var(--textButton-grey-regular);
}
Uploader – Sections
Single Selection

.SingleSelection_enabled {
dropZone: var(--enabled);
}
.SingleSelection_upload {
cardUpload: var(--enabled);
}
Multiple Selection

.MultipleSelection_enabled {
dropZone: var(--enabled);
}
.MultipleSelection_upload {
dropZone: var(--enabledMultiple);
cardUpload1: var(--enabled);
cardUpload2: var(--enabled);
cardUpload3: var(--enabled);
}
Uploader – Drop Zone
Enabled

.enabled {
bg: var(--grey-1);
stroke: var(--grey-5);
icon: var(--grey-7);
intructions: var(--Button-large, --grey-7);
formatSupported: var(--overline, --grey-7);
button: var(--tertiary-grey-regular-enabled);
}
Hover

.enabled {
bg: var(--grey-2);
stroke: var(--grey-5);
icon: var(--grey-7);
intructions: var(--Button-large, --grey-7);
formatSupported: var(--overline, --grey-7);
button: var(--tertiary-grey-regular-hover);
}
Enabled Multiple

.enabledMultiple {
bg: var(--grey-1);
stroke: var(--grey-5);
icon: var(--grey-7);
intructions: var(--Button-large, --grey-7);
formatSupported: var(--overline, --grey-7);
button: var(--tertiary-grey-regular-hover);
cardUpload1: var(--enabled);
cardUpload2: var(--enabled);
cardUpload3: var(--enabled);
cardUpload4: var(--enabled);
}
Hover Multiple

.hoverMultiple {
bg: var(--grey-2);
stroke: var(--grey-5);
icon: var(--grey-7);
intructions: var(--Button-large, --grey-7);
formatSupported: var(--overline, --grey-7);
button: var(--tertiary-grey-regular-hover);
cardUpload1: var(--enabled);
cardUpload2: var(--enabled);
cardUpload3: var(--enabled);
cardUpload4: var(--enabled);
}
Uploader – Upload Card
With Progress Bar
Enabled
.enabled {
padding: var(--spacing-8);
bg: var(--grey-1);
stroke: var(--grey-5);
icon: var(--grey-7, 24px);
gapGeneral: var(--spacing-16);
fileName: var(--Label-large, --grey-8);
gapFileSize: var(--spacing-8);
size: var(--Label-regular, --grey-7);
progressBar: var(--0%, 100px);
close: var(--regular-enabled);
}
Hover
.hover {
padding: var(--spacing-8);
bg: var(--grey-2);
stroke: var(--grey-5);
icon: var(--grey-7, 24px);
gapGeneral: var(--spacing-16);
fileName: var(--Label-large, --grey-8);
gapFileSize: var(--spacing-8);
size: var(--Label-regular, --grey-7);
progressBar: var(--0%, 100px);
close: var(--regular-enabled);
}
Focus
.focus {
padding: var(--spacing-8);
bg: var(--grey-1);
stroke: var(--grey-8);
icon: var(--grey-7, 24px);
gapGeneral: var(--spacing-16);
fileName: var(--Label-large, --grey-8);
gapFileSize: var(--spacing-8);
size: var(--Label-regular, --grey-7);
progressBar: var(--0%, 100px);
close: var(--regular-enabled);
}
Completed
Enabled
.enabled {
padding: var(--spacing-8);
bg: var(--grey-1);
stroke: var(--grey-5);
icon: var(--grey-7, 24px);
gapGeneral: var(--spacing-16);
fileName: var(--Label-large, --grey-8);
gapFileSize: var(--spacing-8);
size: var(--Label-regular, --grey-7);
icon: var(--grey-7, 24px);
}
Hover
.hover {
padding: var(--spacing-8);
bg: var(--grey-2);
stroke: var(--grey-5);
icon: var(--grey-7, 24px);
gapGeneral: var(--spacing-16);
fileName: var(--Label-large, --grey-8);
gapFileSize: var(--spacing-8);
size: var(--Label-regular, --grey-7);
icon: var(--grey-7, 24px);
}
Focus
.focus {
padding: var(--spacing-8);
bg: var(--grey-1);
stroke: var(--grey-8);
icon: var(--grey-7, 24px);
gapGeneral: var(--spacing-16);
fileName: var(--Label-large, --grey-8);
gapFileSize: var(--spacing-8);
size: var(--Label-regular, --grey-7);
icon: var(--grey-7, 24px);
}
Error
Enabled
.enabled {
padding: var(--spacing-8);
bg: var(--grey-1);
stroke: var(--grey-5);
icon: var(--grey-6, 24px);
gapGeneral: var(--spacing-16);
fileName: var(--Label-large, --grey-6);
gapFileSize: var(--spacing-8);
size: var(--Label-regular, --grey-6);
statusIndicator: var(--extended-negative);
icon: var(--grey-7, 24px);
}
Hover
.hover {
padding: var(--spacing-8);
bg: var(--grey-2);
stroke: var(--grey-5);
icon: var(--grey-6, 24px);
gapGeneral: var(--spacing-16);
fileName: var(--Label-large, --grey-6);
gapFileSize: var(--spacing-8);
size: var(--Label-regular, --grey-6);
statusIndicator: var(--extended-negative);
icon: var(--grey-7, 24px);
}
Focus
.focus {
padding: var(--spacing-8);
bg: var(--grey-1);
stroke: var(--grey-8);
icon: var(--grey-6, 24px);
gapGeneral: var(--spacing-16);
fileName: var(--Label-large, --grey-6);
gapFileSize: var(--spacing-8);
size: var(--Label-regular, --grey-6);
statusIndicator: var(--extended-negative);
icon: var(--grey-7, 24px);
}
Useful links

Consult our Figma file to access our assets and inspect them in dev mode.

This component is or will be provided by the Polygon framework. See its documentation to learn more.

This element is in line with the guidelines of the CDS (Cegid Design System). Find out more.
Variations
The uploader pattern offers flexibility through single and multiple selection variations, catering to different user needs. Single selection is optimal for tasks requiring only one file, such as updating a profile picture, ensuring focus and simplicity.
On the other hand, multiple selection enables users to upload several files simultaneously—ideal for situations like adding multiple photos to a gallery or attaching several documents to an email. Choosing between these variations depends on the specific context and desired user workflow within the application.
These are the states once the file(s) are added.
Show the loading progress;
The file upload button must have a text or label that correctly describes the button’s action.
Modal
The modal variation of the uploader pattern provides a focused and contained space for users to upload files or images without leaving their current workflow. By opening the uploader in a modal, users can quickly complete the task at hand while keeping the rest of the interface intact.
This approach is particularly effective for maintaining a clean and organized layout, as it temporarily brings the uploader into view without cluttering the main interface. The modal variation enhances user experience by balancing accessibility with minimal disruption.
Furthermore the file is retained within a text field, with the button icon variation, which allows the button to serve as a trigger to the File Uploader Modal. This style allows the file to be retained in a more “form-friendly” display.

Empty State
In the File Uploader pattern, the empty state is displayed by default in both the enabled and hover states in the drop-zone. This decision ensures a consistent visual language across components, reducing the need for additional states or transitions that might overcomplicate the user experience.
By maintaining consistency with other design elements, users can quickly recognize and interact with thumbnails without confusion.
