Components

Dropfile

Allow users to upload files by dragging and dropping them onto the page.

Click to upload or drag & drop files.

All file types accepted

Source Code

Copy the following code into your project. Modify it to your liking

<template>
  <div
    @click="open()"
    ref="dropZoneRef"
    :class="
      cn(
        'flex w-full cursor-pointer items-center justify-center rounded-md border border-dashed transition hover:border-primary',
        $attrs.class,
        isOverDropZone && 'border-primary bg-primary/10'
      )
    "
  >
    <slot name="message">
      <div class="py-10 text-center">
        <slot name="icon">
          <div
            v-if="icon"
            class="inline-flex items-center justify-center rounded-md border p-2 transition"
            :class="[isOverDropZone && 'animate-bounce border-primary']"
          >
            <Icon
              :name="icon"
              class="h-7 w-7 opacity-70"
              :class="[isOverDropZone && 'text-primary']"
            />
          </div>
        </slot>
        <slot name="title">
          <p class="mt-5 text-sm font-medium" v-html="title"></p>
        </slot>
        <slot name="subtext">
          <p class="mt-1 text-sm text-muted-foreground/60" v-html="subtext"></p>
        </slot>
      </div>
    </slot>
  </div>
</template>

<script setup lang="ts">
  const props = withDefaults(
    defineProps<{
      /**
       * The text to display as the title of the dropzone.
       */
      title?: string;
      /**
       * The text to display as the subtext of the dropzone.
       */
      subtext?: string;
      /**
       * The icon to display in the dropzone.
       */
      icon?: string;
      /**
       * The function to call when files are dropped.
       */
      onDrop?: Function;
      /**
       * Whether or not to allow multiple files to be picked. Does not affect drag and drop.
       */
      multiple?: boolean;
      /**
       * The file types to accept. Does not affect drag and drop.
       */
      accept?: string;
    }>(),
    {
      title: "Click to upload or drag & drop files.",
      subtext: "All file types accepted",
      icon: "heroicons:cloud-arrow-up",
      multiple: true,
      accept: "*",
    }
  );

  const { open, reset, onChange } = useFileDialog({
    multiple: props.multiple,
    accept: props.accept,
  });

  onChange((files) => {
    handleDrop(Array.from(files || []));
    reset();
  });

  const dropZoneRef = ref<HTMLDivElement>();
  const emits = defineEmits<{
    dropped: [any];
  }>();

  const handleDrop = (files: File[] | null) => {
    if (!files) return;
    if (props.onDrop) props.onDrop(files);
    emits("dropped", files);
  };

  const { isOverDropZone } = useDropZone(dropZoneRef, handleDrop);
</script>