npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-dropkit

v0.2.0

Published

A modern, accessible, TypeScript-first React file upload library with drag-and-drop, previews, progress tracking, and zero backend opinions.

Downloads

472

Readme

react-dropkit

A modern, accessible, TypeScript-first React file upload library.
Drag-and-drop, image previews, progress tracking — with zero backend opinions.

npm version npm downloads CI License: MIT

📖 Documentation · ✦ Live Demo


Why react-dropkit?

  • TypeScript-first — full types out of the box, no @types/ package needed
  • Accessible by default — ARIA roles, keyboard navigation, screen reader support
  • ESM + CJS — works with Next.js, Vite, Remix, and any modern bundler
  • Zero backend opinions — plug in your own upload logic (S3, Cloudinary, custom API)
  • Image previews built-in — thumbnails for images, file icons for everything else
  • Per-file progress tracking — animated progress bar per file with status updates
  • Headless or styled — use the ready-made UI or bring your own with useFileUpload
  • Manual or auto upload — choose whether files upload immediately or wait for a button click
  • Fully customisable — style any part of the component with className props
  • Tiny bundle — zero dependencies beyond React

Installation

npm install react-dropkit
# or
yarn add react-dropkit
# or
pnpm add react-dropkit

Peer dependencies: react >= 17, react-dom >= 17


Quick Start

import { Dropzone } from 'react-dropkit'

export default function App() {
  return (
    <Dropzone
      validation={{ accept: ['image/*'], maxSize: 5 * 1024 * 1024 }}
      onFilesAdded={(files) => console.log('Added:', files)}
    />
  )
}

Usage Examples

Basic drop zone

import { Dropzone } from 'react-dropkit'

<Dropzone />

With validation

<Dropzone
  validation={{
    accept: ['image/*', '.pdf'],   // images and PDFs only
    maxSize: 10 * 1024 * 1024,     // 10MB max
    minSize: 1024,                 // 1KB min
    maxFiles: 5,                   // max 5 files
  }}
  multiple={true}
/>

With upload handler (auto upload)

Files upload automatically the moment they are added:

<Dropzone
  onUpload={async (file) => {
    const formData = new FormData()
    formData.append('file', file.file)
    await fetch('/api/upload', { method: 'POST', body: formData })
  }}
/>

Manual upload with buttons

Files sit in idle state until the user clicks Upload:

<Dropzone
  autoUpload={false}
  showUploadButton
  showClearButton
  uploadButtonLabel="Send files"
  clearButtonLabel="Remove all"
  onUpload={async (file) => {
    const formData = new FormData()
    formData.append('file', file.file)
    await fetch('/api/upload', { method: 'POST', body: formData })
  }}
/>

Custom styling

Style the inner drop zone and file list independently:

<Dropzone
  className="my-wrapper"
  dropzoneClassName="my-drop-area"
  fileListClassName="my-file-list"
/>

Single file upload

<Dropzone multiple={false} />

Custom drop zone UI

Pass children to replace the default UI while keeping all the logic:

<Dropzone>
  <div style={{ padding: 40, textAlign: 'center' }}>
    <p>📂 Drop your files here</p>
  </div>
</Dropzone>

Use in a form

import { useState } from 'react'
import { useFileUpload } from 'react-dropkit'

export function ProfileForm() {
  const [name, setName] = useState('')
  const { files, getRootProps, getInputProps, inputRef } = useFileUpload({
    multiple: false,
    validation: { accept: ['image/*'] },
  })

  const handleSubmit = async (e) => {
    e.preventDefault()
    const formData = new FormData()
    formData.append('name', name)
    if (files[0]) formData.append('avatar', files[0].file)
    await fetch('/api/profile', { method: 'POST', body: formData })
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={name} onChange={e => setName(e.target.value)} />
      <div {...getRootProps()}>
        <input {...getInputProps()} ref={inputRef} />
        <p>Drop avatar here</p>
      </div>
      <button type="submit">Save</button>
    </form>
  )
}

Headless Usage — useFileUpload

For full control over the UI, use the hook directly:

import { useFileUpload, FileList } from 'react-dropkit'

export function MyUploader() {
  const {
    files,
    isDragActive,
    isDragReject,
    getRootProps,
    getInputProps,
    inputRef,
    removeFile,
    uploadAll,
    clearFiles,
  } = useFileUpload({
    multiple: true,
    validation: {
      accept: ['image/*'],
      maxSize: 5 * 1024 * 1024,
    },
    onUpload: async (file) => {
      // your upload logic
    },
    onError: (message) => {
      console.error(message)
    },
  })

  return (
    <div>
      <div
        {...getRootProps()}
        style={{
          border: `2px dashed ${isDragReject ? 'red' : isDragActive ? 'blue' : 'gray'}`,
          padding: 32,
          borderRadius: 8,
          cursor: 'pointer',
        }}
      >
        <input {...getInputProps()} ref={inputRef} />
        <p>{isDragActive ? 'Drop here!' : 'Drag files or click to browse'}</p>
      </div>

      <FileList files={files} onRemove={removeFile} />

      {files.length > 0 && (
        <div>
          <button onClick={uploadAll}>Upload All</button>
          <button onClick={clearFiles}>Clear</button>
        </div>
      )}
    </div>
  )
}

API Reference

<Dropzone /> Props

