File Upload <mv-file-upload>
Zone de dépôt de fichiers : glisser-déposer avec mise en évidence (et refus visuel des types non autorisés), liste avec miniatures d’images, icônes, tailles et suppression, validation du type, de la taille et du nombre, et progression par fichier ; associé aux formulaires (FormData).
Catégorie Formulaires Type Web Component (<mv-file-upload>) Statut stable Keywords file-upload, dropzone, drag-and-drop, upload, attachments, progress, form-associated
When to use
Users attach documents or images by drag and drop with type, size and count limits enforced Uploads should show per-file progress, thumbnails and a way to remove a file before submitting A form must submit selected files natively through FormData
Avoid when
A single optional file is picked in a dense form where a drop zone takes too much room → use Input instead Files are dropped into a chat message composer → use AI Chat instead
Obtenir File Upload. Inclus dans toutes les offres : le Pro pack contient les 317 composants, avec la CLI et le serveur MCP utilisés ci-dessous.
Voir les offres
Installation
node scripts/add.mjs file-upload --out ./src/marvelous
Agent IA avec le serveur MCP Marvelous UI : install_components({ slugs: ["file-upload"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Fichiers copiés (dépendances comprises) : tokens/tokens.css, core/base.css, core/dom.js, core/element.js, components/file-upload/file-upload.js, components/file-upload/file-upload.css.
Utilisation
Balisage de référence : partez de celui-ci et personnalisez-le avec les attributs, data-* et les variables CSS :
<form id="file-upload-demo-form" style="display:grid;gap:.5rem;width:min(100%,30rem)">
<label for="file-upload-demo" style="font-size:.875rem;font-weight:500">Supporting documents</label>
<mv-file-upload id="file-upload-demo" name="attachments" accept="image/*,.pdf,.docx,.xlsx" multiple max-size="5 MB" max-files="6"></mv-file-upload>
</form>
<script type="module">
const up = document.getElementById("file-upload-demo");
// Simulated upload: progress ticks until 100%, one in eight fails.
let seeding = false;
const simulate = (file, from = 0) => {
let pct = from;
const fail = Math.random() < 0.125;
const tick = () => {
if (!up.files.includes(file)) return;
pct += 6 + Math.random() * 16;
if (fail && pct > 55) return up.setError(file, "Connection lost");
up.setProgress(file, Math.min(100, pct));
if (pct < 100) setTimeout(tick, 180 + Math.random() * 260);
};
setTimeout(tick, 150);
};
up.addEventListener("mv-add", (e) => { if (!seeding) e.detail.files.forEach((f) => simulate(f)); });
// Seed the demo with a few realistic files.
customElements.whenDefined("mv-file-upload").then(async () => {
const canvas = document.createElement("canvas");
canvas.width = 160; canvas.height = 160;
const ctx = canvas.getContext("2d");
const g = ctx.createLinearGradient(0, 0, 160, 160);
g.addColorStop(0, "#f6d365"); g.addColorStop(0.55, "#fda085"); g.addColorStop(1, "#8e7cc3");
ctx.fillStyle = g; ctx.fillRect(0, 0, 160, 160);
ctx.fillStyle = "rgba(255,255,255,.85)";
ctx.beginPath(); ctx.arc(112, 48, 18, 0, Math.PI * 2); ctx.fill();
ctx.fillStyle = "rgba(40,30,70,.55)";
ctx.beginPath(); ctx.moveTo(0, 160); ctx.lineTo(56, 84); ctx.lineTo(96, 128); ctx.lineTo(124, 100); ctx.lineTo(160, 140); ctx.lineTo(160, 160); ctx.fill();
const blob = await new Promise((r) => canvas.toBlob(r, "image/jpeg", 0.8));
const pad = (n) => new Uint8Array(n);
const photo = new File([blob, pad(1_380_000)], "site-facade.jpg", { type: "image/jpeg" });
const quote = new File([pad(842_000)], "signed-quote-2026-09.pdf", { type: "application/pdf" });
const plan = new File([pad(2_310_000)], "floor-plan-level-2.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" });
seeding = true;
const [a, b, c] = up.addFiles([photo, quote, plan]);
seeding = false;
up.setProgress(a, 100);
up.setProgress(b, 100);
up.setProgress(c, 64);
setTimeout(() => simulate(c, 64), 2500);
});
</script>
API
Attributes
Name Type Default Description namestring Name of the form entries (one per file). acceptstring Accepted types, like <input type=file>: “image/*,.pdf,application/zip”. multipleboolean Multiple files (otherwise a new file replaces the previous one). max-sizestring Max size per file: “5 MB”, “500KB”, “1048576”. max-filesnumber Max number of files (with multiple). labelstring Main text of the zone (default: “Drag and drop files or browse”). hintstring Hint text (generated default: types · size · count). layoutlist | grid listDetailed list or thumbnail grid. requiredboolean Native validation (valueMissing). disabledboolean Disables the zone. localestring en-USSize formatting (Intl: “2.4 MB”).
Properties
Name Type Description filesFile[] Kept files (read-only). itemsArray<{ id, file, status, progress, error }> State of each file: idle | uploading | done | error.
Methods
Name Description open()Opens the native file picker. addFiles(files)Validates and adds files; returns the accepted ones. remove(file | id)Removes a file (and releases its preview). clear()Clears the list. setProgress(file | id, pct)Updates the progress bar (100 = uploaded). setError(file | id, message)Marks a file as failed. checkValidity() / reportValidity()Native validation.
Events
Name Description mv-adddetail: { files }, accepted files (hook point to start the upload). mv-removedetail: { file }. mv-changedetail: { files }, full list after an add/remove. mv-rejectdetail: { errors: [{ file, reason: type | size | count, message }] }.
Content structure
Name Description child contentReplaces the default content of the drop zone.
CSS classes
Name Description mv-file-upload-zone / -item / -thumb / -progress / -errorsGenerated parts; data-dragging / data-drag-reject on the host while dragging.
CSS variables
Name Default Description --mv-file-upload-width32remMax width. --mv-file-upload-zone-height10remMin height of the zone. --mv-file-upload-radiusvar(--mv-radius-xl)Zone radius.
Accessibility
The zone is a focusable role=button (Enter/Space open the picker), named by the <label for> and described by the hint text. Validation errors go in a role=alert region; additions, removals and completed uploads are announced through a polite live region. Each file has a “Remove <name>” button (focus moves to the next one) and a role=progressbar bar while uploading.