File upload

A drag-and-drop file input with preview, validation, and graceful fallback to the native file picker. Wraps a hidden <input type="file"> so standard form submission (including Rails form_with) works unchanged.

Basic upload

Preview
<div class="nano-upload"
     data-controller="nanoui-upload"
     data-nanoui-upload-accept-value="image/*"
     data-nanoui-upload-max-size-value="2097152"
     data-action="dragover->nanoui-upload#onDragover dragleave->nanoui-upload#onDragleave drop->nanoui-upload#onDrop">

  <input type="file" name="logo" class="nano-upload__input"
         data-nanoui-upload-target="input"
         data-action="change->nanoui-upload#onChange">

  <label class="nano-upload__dropzone"
         data-nanoui-upload-target="dropzone"
         data-action="click->nanoui-upload#openPicker"
         tabindex="0">
    <span class="nano-upload__icon"><svg>...</svg></span>
    <span class="nano-upload__prompt"><strong>Click to upload</strong> or drag and drop</span>
    <span class="nano-upload__hint">PNG or JPG up to 2 MB</span>
  </label>

  <div class="nano-upload__preview" data-nanoui-upload-target="preview">
    <img class="nano-upload__thumbnail" alt="" data-nanoui-upload-target="thumbnail">
    <div class="nano-upload__meta">
      <span class="nano-upload__name" data-nanoui-upload-target="name"></span>
      <span class="nano-upload__size" data-nanoui-upload-target="size"></span>
    </div>
    <button type="button" class="nano-upload__remove"
            data-action="click->nanoui-upload#remove">
      <svg>...</svg>
    </button>
  </div>

  <p class="nano-upload__error" data-nanoui-upload-target="error" hidden></p>
</div>

Values

  • accept (String) — MIME types / extensions (e.g. image/*, .pdf,.docx)
  • maxSize (Number, bytes) — reject files larger than this

Targets

  • input — the hidden <input type="file"> that carries the file into the form
  • dropzone — label/area shown when no file is selected
  • preview — container shown after a valid file is chosen
  • thumbnail<img> populated from FileReader for image files
  • name / size — metadata text nodes
  • error — validation message container

Events

  • nanoui-upload:selected — fired with { file } in detail when a valid file is chosen
  • nanoui-upload:removed — fired when the user clears the selection

Notes

  • The wrapper gets data-state="empty" | "dragover" | "filled" which CSS uses to show/hide the dropzone vs. preview. You can target this state yourself for custom animations.
  • Rails tip: pair with Active Storage by naming the input for your attribute (e.g. name="merchant[logo]"); no controller changes required.