A .drop-zone is a large target for choosing files. Put
an icon, a prompt and a native <input type="file">
inside a <label>. Add an optional
.drop-zone-files list to show what was chosen.
The CSS stretches the input invisibly over the whole label. A click, Enter, Space or a dropped file reaches it exactly as it would a plain file input, and the label text names it. For a compact button and path field, use the file input.
<div class="drop-zone">
<label>
<span class="material-symbols" aria-hidden="true">upload_file</span>
Drop files here or browse
<small>PDF or PNG files</small>
<input type="file" multiple accept=".pdf,.png">
</label>
<ul class="drop-zone-files" aria-live="polite"></ul>
</div>
Behavior
Add multiple to the input to take several files at
once. A dropped file replaces the selection, as it does on any file
input. The Forms behavior adds dragover to the zone
while files are over it. It fills .drop-zone-files with
each file's name and size, formatted for the page's locale. It
listens on the document, so zones added later work too. Resetting
the form empties the list after reset dispatch finishes; a canceled
reset preserves it. A newer selection keeps its list and feedback.
accept is a picker hint. Opt into the type validation
below for matching checks on both browse and drop selections.
Check received files on the server; metadata does not verify contents.
Type validation
Add data-validate-types to the native input to check every
selected file against accept. One unsupported file
invalidates the whole selection and blocks normal native form submission.
Every selected file stays in the input and optional list. Unmarked zones
keep their existing picker-hint behavior.
<div class="drop-zone">
<label>Drop documents here or browse
<small>PDF or PNG files</small>
<input type="file" multiple data-validate-types accept=".pdf,.png">
</label>
<p class="supporting-text" data-error="Choose PDF or PNG files."
role="status" aria-live="polite" hidden></p>
<ul class="drop-zone-files" aria-live="polite"></ul>
</div>
Match extensions such as .pdf or .tar.gz, exact
MIME types without parameters, such as application/pdf, or image/*,
audio/* and video/*. Comma-separated alternatives
are trimmed and case-insensitive. Empty MIME types can match an explicit
extension, but do not match MIME-only rules. Unsupported tokens are ignored;
missing, empty or wholly unsupported accept values impose no type restriction.
Supply the direct [data-error] status area even without a
file list. Its data-error value customizes the type message; an empty
value uses a message naming accept. The runtime writes real text,
appends its id once to the input's aria-describedby, and
updates aria-invalid without moving focus. Required fields
and application custom errors remain effective. Existing application
custom validity takes precedence over the native type message; the status
can still explain a type mismatch.
Valid replacement, clearing or an uncanceled reset releases only the
framework's type error. Remaining native/application errors stay visible.
Canceled resets preserve the current view. Disabled controls remain
inactive; after enabling or changing files/constraints from script, call
Expressive.Forms.validateField(input) to refresh validity and
feedback silently. A bubbling composed input or change also refreshes
the file list. Both paths work in open shadow roots.
A changed invalid selection reports one bubbling, composed, noncancelable
expressive:reject from the zone with
{ input, files, rejected, reason: 'type' }.
File arrays are snapshots. A native input/change pair reports once;
silent validation and reset report no rejection. This is a notification,
so canceling it cannot allow invalid files. Combine type validation with
the per-file size and count limits below. Uploads are outside these
options. Native novalidate/formnovalidate bypasses remain
native; always validate received files on the server.
Size limit
Add data-max-size="1048576" to limit each file to 1 MiB.
A file exactly at the limit passes. One oversized file invalidates the
whole selection and blocks ordinary native form submission; all files
remain in the input and optional list. The live example checks size only.
This markup adds type checks to the same size limit:
<div class="drop-zone">
<label>Drop documents here or browse
<small>PDF or PNG, at most 1 MiB per file</small>
<input type="file" multiple data-validate-types accept=".pdf,.png"
data-max-size="1048576">
</label>
<p class="supporting-text" data-error="Choose PDF or PNG files."
data-error-size="Choose files no larger than 1 MiB each."
role="status" aria-live="polite" hidden></p>
<ul class="drop-zone-files" aria-live="polite"></ul>
</div>
Use positive decimal safe-integer bytes; surrounding whitespace is allowed.
Missing, blank, zero, negative, fractional, exponent, hexadecimal, nonnumeric
or unsafe values impose no size limit. This limits each file separately,
not their combined size. A size-only input leaves accept as a
picker hint; add data-validate-types for type checking.
Reuse the direct polite [data-error] status area.
data-error-size customizes the size message; an empty value
uses a message naming the exact byte limit. If types fail, the type error
takes precedence. Otherwise oversized files report
{ input, files, rejected, reason: 'size' } through the
same noncancelable expressive:reject event. Its rejected
snapshot contains only oversized files, while files contains the whole
selection. Changing the type opt-in, accept or effective size limit can
report again after a file input/change event; equivalent byte limits
still deduplicate.
Script refresh, application-validity ownership, disabled controls, native
submission settings and reset recovery follow the type-validation contract
above. Forms.validateField(input) silently refreshes both
constraints. Always enforce received file limits on the server.
File count
Add data-max-files="2" to allow at most two selected files.
Exactly two pass. A larger selection stays intact, but native validity
blocks ordinary form submission and explains the count violation. This
live example checks count independently of type and size.
This markup combines type, per-file size and count checks:
<div class="drop-zone">
<label>Drop documents here or browse
<small>Up to 2 PDF or PNG files, at most 1 MiB each</small>
<input type="file" multiple data-validate-types accept=".pdf,.png"
data-max-size="1048576" data-max-files="2">
</label>
<p class="supporting-text" data-error="Choose PDF or PNG files."
data-error-size="Choose files no larger than 1 MiB each."
data-error-count="Choose no more than two files."
role="status" aria-live="polite" hidden></p>
<ul class="drop-zone-files" aria-live="polite"></ul>
</div>
Use positive decimal safe-integer counts; surrounding whitespace and
leading zeros are allowed. Missing, blank, zero, negative, fractional,
exponent, hexadecimal, nonnumeric or unsafe values impose no count limit.
Each FileList entry counts, including repeated filenames. Add
multiple to choose several files; the limit does not add it.
A count-only input leaves accept as a picker hint.
Supply the same direct polite [data-error] area.
data-error-count localizes count feedback; empty uses a
message naming the limit. Type errors take precedence, followed by size
errors and then count. A count failure reports
{ input, files, rejected, reason: 'count' } through
expressive:reject. Both arrays contain every chosen file,
independently; there is no accepted first-N subset. The event is
noncancelable. A changed effective count limit can report again after
input/change; equivalent decimal values still deduplicate.
Forms.validateField(input) silently refreshes all three
constraints after script changes. Valid replacement, clearing, opt-out
and native reset follow the same application-validity, cancellation,
disabled-control, shadow and external-form recovery contract above.
Always validate received counts, sizes and file contents on the server.
Tokens
Override these on the .drop-zone.
| Token | Default |
|---|---|
--md-comp-drop-zone-shape |
16px |
--md-comp-drop-zone-outline-color |
--md-sys-color-outline |
--md-comp-drop-zone-dragover-container-color |
--md-sys-color-primary-container |
--md-comp-drop-zone-dragover-color |
--md-sys-color-on-primary-container |