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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@rpldy/uploady

v1.8.0

Published

wrapper&context component to expose and provide react-uploady functionality

Downloads

40,673

Readme

Uploady

This is the main UI package. Its role is to initialize and expose the uploader functionality. It contains the Provider that all other UI packages rely on.

It provides multiple hooks that enable more advanced features and data for client apps.

The best place to get started is at our: React-Uploady Documentation Website

Installation

#Yarn: 
   $ yarn add @rpldy/uploady

#NPM:
   $ npm i @rpldy/uploady

Props

| Name (* = mandatory) | Type | Default | Description | |----------------------|------------------------------------------------------------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Uploader Options | | | | | autoUpload | boolean | true | automatically upload files when they are added | | destination | Destination | undefined | configure the end-point to upload to | | inputFieldName | string | "file" | name (attribute) of the file input field (requires sendWithFormData = true) | | grouped | boolean | false | group multiple files in a single request | | maxGroupSize | number | 5 | maximum of files to group together in a single request | | formatGroupParamName | (number, string) => string | undefined | determine the upload request field name when more than file is grouped in a single upload | | fileFilter | (File | string, index: number, File[] | string[]) => boolean | undefined | return false to exclude from batch | | method | string | "POST" | HTTP method in upload request | | params | Object | undefined | collection of params to pass along with the upload (requires sendWithFormData = true) | | forceJsonResponse | boolean | false | parse server response as JSON even if no JSON content-type header received | | withCredentials | boolean | false | set XHR withCredentials to true | | enhancer | UploaderEnhancer | undefined | uploader enhancer function | | concurrent | boolean | false | issue multiple upload requests simultaneously | | maxConcurrent | number | 2 | maximum allowed simultaneous requests | | send | SendMethod | @rpldy/sender | how to send files to the server | | sendWithFormData | boolean | true | upload is sent as part of formdata - when true, additional params can be sent along with uploaded data | | formatServerResponse | FormatServerResponseMethod | undefined | function to create the batch item's uploadResponse from the raw xhr response | | clearPendingOnAdd | boolean | false | whether to clear pending batch(es) when a new one is added | | isSuccessfulCall | IsSuccessfulCall | undefined | callback to use to decide whether upload response is succssful or not | | fastAbortThreshold | number | 100 | the pending/active item count threshold from which to start using the performant abort mechanism | | userData | any | undefined | metadata set by the user and isn't used by the upload process in any way, provided as a convenience to pass data around | | Uploady Options | | | | | debug | boolean | false | enable console logs from uploady packages | | listeners | Object | undefined | map of event name and event handler | | customInput | boolean | false | whether to use a custom file input (see: useFileInput | | inputFieldContainer | HTMLElement | document.body | html element to place the file input element inside | | children | React.Node | undefined | any part of your React app that will require access to the upload flow (components, hooks, etc.) | | capture | string | null | input/file#capture - affects file input only. for example, drag&drop or programmatic uploads will not be affected by this setting | | multiple | boolean | true | input/file#multiple - affects file input only. for example, drag&drop or programmatic uploads will not be affected by this setting | | accept | string | null | input/file#accept - affects file input only. for example, drag&drop or programmatic uploads will not be affected by this setting | | webkitdirectory | boolean | false | webkitdirectory - affects file input only. for example, drag&drop or programmatic uploads will not be affected by this setting | | fileInputId | string | undefined | the value to use for the internal file input element | | noPortal | boolean | false | Dont render Uploady's file input in a portal. (default: false) For SSR, noPortal = false causes a React warning in DEV. |

Example

To be able to use one of the UI Components or one of the hooks, you need to wrap them with Uploady. This will give them access to the UploadyContext.


import Uploady from "@rpldy/uploady";

const App = () => (<Uploady
    multiple
    grouped
    maxGroupSize={2}
    method="PUT"
    destination={{url: "https://my-server", headers: {"x-custom": "123"}}}>

    <RestOfMyApp/>
</Uploady>)

Context

When working in React, The UploadyContext is the API provider for the uploader mechanism. It wraps the uploader and exposes everything the app using it needs.

import React from "react";
import Uploady, { useUploady } from "@rpldy/uploady";

const MyComponent = () => { 
    const uploady = useUploady();

    const onClick = ()=> {
            uploady.showFileUpload();
        };

    return <button onClick={onClick}>Custom Upload Button</button>
};

const App = () => (<Uploady>
    <MyComponent/>
</Uploady>);

UploadyContext API

showFileUpload

(?UploadOptions) => void

Show the native file selection dialog. Optionally Pass options as a parameter to override options set as props on the Uploady component.

upload

(files: UploadInfo | UploadInfo[], addOptions: ?UploadOptions) => void

Upload file(s). Optionally Pass options as the second parameter to override options set as props on the Uploady component.

processPending

(uploadOptions?: UploadOptions) => void

Start uploading batches that were added with autoUpload = false

Upload Options can be added here to be (deep) merged with the options the batch(es) was added with.

clearPending

() => void

Remove all batches that were added with autoUpload = false, and were not uploaded yet.

setOptions

(UploadOptions) => void

Update the uploader instance with different options than the ones used to initialize

getOptions

() => UploadOptions

get the current options used by the uploader

getExtension

(name: any) => ?Object

get an extension registered by that name (through an enhancer)

abort

(id?: string) => void

abort all files being uploaded or a single item by its ID

abortBatch

(id: string) => void

abort a specific batch by its ID

on

(name: any, cb: EventCallback) => OffMethod

register for an event

once

(name: any, cb: EventCallback) => OffMethod

register once for an event

off

(name: any, cb?: EventCallback) => void

unregister an event handler

Hooks

Uploady provides hooks for all uploader events, as well as a few other useful ones.

useBatchAddListener (event hook)

Called when a new batch is added.

This event is cancellable

    import { useBatchAddListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useBatchAddListener((batch, options) => {
            console.log(`batch ${batch.id} was just added with ${batch.items.length} items`);  
        });

        //...    
    };

useBatchStartListener (event hook)

Called when batch items start uploading

This event is cancellable

This event can be scoped to a specific batch by passing the batch id as a second parameter

The callback function can be async or return a promise when needed

    import { useBatchStartListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useBatchStartListener((batch) => {
            console.log(`batch ${batch.id} started uploading`);  
        });

        //or scoped:
        useBatchStartListener((batch) => {
            console.log(`batch ${batch.id} started uploading`);  
        }, "b-123");
        //...    
    };

The callback passed to the hook may also return an object containing items and/or options in order to update the request dynamically, similar to useRequestPreSend only for the entire batch. See withBatchStartUpdate HOC below for more details.

useBatchProgressListener (event hook)

Called every time progress data is received from the upload request(s)

This event can be scoped to a specific batch by passing the batch id as a second parameter

    import { useBatchProgressListener } from "@rpldy/uploady";

   const MyComponent = () => {
        const batch = useBatchProgressListener((batch) => {});
    
        console.log(`batch ${batch.id} is ${batch.completed}% done and ${batch.loaded} bytes uploaded`)

       //...    
   };

Scoping to an id can be done like so:

    //...
    const { completed: batchCompleted } = useBatchProgressListener("batch-id") || { completed: 0 };
    //...

useBatchFinishListener (event hook)

Called when batch items finished uploading

This event can be scoped to a specific batch by passing the batch id as a second parameter

    import { useBatchFinishListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useBatchFinishListener((batch) => {
            console.log(`batch ${batch.id} finished uploading`);  
        });

        //...    
    };

useBatchCancelledListener (event hook)

Called in case batch was cancelled from BATCH_START event handler

This event can be scoped to a specific batch by passing the batch id as a second parameter

    import { useBatchCancelledListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useBatchCancelledListener((batch) => {
            console.log(`batch ${batch.id} was cancelled`);  
        });

        //...    
    };

useBatchAbortListener (event hook)

Called in case abortBatch was called

This event can be scoped to a specific batch by passing the batch id as a second parameter

    import { useBatchAbortListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useBatchAbortListener((batch) => {
            console.log(`batch ${batch.id} was aborted`);  
        });

        //...    
    };

useBatchErrorListener (event hook)

Called in case batch failed with an error. These errors will most likely occur due to invalid event handling. For instance, by a handler (ex: BATCH_START) throwing an error.

This event can be scoped to a specific batch by passing the batch id as a second parameter

    import { useBatchErrorListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useBatchErrorListener((batch) => {
            console.log(`batch ${batch.id} had an error: ${batch.additionalInfo}`);  
        });

        //...    
    };

useBatchFinalizeListener (event hook)

Called for batch when all its items have finished uploading or in case the batch was cancelled(abort) or had an error

This event can be scoped to a specific batch by passing the batch id as a second parameter

    import { useBatchFinalizeListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useBatchFinalizeListener((batch) => {
            console.log(`batch ${batch.id} finished uploading with status: ${batch.state}`);  
        });

        //...    
    };

useItemStartListener (event hook)

Called when item starts uploading (just before) For grouped uploads (multiple files in same xhr request) ITEM_START is triggered for each item separately

This event is cancellable

This event can be scoped to a specific item by passing the item id as a second parameter

The callback function can be async or return a promise when needed

    import { useItemStartListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useItemStartListener((item) => {
            console.log(`item ${item.id} started uploading`);  
        });
    
        //or scoped:
        useItemStartListener((item) => {
            console.log(`item ${item.id} started uploading`);  
        }, "i-123");

        //...    
    };

useItemFinishListener (event hook)

Called when item finished uploading

This event can be scoped to a specific item by passing the item id as a second parameter

    import { useItemFinishListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useItemFinishListener((item) => {
            console.log(`item ${item.id} finished uploading, response was: `, item.uploadResponse, item.uploadStatus);  
        });

        //...    
    };

useItemProgressListener (event hook)

Called every time progress data is received for this file upload

This event can be scoped to a specific item by passing the item id as a second parameter

    import { useItemProgressListener } from "@rpldy/uploady";

    const MyComponent = () => {
        const item = useItemProgressListener((item) => {
        	//callback is optional for this hook
        });
		
        console.log(`item ${item.id} is ${item.completed}% done and ${item.loaded} bytes uploaded`)
    
       //...    
    };

Scoping to an id can be done like so:

    //...
    const { completed: itemCompleted } = useItemProgressListener("item-id") || { completed: 0 };
    //...

useItemCancelListener (event hook)

Called in case item was cancelled from ITEM_START event handler

This event can be scoped to a specific item by passing the item id as a second parameter

    import { useItemCancelListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useItemCancelListener((item) => {
            console.log(`item ${item.id} was cancelled`);  
        });

        //...    
    };

useItemErrorListener (event hook)

Called in case item upload failed

This event can be scoped to a specific item by passing the item id as a second parameter

    import { useItemErrorListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useItemErrorListener((item) => {
            console.log(`item ${item.id} failed - `, item.uploadResponse);  
        });

        //...    
    };

useItemAbortListener (event hook)

Called in case abort was called for an item

This event can be scoped to a specific item by passing the item id as a second parameter

    import { useItemAbortListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useItemAbortListener((item) => {
            console.log(`item ${item.id} was aborted`);  
        });

        //...    
    };

useItemFinalizeListener (event hook)

Called for item when uploading is done due to: finished, error, cancel or abort

This event can be scoped to a specific item by passing the item id as a second parameter

    import { useItemFinalizeListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useItemFinalizeListener((item) => {
            console.log(`item ${item.id} is done with state: ${item.state}`);  
        });

        //...    
    };

useRequestPreSend (event hook)

Called before a group of items is going to be uploaded Group will contain a single item unless "grouped" option is set to true.

Handler receives the item(s) in the group and the upload options that were used. The handler can change data inside the items and in the options by returning different data than received. See simple example below or this more detailed guide.

This event is cancellable

    import { useRequestPreSend } from "@rpldy/uploady";

    const MyComponent = () => {
        useRequestPreSend(({ items, options }) => {        	
            let method = options.method;

            if (options.destination.url.startsWith("https://put-server")) {
                method = "PUT";
            }            

            return {
                options: { method } //will be merged with the rest of the options 
            };  
        });

        //...    
    };

useAllAbortListener (event hook)

Called in case abort was called for all running uploads

    import { useAllAbortListener } from "@rpldy/uploady";

    const MyComponent = () => {
        useAllAbortListener(() => {
            console.log("abort all was called");
        });
    };

useUploadyContext (alias: useUploady)

Shortcut hook to get the Uploady Context instance

Will throw in case used outside of Uploady render tree

    import { useUploady } from "@rpldy/uploady";

    const MyComponent = () => {
        const uploady = useUploady();
        
        const onClick = () => {
            uploady.showFileUpload();
        }

        //...       
    };

    const App = () => (
        <Uploady destination={{...}}>
            <MyComponent/>
        </Uploady>
    );

useUploadOptions

Shortcut hook to set/get upload options.

    import { useUploadOptions } from "@rpldy/uploady";

    const MyComponent = () => {
        const options = useUploadOptions({ grouped: true, maxGroupSize: 3 });
        
        //...       
    };

useAbortItem

Returns abort item method

    import { useAbortItem } from "@rpldy/uploady";
    
    const MyComponent = () => {
        const abortItem = useAbortItem();
        
        return <button onClick={() => abortItem("i-123")}>Abort Item</button>       
    };

useAbortBatch

Returns abort batch method

    import { useAbortBatch } from "@rpldy/uploady";
    
    const MyComponent = () => {
        const abortBatch = useAbortBatch();
        
        return <button onClick={() => abortBatch("b-123")}>Abort Batch</button>       
    };

useAbortAll

Returns abort all method

  import { useAbortAll } from "@rpldy/uploady";
  
  const MyComponent = () => {
      const abortAll = useAbortAll();
      
      return <button onClick={() => abortAll()}>Abort All</button>       
  };

useFileInput

When customInput prop is set to true, Uploady will not create its own file input element. In this case, Uploady will wait for a ref to an existing input.

The way you pass in your own input element is by using this hook.

In case Uploady wasn't provided with a destination prop or if it doesn't have a URL property, Uploady will check whether the input resides in a form. It will then use the form's action and method to set the upload endpoint and request method.

In case the form's attributes were used for the upload destination, updating the form's attributes dynamically won't affect the uploader configuration once it was set.

import Uploady, { useFileInput } from "@rpldy/uploady";
import UploadButton from "@rpldy/upload-button";

const MyForm = () => {
    const inputRef = useRef();
    useFileInput(inputRef);

    return <form action="/upload" method="POST">
        <input type="file" name="testFile" style={{ display: "none" }} ref={inputRef}/>
    </form>;
};

export const WithCustomFileInputAndForm = () => {
    return <section>
        <Uploady
            debug
            customInput
        >
            <MyForm />
            <UploadButton/>
        </Uploady>
    </section>
};

This hook can also be used to retrieve Uploady's internal file input. Calling the hook without parameters will return the ref.


const inputRef = useFileInput();

if (inputRef.current) {
    inputRef.current.setAttribute("webkitdirectory", "true");
}

NOTE! This isn't the recommended, or the 'Reacty' way to do things. It is still recommended to pass along a ref to an input that you render. In the future, accessing the internal input may have other consequences related to opting to interact with it directly instead of passing props to the Uploady component.

Check out the Custom Input guide for more details and examples.

HOCs

withRequestPreSendUpdate

HOC to enable components to interact with the upload data and options just-in-time before the request is sent. This is a hatch point to introduce custom logic that may affect the upload data.

A good example use-case for this is applying crop to selected image before it is uploaded.

When rendering the HOC's output, the id of the batch-item must be provided as a prop. This ensures the HOC only re-renders for a specific item and not for all. The id of the batch-item can be obtained from a hook (ex: useItemStartListener or useBatchStartListener)

    import React, { useState, useCallback } from "react";
    import Cropper from "react-easy-crop";
	import Uploady, { withRequestPreSendUpdate } from "@rpldy/uploady";
	import UploadButton from "@rpldy/upload-button";
    import cropImage from "./my-image-crop-code";

    const ItemCrop = withRequestPreSendUpdate((props) => {
        const [crop, setCrop] = useState({ x: 0, y: 0 });
        const [cropPixels, setCropPixels] = useState(null);
        
        const { url, updateRequest, requestData } = props;
         
        const onUploadCrop = useCallback(async() => {
            if (updateRequest && cropPixels) {
                //replace the file data with the cropped result
                requestData.items[0].file = await cropImage(url, requestData.items[0].file, cropPixels);
                //resume the upload flow with the updated file data			    
                updateRequest({ items: requestData.items });
            }
        }, [url, requestData, updateRequest, cropPixels]);
    
        const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => {
            setCropPixels(croppedAreaPixels);
        }, []);

        return <>            
            <Cropper
                image={url}
                crop={crop}
                onCropChange={setCrop}
                onCropComplete={onCropComplete}
            />           
            <button style={{ display: updateRequest && cropPixels ? "block" : "none" }}
                    onClick={onUploadCrop}>
                Upload Cropped
            </button>
        </>;
    });

    const MyApp = () => {
        return <Uploady destination={{ url: "my-server.com/upload" }}>
            <UploadButton />
            <ItemCrop id="batch-item-1" />
        </Uploady>
    }

