# Dropzone

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";
---

<div class="w-full max-w-[400px]">
  <Dropzone />
</div>
```
  </div>
  <div slot="react">
```tsx
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";

export function Example() {
  return (
    <div className="w-full max-w-[400px]">
      <Dropzone />
    </div>
  );
}
```
  </div>
  <div slot="vue">
```vue
<script setup lang="ts">
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
</script>

<template>
  <div class="w-full max-w-[400px]">
    <Dropzone />
  </div>
</template>
```
  </div>
</FrameworkCodeSwitcher>

## Installation

```bash
npx starwind@latest add dropzone --framework astro
```
</DocsTabsContent>
```bash
npx starwind@latest add dropzone --framework react
```
</DocsTabsContent>
```bash
npx starwind@latest add dropzone --framework vue
```
</DocsTabsContent>
</DocsTabs>

## Usage

> **Info:** No dropped files on this page are uploaded anywhere.

## Default

The default Dropzone component displays a cloud upload icon with a prompt to upload files, and shows what files have been selected.

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";
---

<Dropzone />
```
  </div>
  <div slot="react">
```tsx
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";

export function Example() {
  return (
    <>
      <Dropzone />
    </>
  );
}
```
  </div>
  <div slot="vue">
```vue
<script setup lang="ts">
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
</script>

<template>
  <Dropzone />
</template>
```
  </div>
</FrameworkCodeSwitcher>

## Custom Content

The dropzone components can be customized through slots. The default `<Dropzone />` component has the following three components in the slot if nothing is passed:

<FrameworkCodeSwitcher>
<div slot="astro">
```astro
---
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
---

<Dropzone>
  <DropzoneUploadIndicator />
  <DropzoneLoadingIndicator />
  <DropzoneFilesList />
</Dropzone>
```
</div>
<div slot="react">
```tsx
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";

export function Example() {
  return (
    <Dropzone>
      <DropzoneUploadIndicator />
      <DropzoneLoadingIndicator />
      <DropzoneFilesList />
    </Dropzone>
  );
}
```
</div>
<div slot="vue">
```vue
<script setup lang="ts">
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
</script>

<template>
  <Dropzone>
    <DropzoneUploadIndicator />
    <DropzoneLoadingIndicator />
    <DropzoneFilesList />
  </Dropzone>
</template>
```
</div>
</FrameworkCodeSwitcher>

The `DropzoneUploadIndicator` and `DropzoneLoadingIndicator` each have their own slots to customize the upload and loading indicators. The `DropzoneFilesList` component can be passed any classes to use.

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";
---

<Dropzone>
  <DropzoneUploadIndicator>
    <span class="my-6 text-lg">Your specific message</span>
  </DropzoneUploadIndicator>
  <DropzoneLoadingIndicator />
  <DropzoneFilesList />
</Dropzone>
```
  </div>
  <div slot="react">
```tsx
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";

export function Example() {
  return (
    <>
      <Dropzone>
        <DropzoneUploadIndicator>
          <span className="my-6 text-lg">Your specific message</span>
        </DropzoneUploadIndicator>
        <DropzoneLoadingIndicator />
        <DropzoneFilesList />
      </Dropzone>
    </>
  );
}
```
  </div>
  <div slot="vue">
```vue
<script setup lang="ts">
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
</script>

<template>
  <Dropzone>
    <DropzoneUploadIndicator>
      <span class="my-6 text-lg">Your specific message</span>
    </DropzoneUploadIndicator>
    <DropzoneLoadingIndicator />
    <DropzoneFilesList />
  </Dropzone>
</template>
```
  </div>
</FrameworkCodeSwitcher>

## Loading

Nothing is actually uploaded when a file is selected, as internally a standard html `<input type="file">` is used. To show a loading indication initially, you can use the `isUploading` prop.

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";
---

<Dropzone isUploading={true} />
```
  </div>
  <div slot="react">
```tsx
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";

export function Example() {
  return (
    <>
      <Dropzone isUploading={true} />
    </>
  );
}
```
  </div>
  <div slot="vue">
```vue
<script setup lang="ts">
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
</script>

<template>
  <Dropzone :isUploading="true" />
</template>
```
  </div>
</FrameworkCodeSwitcher>

You can also use the `data-is-uploading` attribute to show or hide a loading indicator at any time. This is useful if you want to show a loading indicator while a file is actually being uploaded (like on form submission).

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import { Button } from "@/components/starwind/button";
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
---

<div class="flex w-full max-w-[400px] flex-col gap-4">
  <form id="dropzone-form-demo" class="space-y-4">
    <Dropzone id="my-dropzone" name="dropzone-file" />
    <Button type="submit" class="w-full">Submit</Button>
  </form>
</div>

