Skip to content
Devix Open Source

Guide

React, Vue, Svelte and a plain page

Every wrapper is a thin layer over the same DOM core, so all of them behave identically.

React

import { FileUploader } from '@devix-labs/file-uploader/react';
import '@devix-labs/file-uploader/styles.css';

function Attachments() {
  return (
    <FileUploader
      url="/upload"
      accept="image/*,.pdf"
      maxSize="5MB"
      maxFiles={10}
      name="docs[]"
      onSuccess={(file, response) => save(JSON.parse(response))}
      onComplete={(files) => setDone(files.length)}
    />
  );
}

Anything that is not an option — name, className, required, aria-* — goes straight to the input. A ref gives you upload(), clear(), files() and instance.

Note that the uploader's onError and onProgress are the uploader's, not React's DOM events of those names.

Vue

<script setup>
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { fileUploader } from '@devix-labs/file-uploader/vue';
import '@devix-labs/file-uploader/styles.css';

const host = ref(null);
const emit = defineEmits(['uploaded']);
let uploader;

onMounted(() => {
  uploader = fileUploader(host.value, {
    url: '/upload',
    maxSize: '5MB',
    onSuccess: (file, response) => emit('uploaded', JSON.parse(response)),
  });
});
onBeforeUnmount(() => uploader?.destroy());
</script>

<template>
  <input ref="host" type="file" name="docs[]">
</template>

Svelte

<script>
  import { uploader } from '@devix-labs/file-uploader/svelte';
  import '@devix-labs/file-uploader/styles.css';
</script>

<input type="file" name="docs[]" use:uploader={{ url: '/upload', maxSize: '5MB' }}>

A web component

<script type="module" src="https://devix.pk/cdn/oss/file-uploader@1.0.0/element.js"></script>

<dx-uploader name="docs[]" url="/upload" accept="image/*" max-size="5MB" max-files="10"></dx-uploader>

Attributes: name, url, accept, max-size, max-files, chunk-size, field-name. It exposes .files and .uploader, and fires upload and complete.

A plain page, from the CDN

<link rel="stylesheet" href="https://devix.pk/cdn/oss/file-uploader@1.0.0/file-uploader.min.css">
<script src="https://devix.pk/cdn/oss/file-uploader@1.0.0/file-uploader.min.js"></script>
<script>
  DevixFileUploader.createFileUploader(document.querySelector('#docs'), { url: '/upload' });
</script>

Laravel and Livewire

Because the real input is kept and the files stay in it, the plain case needs no JavaScript wiring at all:

<form method="post" action="{{ route('docs.store') }}" enctype="multipart/form-data">
  @csrf
  <input id="docs" name="docs[]" type="file">
  <button>Send</button>
</form>

<script type="module">
  import { createFileUploader } from '/cdn/oss/file-uploader@1.0.0/index.js';
  createFileUploader(document.querySelector('#docs'), { accept: 'image/*,.pdf', maxSize: '5MB' });
</script>
$request->validate(['docs.*' => ['file', 'max:5120']]);
foreach ($request->file('docs', []) as $file) {
    $file->store('attachments');
}

To upload as the files are chosen instead, give it a url and a CSRF header:

createFileUploader(input, {
  url: '{{ route('uploads.store') }}',
  headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]').content },
  onSuccess: (file, response) => attach(JSON.parse(response).path),
});

Server rendering

Nothing touches window at import time, so importing the package on a server is safe in Next.js, Nuxt, SvelteKit and Astro. The widget is built when the component mounts.

Updated 15 Sep 2026