Skip to main content


The @uppy/xhr-upload plugin is for regular uploads to a HTTP server.

When should I use it?


Not sure which uploader is best for you? Read “Choosing the uploader you need”.

When you have an existing HTTP server and you don’t need Transloadit services or want to run a tus server. Note that it’s still possible to use tus without running an extra server by integrating tus into your existing one. For instance, if you have a Node.js server (or server-side framework like Next.js) you could integrate tus-node-server.


npm install @uppy/xhr-upload


A quick overview of the complete API.

import Uppy from '@uppy/core';
import Dashboard from '@uppy/dashboard';
import XHR from '@uppy/xhr-upload';

import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';

new Uppy()
.use(Dashboard, { inline: true, target: 'body' })
.use(XHR, { endpoint: '' });




A unique identifier for this plugin (string, default: 'XHRUpload').


URL of the HTTP server (string, default: null).


Configures which HTTP method to use for the upload (string, default: 'post').


Configures whether to use a multipart form upload, using FormData (boolean, default: true).

This works similarly to using a <form> element with an <input type="file"> for uploads. When set to true, file metadata is also sent to the endpoint as separate form fields. When set to false, only the file contents are sent.


When formData is set to true, this is used as the form field name for the file to be uploaded.

It defaults to 'files[]' if bundle option is set to true, otherwise it defaults to 'file'.


Pass an array of field names to limit the metadata fields that will be added to upload.

  • Set this to an empty array [] to not send any fields.
  • Set this to ['name'] to only send the name field.
  • Set this to null (the default) to send all metadata fields.

If the formData option is set to false, metaFields is ignored.


An object containing HTTP headers to use for the upload request. Keys are header names, values are header values.

const headers = {
authorization: `Bearer ${window.getCurrentUserToken()}`,

Header values can also be derived from file data by providing a function. The function receives an Uppy file and must return an object where the keys are header names, and values are header values.

const headers = (file) => {
return {
authorization: `Bearer ${window.getCurrentUserToken()}`,
expires: file.meta.expires,

The function syntax is not available when bundle is set to true.


Send all files in a single multipart request (boolean, default: false).

All files will be appended to the provided fieldName field in the request.


When bundle is set to true:

  • formData must also be set to true.
  • Uppy won’t be able to bundle remote files (such as Google Drive) and will throw an error in this case.
  • Only global uppy metadata is sent to the endpoint. Individual per-file metadata is ignored.

To upload files on different fields, use uppy.setFileState() to set the xhrUpload.fieldName property on the file:

uppy.setFileState(fileID, {
xhrUpload: { fieldName: 'pic0' },


Check if the response was successful (function, default: (status, responseText, response) => boolean).

  • By default, responses with a 2xx HTTP status code are considered successful.
  • When true, getResponseData() will be called and the upload will be marked as successful.
  • When false, both getResponseData() and getResponseError() will be called and the upload will be marked as unsuccessful.
  • The statusCode is the numeric HTTP status code returned by the endpoint.
  • The responseText is the XHR endpoint response as a string.
  • response is the XMLHttpRequest object.

This option is only used for local uploads. Uploads from remote providers like Google Drive or Instagram do not support this and will always use the default.


Extract the response data from the successful upload (function, default: (responseText, response) => void).

  • responseText is the XHR endpoint response as a string.
  • response is the XMLHttpRequest object.

JSON is handled automatically, so you should only use this if the endpoint responds with a different format. For example, an endpoint that responds with an XML document:

function getResponseData(responseText, response) {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(responseText, 'text/xml');
return {
url: xmlDoc.querySelector('Location').textContent,

This response data will be available on the file’s .response property and will be emitted in the upload-success event.


When uploading files from remote providers such as Dropbox or Instagram, Companion sends upload response data to the client. This is made available in the getResponseData() function as well. The response object from Companion has some properties named after their XMLHttpRequest counterparts.


Extract the error from the failed upload (function, default: (responseText, response) => void).

For example, if the endpoint responds with a JSON object containing a { message } property, this would show that message to the user:

function getResponseError(responseText, response) {
return new Error(JSON.parse(responseText).message);


The field name containing the location of the uploaded file (string, default: 'url').

This is returned by getResponseData().

timeout: 30 * 1000

Abort the connection if no upload progress events have been received for this milliseconds amount (number, default: 30_000).

Note that unlike the XMLHttpRequest.timeout property, this is a timer between progress events: the total upload can take longer than this value. Set to 0 to disable this check.


The maximum amount of files to upload in parallel (number, default: 5).


The response type expected from the server, determining how the xhr.response property should be filled (string, default: 'text').

The xhr.response property can be accessed in a custom getResponseData() callback. This option sets the XMLHttpRequest.responseType property. Only '', 'text', 'arraybuffer', 'blob' and 'document' are widely supported by browsers, so it’s recommended to use one of those.


Indicates whether cross-site Access-Control requests should be made using credentials (boolean, default: false).

locale: {}

export default {
strings: {
// Shown in the Informer if an upload is being canceled because it stalled for too long.
timedOut: 'Upload stalled for %{seconds} seconds, aborting.',

Frequently Asked Questions

How to send along meta data with the upload?

When using XHRUpload with formData: true, file metadata is sent along with each upload request. You can set metadata for a file using uppy.setFileMeta(fileID, data), or for all files simultaneously using uppy.setMeta(data).

It may be useful to set metadata depending on some file properties, such as the size. You can use the file-added event and the uppy.setFileMeta(fileID, data) method to do this:

uppy.on('file-added', (file) => {
uppy.setFileMeta(, {
size: file.size,

Now, a form field named size will be sent along to the endpoint once the upload starts.

By default, all metadata is sent, including Uppy’s default name and type metadata. If you do not want the name and type metadata properties to be sent to your upload endpoint, you can use the metaFields option to restrict the field names that should be sent.

uppy.use(XHRUpload, {
// Only send our own `size` metadata field.
allowedMetaFields: ['size'],

How to upload to a PHP server?

The XHRUpload plugin works similarly to a <form> upload. You can use the $_FILES variable on the server to work with uploaded files. See the PHP documentation on Handling file uploads.

The default form field for file uploads is files[], which means you have to access the $_FILES array as described in Uploading many files:

// upload.php
$files = $_FILES['files'];
$file_path = $files['tmp_name'][0]; // temporary upload path of the first file
$file_name = $_POST['name']; // desired name of the file
move_uploaded_file($file_path, './img/' . basename($file_name)); // save the file in `img/`

Note how we are using $_POST['name'] instead of $my_file['name']. $my_file['name'] has the original name of the file on the user’s device. $_POST['name'] has the name metadata value for the uploaded file, which can be edited by the user using the Dashboard.

Set a custom fieldName to make working with the $_FILES array a bit less convoluted:

// app.js
uppy.use(XHRUpload, {
endpoint: '/upload.php',
fieldName: 'my_file',
// upload.php
$my_file = $_FILES['my_file'];
$file_path = $my_file['tmp_name']; // temporary upload path of the file
$file_name = $_POST['name']; // desired name of the file
move_uploaded_file($file_path, $_SERVER['DOCUMENT_ROOT'] . '/img/' . basename($file_name)); // save the file at `img/FILE_NAME`