<script>
  function handleFormSubmit() {
    const form = document.querySelector("#dropzone-form-demo") as HTMLFormElement;
    const dropzone = document.querySelector("#my-dropzone") as HTMLElement;
    if (form) {
      form.addEventListener("submit", (e) => {
        e.preventDefault();

        dropzone.setAttribute("data-is-uploading", "true");

        const formData = new FormData(form);
        const formValues = Object.fromEntries(formData.entries());

        // demo form data logging
        console.log("Form submission values:", formValues);

        // You can add additional logic here like:
        // - Form validation
        // - API submission
        // - Success/error handling

        // Simulate API submission
        setTimeout(() => {
          dropzone.setAttribute("data-is-uploading", "false");
        }, 2000);
      });
    }
  }

  handleFormSubmit();

  document.addEventListener("astro:after-swap", handleFormSubmit);
</script>

```
  </div>
  <div slot="react">
```tsx
import type { FormEvent } from "react";
import { useState } from "react";
import { Button } from "@/components/starwind/button";
import { Dropzone } from "@/components/starwind/dropzone";

export function Example() {
  const [isUploading, setIsUploading] = useState(false);

  async function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    setIsUploading(true);
    console.log(
      "Form submission values:",
      Object.fromEntries(new FormData(event.currentTarget).entries()),
    );
    await new Promise((resolve) => setTimeout(resolve, 2000));
    setIsUploading(false);
  }

  return (
    <div className="flex w-full max-w-[400px] flex-col gap-4">
      <form className="space-y-4" onSubmit={handleSubmit}>
        <Dropzone name="dropzone-file" isUploading={isUploading} />
        <Button type="submit" className="w-full">Submit</Button>
      </form>
    </div>
  );
}
```
  </div>
  <div slot="vue">
```vue
<script setup lang="ts">
import { Button } from "@/components/starwind/button";
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
import { onBeforeUnmount, ref } from "vue";

const uploading = ref(false);
let timer: ReturnType<typeof setTimeout> | undefined;
function submit(event: Event) {
  console.log(
    "Form submission values:",
    Object.fromEntries(new FormData(event.currentTarget as HTMLFormElement)),
  );
  uploading.value = true;
  clearTimeout(timer);
  timer = setTimeout(() => {
    uploading.value = false;
  }, 2000);
}
onBeforeUnmount(() => clearTimeout(timer));
</script>

<template>
  <div class="flex w-full max-w-[400px] flex-col gap-4">
    <form @submit.prevent="submit" id="dropzone-form-demo" class="space-y-4">
      <Dropzone :is-uploading="uploading" id="my-dropzone" name="dropzone-file" />
      <Button type="submit" class="w-full">Submit</Button>
    </form>
  </div>
</template>
```
  </div>
</FrameworkCodeSwitcher>

## Accept Specific File Types

You can limit the types of files that users can select by using the `accept` attribute.

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";
---

<Dropzone accept="image/*" />
```
  </div>
  <div slot="react">
```tsx
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";

export function Example() {
  return (
    <>
      <Dropzone accept="image/*" />
    </>
  );
}
```
  </div>
  <div slot="vue">
```vue
<script setup lang="ts">
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
</script>

<template>
  <Dropzone accept="image/*" />
</template>
```
  </div>
</FrameworkCodeSwitcher>

## Multiple Files

Allow users to select multiple files by using the `multiple` attribute.

<FrameworkCodeSwitcher>
  <div slot="astro">
```astro
---
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";
---

<Dropzone multiple />
```
  </div>
  <div slot="react">
```tsx
import { Dropzone, DropzoneFilesList, DropzoneLoadingIndicator, DropzoneUploadIndicator } from "@/components/starwind/dropzone";

export function Example() {
  return (
    <>
      <Dropzone multiple />
    </>
  );
}
```
  </div>
  <div slot="vue">
```vue
<script setup lang="ts">
import {
  Dropzone,
  DropzoneFilesList,
  DropzoneLoadingIndicator,
  DropzoneUploadIndicator,
} from "@/components/starwind/dropzone";
</script>

<template>
  <Dropzone multiple />
</template>
```
  </div>
</FrameworkCodeSwitcher>

## API Reference
### Dropzone
- Inherits input attributes. Omits `disabled` and `type`.

### DropzoneUploadIndicator
- Inherits div attributes.

### DropzoneLoadingIndicator
- Inherits div attributes.

### DropzoneFilesList
- Inherits div attributes.
### Primitive And Runtime API
Behavior, state, events, form participation, and imperative methods are documented in the lower-level references.
- Primitive: [Dropzone Primitive](/docs/primitives/dropzone/)
- Runtime factory: [`createDropzone`](/docs/runtime/#create-dropzone) from `@starwind-ui/runtime/dropzone`

## Changelog

### v2.0.1

- Named the generated aggregate default export so React and Astro tooling can identify the installed component cleanly.

### v2.0.0

- Rebuilt Dropzone on Starwind Runtime for drag-and-drop, file input, and component state handling.
- See the [Dropzone Primitive](/docs/primitives/dropzone/) for the underlying unstyled anatomy and behavior API.