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

@get-set/gs-upload

v1.0.2

Published

Get-Set Upload

Readme

GSUpload

A dependency-free, feature-rich file upload / dropzone available in four usage modes from one codebase:

  • Native / vanilla JS — a window.GSUpload(selector | element, params) factory plus a window.GSUploadConfigue instance registry.
  • jQuery$(selector).GSUpload(params).
  • Prototypeelement.GSUpload(params) on any HTMLElement.
  • React — a <GSUpload /> component with an imperative ref handle and a React-only gsx scoped-style prop.

All four share the exact same engine (actions/, constants/, helpers/, types/), so behaviour is identical across every target.

Features

  • Drag-and-drop zone with a beautiful drag-over highlight (border + lift + glow), click-to-browse, and paste-to-upload (Ctrl/Cmd+V of files/images).
  • multiple selection, or single-file mode that replaces the previous file.
  • accept filtering by MIME (image/png), wildcard MIME (image/*) and extension (.pdf) — mixed lists welcome.
  • maxSize (per file), maxFiles, minFiles, and duplicate detection (dedupe).
  • Image thumbnail previews (object URLs, auto-revoked) with a graceful extension badge fallback for non-images.
  • Per-file progress bar with a smooth fill + shimmer animation, live percentage, and success/error states.
  • Auto-upload to a url (method / headers / fieldName / withCredentials / data) — or stage files and upload manually.
  • Remove and retry per file, with in-flight XMLHttpRequest abort on remove/clear/destroy.
  • Client-side validation with a structured onInvalid(file, reason, message) callback (accept / maxSize / maxFiles / duplicate).
  • Directory upload (webkitdirectory).
  • List layout: list or grid.
  • disabled state.
  • Theminglight / dark / auto (follows OS) plus a custom accentColor token.
  • RTL, size presets (sm / md / lg), and a React-only gsx prop for scoped per-instance styles.
  • Full keyboard + ARIA a11y — the dropzone is a focusable role="button" (Enter/Space open the picker), remove/retry buttons carry aria-labels.
  • Beautiful micro-interactions — add/remove pop, progress fill, drag-over lift — all respecting prefers-reduced-motion.
  • Callbacks: onAdd / onRemove / onProgress / onSuccess / onError / onComplete / onInvalid / onChange.

Installation

npm i @get-set/gs-upload

Compatibility

| Target | Requirement | |---|---| | React (the <GSUpload> component) | React 16.8+ (Hooks), and 17 / 18 / 19. React is an optional peer dependency — only needed for the component. | | Native / vanilla / jQuery / prototype (window.GSUpload) | No framework. Any modern evergreen browser. | | TypeScript | First-class — type declarations (.d.ts) ship in the package. | | SSR / Next.js | Safe to import server-side (no DOM access at module load). Render inside a Client Component ('use client'). |

Package entry points

From package.json:

| Field | Value | |---|---| | name | @get-set/gs-upload | | main | dist/components/GSUpload.js | | module | dist/components/GSUpload.js | | types | dist/components/GSUpload.d.ts | | exports['.'].import | ./dist/components/GSUpload.js (React/ESM) | | exports['.'].require | ./dist-js/bundle.js (native window bundle) | | files | dist, dist-js, styles, example.html |

Project layout

GSUpload.ts               # native entry  (webpack -> dist-js/bundle.js, window global)
components/GSUpload.tsx    # React component (tsc -> dist/, npm entry)
actions/                  # shared engine (init / engine / render / destroy / refresh)
constants/                # defaultParams + sizes
helpers/                  # validation, upload (XHR), files, format, icons, uihelpers
types/                    # Params / Ref / Window augmentation
components/styles/         # SCSS + compiled CSS + CSS-as-TS (runtime injection for React)
styles/                   # SCSS + compiled CSS (for <link> use by the native build)

Build

npm install
npm run build          # builds both targets
npm run build:js       # native bundle -> dist-js/bundle.js
npm run build:react    # React + types  -> dist/
npm test               # vitest

Vanilla / native usage

Include the bundle and the stylesheet, then call the factory. The factory accepts either a CSS selector string or a DOM element.

<link rel="stylesheet" href="node_modules/@get-set/gs-upload/styles/GSUpload.css" />
<script src="node_modules/@get-set/gs-upload/dist-js/bundle.js"></script>

<div id="dropzone"></div>
<!-- or enhance an existing file input -->
<input id="file-input" type="file" />

<script>
  // By selector string
  GSUpload('#dropzone', {
    reference: 'gallery',
    accept: 'image/*',
    maxFiles: 10,
    url: '/api/upload',
    label: 'Drop images here',
    hint: 'PNG, JPG, GIF up to 10 files',
    onSuccess: (file, response) => console.log('uploaded', file.name, response),
    onError: (file, err) => console.warn(err)
  });

  // …or by element (an input[type=file] is reused as the plugin input)
  var el = document.getElementById('file-input');
  GSUpload(el, { reference: 'single', multiple: false, accept: '.pdf' });
</script>

Instance registry

Every instance registers under its reference in window.GSUploadConfigue:

const inst = GSUploadConfigue.instance('gallery');
inst.upload();            // upload all staged files
inst.open();              // open the native file browser
inst.addFiles(fileList);  // add programmatically
inst.getFiles();          // snapshot of tracked files
inst.clear();             // remove everything
inst.destroy();           // teardown + restore host + unregister

jQuery usage

The bundle registers $.fn.GSUpload when jQuery is present. It initializes each matched element (passing the element, not the selector, to the factory):

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="node_modules/@get-set/gs-upload/dist-js/bundle.js"></script>

<div class="uploader"></div>

<script>
  $('.uploader').GSUpload({
    reference: 'jq',
    accept: 'image/*',
    url: '/api/upload'
  });

  // Reach the instance via the registry:
  $('#go').on('click', () => GSUploadConfigue.instance('jq').upload());
</script>

Prototype usage

The bundle also attaches HTMLElement.prototype.GSUpload, so any element can enhance itself:

<script src="node_modules/@get-set/gs-upload/dist-js/bundle.js"></script>
<input id="avatar" type="file" />

<script>
  document.getElementById('avatar').GSUpload({
    reference: 'avatar',
    multiple: false,
    accept: 'image/*',
    maxSize: 1024 * 1024
  });
</script>

React usage

'use client';
import { useRef } from 'react';
import GSUpload, { GSUploadHandle, GSUploadFile } from '@get-set/gs-upload';

export default function Demo() {
  const ref = useRef<GSUploadHandle>(null);

  return (
    <>
      <GSUpload
        ref={ref}
        reference="react-demo"
        accept="image/*"
        maxFiles={8}
        maxSize={5 * 1024 * 1024}
        layout="grid"
        theme="auto"
        accentColor="#8b5cf6"
        url="/api/upload"
        auto={false}
        label="Drop images here"
        hint="Up to 8 images, 5 MB each"
        onAdd={(added: GSUploadFile[]) => console.log('added', added)}
        onProgress={(f, p) => console.log(f.name, p)}
        onSuccess={(f, res) => console.log('done', f.name, res)}
        onComplete={(all) => console.log('batch complete', all)}
        // React-only: scoped per-instance styles (removed on unmount)
        gsx={{
          '.gs-upload-zone': { borderRadius: '20px' },
          '.gs-upload-icon': { color: '#8b5cf6' }
        }}
      />

      <button onClick={() => ref.current?.open()}>Browse…</button>
      <button onClick={() => ref.current?.upload()}>Upload all</button>
      <button onClick={() => ref.current?.clear()}>Clear</button>
    </>
  );
}

The GSUploadHandle (React ref)

| Method | Signature | Description | |---|---|---| | open | () => void | Open the native file browser dialog. | | addFiles | (files: File[] \| FileList) => void | Add File objects programmatically (bypasses the picker; runs validation + auto-upload). | | removeFile | (id: string) => void | Remove a tracked file by id (aborts its upload). | | clear | () => void | Remove every file (aborts all in-flight uploads). | | upload | (id?: string) => void | Upload all pending/failed files, or just one by id. | | retry | (id: string) => void | Retry a failed / aborted file's upload. | | getFiles | () => GSUploadFile[] | Snapshot of the tracked files. | | isComplete | () => boolean | true when every non-invalid file uploaded successfully. | | destroy | () => void | Abort uploads + revoke previews for this instance. |

Native instance methods (Ref / registry)

The native factory returns — and GSUploadConfigue.instance(ref) retrieves — an object exposing the same imperative surface, plus refresh():

open(), addFiles(files), removeFile(id), clear(), upload(id?), retry(id), getFiles(), isComplete(), refresh(), destroy().


Props / options

Every option below works identically across native params and React props (React additionally accepts gsx).

| Prop | Type | Default | Description | |---|---|---|---| | reference | string | auto GUID | Unique key used in the registry. | | multiple | boolean | true | Allow selecting more than one file. | | accept | string \| string[] | — | Accepted types: MIME, image/*, and/or .ext. | | maxSize | number \| false | false | Max size per file in bytes. | | maxFiles | number \| false | false | Max number of files kept. | | minFiles | number | 0 | Minimum files required for upload() to proceed. | | dedupe | boolean | true | Reject a file whose name+size already exists. | | directory | boolean | false | Allow whole-directory selection (webkitdirectory). | | paste | boolean | true | Enable paste-to-upload. | | clickable | boolean | true | Enable click-to-browse on the dropzone. | | dragDrop | boolean | true | Enable drag-and-drop. | | auto | boolean | true | Auto-upload each file on add (requires url). | | url | string | — | Upload endpoint. | | method | 'POST' \| 'PUT' \| 'PATCH' | 'POST' | HTTP method. | | headers | Record<string,string> | — | Extra request headers. | | fieldName | string | 'file' | Multipart form field name for the file. | | withCredentials | boolean | false | Send cookies / auth with the request. | | data | Record<string,string> | — | Extra multipart form fields. | | label | string | 'Drag & drop files here' | Big dropzone label. | | hint | string | — | Secondary hint line. | | buttonText | string | 'browse' | Browse link text. | | preview | boolean | true | Show image thumbnail previews. | | progressBar | boolean | true | Show the per-file progress bar. | | layout | 'list' \| 'grid' | 'list' | File list layout. | | showList | boolean | true | Render the file list. | | removable | boolean | true | Show the per-file remove button. | | size | 'sm' \| 'md' \| 'lg' | 'md' | Dropzone size preset. | | disabled | boolean | false | Disable all interaction. | | reducedMotion | 'auto' \| boolean | 'auto' | Honor prefers-reduced-motion. | | theme | 'light' \| 'dark' \| 'auto' | 'auto' | Visual theme. | | accentColor | string | #2563eb | Accent color (--gsu-accent). | | rtl | boolean | false | Right-to-left layout. | | className | string | — | Extra class on the root. | | gsx | NestedCSS | — | React-only scoped per-instance styles. | | onAdd | (added, all) => void | — | File(s) added (valid + invalid). | | onRemove | (removed, all) => void | — | A file was removed. | | onProgress | (file, percent) => void | — | Upload progress 0–100. | | onSuccess | (file, response) => void | — | A file finished uploading. | | onError | (file, error) => void | — | A file upload errored. | | onComplete | (all) => void | — | The batch settled. | | onInvalid | (file, reason, message) => void | — | A file failed validation. | | onChange | (all) => void | — | Any change to the file set. |

The GSUploadFile object

Callbacks and getFiles() return tracked files shaped like:

interface GSUploadFile {
  id: string;                 // stable per-instance id
  file: File;                 // the underlying browser File
  name: string;
  size: number;
  type: string;
  status: 'pending' | 'uploading' | 'success' | 'error' | 'invalid' | 'aborted';
  progress: number;           // 0–100
  previewUrl?: string;        // object URL for image previews
  error?: string;             // validation / upload error message
  invalidReason?: 'accept' | 'maxSize' | 'maxFiles' | 'duplicate';
  response?: unknown;         // server response (JSON or text) on success
}

Catalogs

Layouts

| layout | Behaviour | |---|---| | list | Vertical rows: thumbnail + name/meta/progress + controls. | | grid | Responsive card grid with large thumbnails — great for galleries. |

Themes

| theme | Behaviour | |---|---| | light | Light surfaces. | | dark | Dark surfaces (slate palette). | | auto | Follows prefers-color-scheme. |

Sizes

| size | Dropzone padding | |---|---| | sm | Compact. | | md | Default. | | lg | Spacious. |

Validation reasons (onInvalid)

| reason | Cause | |---|---| | accept | File type/extension not in accept. | | maxSize | File larger than maxSize. | | maxFiles | Adding would exceed maxFiles. | | duplicate | Same name+size already added (dedupe). |


Accessibility

  • The dropzone is a focusable role="button" (tabindex=0); Enter / Space open the file picker.
  • Focus ring uses the accent color via :focus-visible.
  • Remove / retry buttons carry descriptive aria-labels; status icons are aria-hidden.
  • The loader / list regions announce politely.
  • All motion respects prefers-reduced-motion (and the reducedMotion prop).

Styling

Import the CSS once (native <link> or the React component injects it automatically):

<link rel="stylesheet" href="@get-set/gs-upload/styles/GSUpload.css" />

Override the CSS custom properties to theme it:

.gs-upload {
  --gsu-accent: #8b5cf6;
  --gsu-radius: 20px;
}

Or, in React, use the scoped gsx prop (auto-removed on unmount).

License

ISC © Get-Set