| Prop | Type | Default | Description | |---|---|---|---| | validation | ValidationOptions | {} | File type, size, and count rules | | multiple | boolean | true | Allow multiple files | | disabled | boolean | false | Disable the drop zone | | autoUpload | boolean | true | Upload files immediately on add. Set to false for manual upload | | showUploadButton | boolean | false | Show a built-in Upload All button | | showClearButton | boolean | false | Show a built-in Clear All button | | uploadButtonLabel | string | "Upload All" | Custom label for the upload button | | clearButtonLabel | string | "Clear All" | Custom label for the clear button | | onFilesAdded | (files: UploadedFile[]) => void | — | Called when new files pass validation | | onFilesChange | (files: UploadedFile[]) => void | — | Called on every change — add, remove, clear | | onFileRemoved | (id: string) => void | — | Called when a file is removed | | onUpload | (file: UploadedFile) => Promise<void> | — | Your upload handler per file | | onError | (error: string) => void | — | Called on validation failure or upload error | | className | string | "" | Class on the outer wrapper | | dropzoneClassName | string | "" | Class on the inner drop zone area | | fileListClassName | string | "" | Class on the file list | | children | ReactNode | — | Override the default drop zone UI |

<FileList /> Props

| Prop | Type | Description | |---|---|---| | files | UploadedFile[] | Array of files to display | | onRemove | (id: string) => void | Called when remove is clicked | | className | string | Custom class |

<FilePreview /> Props

| Prop | Type | Description | |---|---|---| | file | UploadedFile | Single file to display | | onRemove | (id: string) => void | Called when remove is clicked | | className | string | Custom class |

<ProgressBar /> Props

| Prop | Type | Description | |---|---|---| | progress | number | 0–100 | | status | FileStatus | idle, uploading, success, error | | className | string | Custom class |

useFileUpload() Options

| Option | Type | Default | Description | |---|---|---|---| | validation | ValidationOptions | {} | File validation rules | | multiple | boolean | true | Allow multiple files | | autoUpload | boolean | true | Auto upload on file add | | onUpload | (file: UploadedFile) => Promise<void> | — | Upload handler | | onFilesAdded | (files: UploadedFile[]) => void | — | Called when files are added | | onFilesChange | (files: UploadedFile[]) => void | — | Called on every state change | | onFileRemoved | (id: string) => void | — | Called when a file is removed | | onError | (error: string) => void | — | Called on error |

useFileUpload() Returns

| Value | Type | Description | |---|---|---| | files | UploadedFile[] | Current files with status, progress, preview | | isDragActive | boolean | True when dragging over the drop zone | | isDragReject | boolean | True when dragged files don't match accepted types | | getRootProps() | HTMLAttributes | Spread onto your drop zone container | | getInputProps() | InputHTMLAttributes | Spread onto a hidden input element | | inputRef | RefObject<HTMLInputElement> | Attach to input so clicking opens file browser | | addFiles(files) | (File[]) => void | Programmatically add files | | removeFile(id) | (string) => void | Remove a file by ID | | clearFiles() | () => void | Remove all files | | uploadFile(id) | (string) => Promise<void> | Upload a single file by ID | | uploadAll() | () => Promise<void> | Upload all idle or failed files | | openFileDialog() | () => void | Programmatically open the file browser |


Validation Options

interface ValidationOptions {
  accept?: string[]    // ['image/*', '.pdf', 'video/mp4']
  maxSize?: number     // bytes — e.g. 5 * 1024 * 1024 for 5MB
  minSize?: number     // bytes
  maxFiles?: number    // max total files allowed
}

Accept supports:

  • Wildcard MIME types: image/*, video/*
  • Exact MIME types: image/png, application/pdf
  • File extensions: .pdf, .docx, .csv

Types

type FileStatus = 'idle' | 'uploading' | 'success' | 'error'

interface UploadedFile {
  id: string
  file: File
  preview?: string     // blob URL for images, undefined for others
  progress: number     // 0–100
  status: FileStatus
  error?: string
}

Utility Functions

import {
  formatFileSize,      // formatFileSize(1024) → "1 KB"
  isFileAccepted,      // checks MIME type or extension
  isFileSizeValid,     // checks against max/min size
  isImageFile,         // true if file.type starts with "image/"
  getFileExtension,    // getFileExtension("doc.pdf") → "PDF"
  createPreview,       // creates blob URL for images
  revokePreview,       // cleans up blob URLs
} from 'react-dropkit'

Next.js Support

react-dropkit is fully compatible with Next.js App Router and Pages Router.

For App Router, mark your component as a client component:

'use client'

import { Dropzone } from 'react-dropkit'

Changelog

v0.2.0

  • Added autoUpload prop — toggle between auto and manual upload mode
  • Added showUploadButton and showClearButton props — built-in action buttons on <Dropzone />
  • Added uploadButtonLabel and clearButtonLabel — custom button text
  • Added dropzoneClassName — style the inner drop zone area independently
  • Added fileListClassName — style the file list independently

v0.1.0

  • Initial release

Contributing

Contributions are welcome! Please open an issue first to discuss what you'd like to change.

git clone https://github.com/abideen-program/react-dropkit
cd react-dropkit
npm install
npm test
npm run dev

License

MIT © Abideen · Documentation · npm · GitHub