NOCTA

File Upload

Advanced file upload component with drag & drop, progress tracking, validation, and preview support

Installation

Install the File Upload component using the nocta-ui CLI:

Terminal
npx nocta-ui add file-upload

Then import the component:

Import
import { FileUpload } from '@/components/ui/file-upload';

Basic Usage

Basic File Upload

Basic File Upload

Click to upload or drag and drop

Accepted types: image/*,.pdf,.doc,.docx

Max size: 5 MB

Max files: 5

Single File Upload

Single File Upload

Click to upload a document

Accepted types: .pdf,.doc,.docx

Max size: 10 MB

Image Upload with Preview

Image Upload with Preview

Click to upload images or drag and drop

Accepted types: image/*

Max size: 2 MB

Max files: 10

Sizes

Three size options are available: sm, md, and lg. The default size is md.

Small Size

Click to upload or drag and drop

Medium Size (Default)

Click to upload or drag and drop

Large Size

Click to upload or drag and drop

Component States

Disabled State

Disabled State

Upload is disabled

File Types & Validation

File Type Restrictions

File Type Examples
// Images only
<FileUpload accept="image/*" />

// Specific image types
<FileUpload accept="image/jpeg,image/png,image/gif" />

// Documents
<FileUpload accept=".pdf,.doc,.docx,.txt" />

// Multiple types
<FileUpload accept="image/*,.pdf,.doc,.docx,text/*" />

// All files
<FileUpload accept="*/*" />

File Size Limits

Size Limits
// 1MB limit
<FileUpload maxSize={1024 * 1024} />

// 5MB limit
<FileUpload maxSize={5 * 1024 * 1024} />

// 50MB limit
<FileUpload maxSize={50 * 1024 * 1024} />

File Count Limits

File Count
// Single file only
<FileUpload multiple={false} />

// Up to 5 files
<FileUpload multiple maxFiles={5} />

// Up to 20 files
<FileUpload multiple maxFiles={20} />

// Unlimited files
<FileUpload multiple />

Custom Upload Implementation

Basic Upload Function

Upload Implementation
const handleUpload = async (filesToUpload: FileUploadFile[]) => {
  for (const fileData of filesToUpload) {
    const formData = new FormData();
    formData.append('file', fileData.file);
    
    try {
      // Update status to uploading
      setFiles(prevFiles => 
        prevFiles.map(f => 
          f.id === fileData.id 
            ? { ...f, status: 'uploading', progress: 0 }
            : f
        )
      );

      // Make upload request
      const response = await fetch('/api/upload', {
        method: 'POST',
        body: formData,
      });

      if (response.ok) {
        // Success
        setFiles(prevFiles => 
          prevFiles.map(f => 
            f.id === fileData.id 
              ? { ...f, status: 'success', progress: 100 }
              : f
          )
        );
      } else {
        throw new Error('Upload failed');
      }
    } catch (error) {
      // Error
      setFiles(prevFiles => 
        prevFiles.map(f => 
          f.id === fileData.id 
            ? { ...f, status: 'error', error: 'Upload failed' }
            : f
        )
      );
    }
  }
};

Upload with Progress Tracking

Progress Tracking
const handleUploadWithProgress = async (filesToUpload: FileUploadFile[]) => {
  for (const fileData of filesToUpload) {
    const formData = new FormData();
    formData.append('file', fileData.file);
    
    try {
      setFiles(prevFiles => 
        prevFiles.map(f => 
          f.id === fileData.id 
            ? { ...f, status: 'uploading', progress: 0 }
            : f
        )
      );

      await new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        
        // Track upload progress
        xhr.upload.addEventListener('progress', (e) => {
          if (e.lengthComputable) {
            const progress = (e.loaded / e.total) * 100;
            setFiles(prevFiles => 
              prevFiles.map(f => 
                f.id === fileData.id 
                  ? { ...f, progress }
                  : f
              )
            );
          }
        });

        xhr.addEventListener('load', () => {
          if (xhr.status === 200) {
            setFiles(prevFiles => 
              prevFiles.map(f => 
                f.id === fileData.id 
                  ? { ...f, status: 'success', progress: 100 }
                  : f
              )
            );
            resolve(xhr.response);
          } else {
            reject(new Error('Upload failed'));
          }
        });

        xhr.addEventListener('error', () => {
          reject(new Error('Upload failed'));
        });

        xhr.open('POST', '/api/upload');
        xhr.send(formData);
      });
    } catch (error) {
      setFiles(prevFiles => 
        prevFiles.map(f => 
          f.id === fileData.id 
            ? { ...f, status: 'error', error: 'Upload failed' }
            : f
        )
      );
    }
  }
};

Accessibility

The File Upload component includes comprehensive accessibility features:

  • Keyboard Navigation: Full keyboard support with Enter and Space keys
  • Screen Reader Support: Proper ARIA labels and announcements
  • Focus Management: Visible focus indicators and logical tab order
  • Progress Announcements: Screen readers announce upload progress
  • Error Handling: Clear error messages for assistive technologies

API Reference

FileUpload Props

PropTypeDefaultDescription
variant'default' | 'default' | 'compact''default'Visual style variant
size'sm' | 'md' | 'lg''md'Size of the upload area
multiplebooleanfalseAllow multiple file selection
acceptstring-Accepted file types (MIME types or extensions)
maxSizenumber-Maximum file size in bytes
maxFilesnumber-Maximum number of files
disabledbooleanfalseDisable the upload component
filesFileUploadFile[][]Array of files
onFilesChange(files: FileUploadFile[]) => void-Callback when files change
onUpload(files: FileUploadFile[]) => Promise<void>-Upload function
showProgressbooleantrueShow upload progress
showPreviewbooleantrueShow image previews
uploadTextstring'Click to upload or drag and drop'Upload zone text
dragTextstring'Drop files here'Drag over text
classNamestring-Additional CSS classes