See the Crop Guide for a full example.

withBatchStartUpdate

HOC to enable components to interact with the upload data and options of the batch just-in-time before the items are processed and requests are being sent.

This makes it possible to create a UI that will allow the user to interact and possible make changes to different or all items within the batch before a single request is made. For example: cropping multiple items prior to upload.

When rendering the HOC's output, the id of the batch must be provided as a prop. The id of the batch can be obtained from the useBatchAddListener

    import React, { useState, useCallback } from "react";
    import Cropper from "react-easy-crop";
    import Uploady, { withBatchStartUpdate } from "@rpldy/uploady";
    import UploadButton from "@rpldy/upload-button";
    import UploadPreview from "@rpldy/upload-preview";
    import cropImage from "./my-image-crop-code";
    
    const CropperForMultiCrop = ({ item, url, setCropForItem }) => {
        const [crop, setCrop] = useState({ x: 0, y: 0 });
        const [cropPixels, setCropPixels] = useState(null);
		
        const onSaveCrop = async () => {
            const cropped = await cropImage(url, item.file, cropPixels);
            setCropForItem(item.id, cropped);
        };

        const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => {
            setCropPixels(croppedAreaPixels);
        }, []);
		
        return (<div>
            <Cropper
                image={url}
                crop={crop}
                onCropChange={setCrop}
                onCropComplete={onCropComplete}
            />
            {cropPixels && 
                <Button onClick={onSaveCrop} id="save-crop-btn">Save Crop</Button>}
        </div>);
    };

    const BatchCrop = withBatchStartUpdate((props) => {
	    const { id, updateRequest, requestData } = props;
        const [cropped, setCropped] = useState({});
        const hasData = !!(id && requestData);
		
        const setCropForItem = (id, data) => {
            setCropped((cropped) => ({ ...cropped, [id]: data }));
        };
		
        const onUploadAll = () => {
            if (updateRequest) {
                const readyItems = requestData.items
                    .map((item) => {
                        item.file = cropped[item.id] || item.file;
                        return item;
                    });

				//update the items in the batch with the cropped files
                updateRequest({ items: readyItems });
            }
        };

        const getPreviewCompProps = useCallback((item) => {
            return ({
                onPreviewSelected: setSelected,
                isCroppedSet: cropped[item.id],
            });
        }, [cropped, setSelected]);

        return (<div>
            {hasData &&
                <button onClick={onUploadAll}>Upload All</button>}

            <UploadPreview
                rememberPreviousBatches
                PreviewComponent={ItemPreviewThumb}
                fallbackUrl="https://icon-library.net/images/image-placeholder-icon/image-placeholder-icon-6.jpg"
                previewComponentProps={getPreviewCompProps}
            />

            {selectedItem && hasData &&
                <CropperForMultiCrop
                    {...selected}
                    item={selectedItem}
                    setCropForItem={setCropForItem}
                />}
        </div>);
    });
	
    const MultiCropQueue = () => {
        const [currentBatch, setCurrentBatch] = useState(null);
    
        useBatchAddListener((batch) => setCurrentBatch(batch.id));
    
        return <BatchCrop id={currentBatch} />;
    };
	
    export const MyApp = () => {
        return <Uploady destination={{ url: "my-server.com/upload" }}>
            <UploadButton />
            <MultiCropQueue  />
        </Uploady>;
    };

See the Multi Crop Guide for a full example.

Contribute

Show Uploady your support by giving us a .

If you'd like to help Uploady grow & improve, take a look at the Contributing doc.

The Discussions page is a great place to ask questions, raise ideas and interact with Uploady maintainer, users and contributors.

Already using Uploady in Production? Let us know how & where in this open discussion.

Financial Contributors

Want to help sustain and grow Uploday? You can become a financial backer on OpenCollective.

Become a financial contributor and help us sustain our community.

You can make a one-time contribution or on a monthly basis