Selector & list appearance axes, card sizes, popover, files-inside, single vs multiple, accept patterns
The visual surface is split into three independent attributes you can combine freely:
selector-appearance โ card ยท button ยท minimal ยท (what the user clicks/drops onto)list-appearance โ list ยท detailed ยท grid ยท badges ยท popover ยท none ยท (where picked files appear)card-size โ minimal ยท compact ยท big ยท (density of the card selector)The legacy single-axis display-mode attribute is still supported as a shorthand โ see the bottom of this page.
controls + item-controls)Two Plyr-style allowlists. controls picks which top-level surfaces render; item-controls picks which parts of each file row render. Both are comma-separated; when an attribute is absent everything renders. Toggle the boxes and watch the dropzone recompose live โ add a file to see the progress parts animate.
Count- or state-based progress behaviour doesn't need dedicated attributes โ compose it from the primitives above plus the change event and auto-upload. Two common asks:
progress token toggles.
const dz = document.getElementById('recipe-count')
const LEAN = 'icon,name,size,type,status,action,remove' // no progress
dz.addEventListener('change', () => {
const want = dz.files.length >= 4 ? null : LEAN
// skip no-op writes; `change` fires on selection changes only, so no loop
if (dz.getAttribute('item-controls') === want) return
want === null ? dz.removeAttribute('item-controls')
: dz.setAttribute('item-controls', want)
})
auto-upload="false" files sit pending and the strip stays hidden (hasActivity is false) until you kick off the upload โ no extra config, it's the default gate.
<web-dropzone auto-upload="false" ...></web-dropzone>
// files added โ pending โ global strip hidden (hasActivity = false)
startBtn.addEventListener('click', () => dz.uploadAll())
// upload starts โ strip appears on its own
selector-appearance)Four visual entry points for picking files.
select-files-text and no-file-chosen-text.
list-appearance.
list-appearance)How the picked files are presented. Six options ranging from a simple vertical list to badge-style pills or a hidden popover.
web-multiselect badges. Default: file-type icons in the pill. Set show-thumbnails for image previews, or override --dz-badge-icon-size: 0 to hide the slot entirely.
Grid mode is almost entirely CSS-variable driven. Drop a few images below, then move the sliders to reshape the grid in real time. The panel writes the same --dz-* custom properties you'd set in your own CSS โ the live readout is copy-pasteable.
Switch grid-layout to natural for a justified photo gallery: mixed portrait/landscape images share one row height (--dz-preview-row-height) and keep their own aspect ratio โ nothing cropped or stretched. In uniform layout, object-fit chooses crop (cover) vs. fit (contain). Set grid-status="overlay" and let a file finish uploading to see the light veil + big centred check/error glyph.
card-size)Density variants for the card selector. Only meaningful when selector-appearance="card".
files-inside)Render the file list inside the dropzone area instead of below it. Useful when vertical space is constrained or when you want a unified container look.
Mixing the three axes covers the patterns users actually want โ toolbars, attachment fields, marketing landings, and more.
summary-template)When list-appearance="popover" the summary line supports {count} and {size} placeholders โ quick customization without writing JS. For richer summaries (pluralization, badges) use renderSummaryCallback; see Structural Rendering.
popover-placement)Positioning uses Floating UI, so it auto-flips on viewport collisions. The attribute sets the preferred initial placement.
display-mode shorthandThe original single-axis attribute still works as a shorthand. When both are set, the orthogonal attribute wins.
display-mode="list" โ selector-appearance="card" + list-appearance="list"display-mode="detailed" โ card + detaileddisplay-mode="grid" โ card + griddisplay-mode="compact" โ card + popoverThe multiple attribute defaults to true. Set multiple="false" to lock to a single file (subsequent drops replace the existing selection).
The accept attribute uses the same syntax as the native <input type="file">: comma-separated MIME types, MIME wildcards, or file extensions.
Files that don't match accept are rejected at validation time and surface via the files-rejected event โ see Validation.
The disabled attribute follows HTML convention: presence enables it, ="false" overrides.
Methods and events let you drive the dropzone from your own code.
(none yet โ drop or pick a file)
const el = document.getElementById('api-demo')
el.addEventListener('file-added', e => console.log('added', e.detail))
el.addEventListener('file-removed', e => console.log('removed', e.detail))
el.addEventListener('files-rejected', e => console.log('rejected', e.detail))
el.addEventListener('change', e => console.log('change', e.detail))
el.addFiles(fileList) // programmatic add (FileList or File[])
el.removeFile(id) // remove by FileState.id
el.clear() // remove all
const files = el.files // current FileState[]
fileIcons + fileIconCallback)Each row's icon resolves from the file's category by default (Lucide glyphs). Override per extension or per category with the fileIcons map, or take full control with fileIconCallback. Values are any HTML string โ this demo mixes an inline SVG (.psd), an <img> (.fig), and emoji (.sketch, the archive category, and the callback).
hero.psd โ SVG ยท ui.fig โ <img> ยท logo.sketch โ ๐ ยท
assets.zip โ ๐ฆ (archive category) ยท invoice-*.pdf โ ๐งพ (callback) ยท
photo.jpg / notes.txt โ built-in Lucide.
โ ๏ธ Icon values are inserted as raw HTML (innerHTML) and are NOT sanitized. Only pass markup you control โ never untrusted / user-supplied strings.
const el = document.getElementById('icon-demo')
// Keyed by EXTENSION (leading dot optional, case-insensitive) OR CATEGORY.
// Extension keys win over category keys, which win over the built-in icon.
el.fileIcons = {
psd: '<svg โฆ>โฆ</svg>', // extension โ inline SVG
fig: '<img src="โฆ" alt="">', // extension โ <img>
sketch: '๐', // extension โ emoji
archive: '๐ฆ', // whole CATEGORY โ emoji
}
// Consulted BEFORE the map. Return HTML to use it, or null to fall through.
el.fileIconCallback = (file) =>
/^invoice/i.test(file.name) ? '๐งพ' : null