Tusflow

Tusflow UI

A powerful file upload interface built on top of Uppy, providing a seamless and customizable upload experience for Next.js applications.

We are currently working on a new upload UI component using shadcn/ui. Stay tuned for updates!

This installation guide is specifically for Next.js applications. For other frameworks like Angular, Vue, or Svelte, please refer to the official Uppy documentation.

Installation

First, install the required Uppy packages. You can use your preferred package manager:

npm install @uppy/core @uppy/dashboard @uppy/react @uppy/tus @uppy/file-input @uppy/drag-drop @uppy/progress-bar sonner

Setting up the Provider

Create a new file called UppyProvider.tsx in your components directory:

'use client';
 
import React, { createContext, useContext, useEffect, useState } from 'react';
import Uppy, { UppyFile, type Meta } from '@uppy/core';
import Tus, { TusBody } from '@uppy/tus';
import { toast } from 'sonner';
 
const MIN_CHUNK_SIZE = 5 * 1024 * 1024; // 5MB (S3 minimum)
const UppyContext = createContext<Uppy<Meta, TusBody> | null>(null);
 
export const useUppy = () => {
  const context = useContext(UppyContext);
  if (!context) {
    throw new Error('useUppy must be used within an UppyProvider');
    toast.error('useUppy must be used within an UppyProvider')
  }
  return context;
};
 
export const UppyProvider: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  const [uppy] = useState(() =>
    new Uppy({
      autoProceed: false,
       restrictions: {
        maxFileSize: 8 * 1024 * 1024,
        minFileSize: 1000000,
        maxNumberOfFiles: 5,
      },
      debug: true,
      onBeforeUpload: (files: { [key: string]: UppyFile<Meta, TusBody> }) => {
        const file = Object.values(files)[0];
        if (file?.size && file.size > 8 * 1024 * 1024) {
          toast.error('File size exceeds 8MB limit');
          return false;
        }
        return true;
      },
    }).use(Tus, {
      endpoint: process.env.NEXT_PUBLIC_WORKERS_ENDPOINT,
      limit: 5,
      chunkSize: MIN_CHUNK_SIZE,
      withCredentials: true,
      overridePatchMethod: false,
      removeFingerprintOnSuccess: true,
      retryDelays: [0, 1000, 3000, 5000],
      // use this if you have set authentication
      onBeforeRequest: (req, file) => {
        req.setHeader(
          'Authorization',
          `Bearer ${process.env.NEXT_PUBLIC_WORKERS_API_KEY}`
        );
      },
    })
  );
 
  useEffect(() => {
    uppy.on('file-added', (file) => {
      toast.success(`File ${file.meta.name} added successfully`);
    });
 
    uppy.on('upload-success', (file) => {
      toast.success(`Successfully uploaded ${file?.meta.name}!`);
    });
 
    uppy.on('upload-error', (file, error) => {
      toast.error(`Failed to upload ${file?.meta.name}: ${error.message}`);
    });
 
    return () => {
      uppy.clear()
    };
  }, [uppy]);
 
  return <UppyContext.Provider value={uppy}>{children}</UppyContext.Provider>;
};

Creating the File Uploader Component

Create a new file called file-uploader.tsx in your components directory:

'use client';
 
import React from 'react';
import { useTheme } from 'next-themes';
import '@uppy/core/dist/style.min.css';
import '@uppy/dashboard/dist/style.min.css';
import { Dashboard } from '@uppy/react';
import { useUppy } from './UppyProvider';
 
export function FileUploader() {
  const uppy = useUppy();
  const { resolvedTheme } = useTheme();
 
  return (
    <div className="flex flex-col w-full max-w-4xl mx-auto">
      <div className="uppy-wrapper rounded-xl border dark:border-gray-800 bg-card">
        <Dashboard
          uppy={uppy}
          theme={resolvedTheme === 'dark' ? 'dark' : 'light'}
          width="100%"
          height="450px"
          showProgressDetails={true}
          note="Upload any file type, up to 5GB"
          proudlyDisplayPoweredByUppy={false}
        />
      </div>
    </div>
  );
}

Adding the Provider to Your Layout

Update your app/layout.tsx to include the UppyProvider:

import { UppyProvider } from '@/components/UppyProvider';
import { Toaster } from 'sonner';
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <UppyProvider>
          {children}
          <Toaster />
        </UppyProvider>
      </body>
    </html>
  );
}

Usage

You can now use the FileUploader component in any of your pages:

import { FileUploader } from '@/components/file-uploader';
 
export default function UploadPage() {
  return (
    <div>
      <h1>File Upload</h1>
      <FileUploader />
    </div>
  );
}

[!important] Make sure to replace WORKERS_API_ENDPOINT in the UppyProvider with your actual tus server endpoint.

Styling

The Uppy Dashboard comes with its own styles. Make sure you've imported them in your component:

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

You can customize the appearance further using Tailwind CSS classes and the theme prop on the Dashboard component.

Edit on GitHub

Last updated on

On this page