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 🙏

© 2026 – Pkg Stats / Ryan Hefner

evodeus-b2-react

v0.1.2

Published

Modern React file upload components with drag & drop, progress tracking, automatic image resizing, and Cloudflare R2 integration

Readme

React File Upload Components

Modern, accessible React file upload components with drag & drop support, progress tracking, automatic image resizing, and Cloudflare R2 integration.

Components

  • FileUpload - General purpose file upload component
  • PhotoUpload - Advanced photo upload with gallery view and multiple versions
  • SimplePhotoUpload - Photo upload with FileUpload interface but automatic resizing

Features

  • 🎯 Drag & Drop - Intuitive file dropping interface
  • 📊 Progress Tracking - Real-time upload progress with visual feedback
  • 🖼️ Automatic Image Resizing - Creates thumbnail, preview, and original versions
  • 🔒 File Validation - Size and type validation with customizable limits
  • 🎨 Customizable - Tailwind CSS styling with className override support
  • Accessible - Built with accessibility best practices
  • 🔧 TypeScript - Full TypeScript support with exported types
  • ☁️ Cloudflare R2 - Optimized for Cloudflare R2 storage via worker proxy

Installation

npm install evodeus-b2-react

Peer Dependencies

Make sure you have these installed in your project:

npm install react react-dom

Basic Usage

FileUpload - General Purpose

import { FileUpload } from 'evodeus-b2-react'
import 'evodeus-b2-react/style.css'

function App() {
  return (
    <FileUpload
      workerUrl="https://up.evodeus.xyz"
      onUploadComplete={(fileUrl, fileName) => {
        console.log('Upload complete:', { fileUrl, fileName })
      }}
      onUploadError={(error) => {
        console.error('Upload error:', error)
      }}
      maxFileSize={10}
      acceptedFileTypes={['image/*', 'application/pdf']}
    />
  )
}

SimplePhotoUpload - Photo Upload with FileUpload Interface

import { SimplePhotoUpload } from 'evodeus-b2-react'
import 'evodeus-b2-react/style.css'

function App() {
  return (
    <SimplePhotoUpload
      workerUrl="https://up.evodeus.xyz"
      onUploadComplete={(fileUrl, fileName, allVersions) => {
        console.log('Main photo URL:', fileUrl)
        console.log('All versions:', allVersions) // original, preview, thumbnail
        
        // Access specific versions
        const thumbnail = allVersions?.find(v => v.version === 'thumbnail')
        const preview = allVersions?.find(v => v.version === 'preview')
      }}
      onUploadError={(error) => {
        console.error('Upload error:', error)
      }}
      maxFileSize={10}
    />
  )
}

PhotoUpload - Advanced Photo Gallery

import { PhotoUpload } from 'evodeus-b2-react'
import 'evodeus-b2-react/style.css'

function App() {
  return (
    <PhotoUpload
      workerUrl="https://up.evodeus.xyz"
      onUploadComplete={(uploads) => {
        console.log('Photo versions created:', uploads)
        // uploads contains all 3 versions with dimensions and URLs
      }}
      onUploadError={(error) => {
        console.error('Upload error:', error)
      }}
      maxFileSize={10}
    />
  )
}

Component Comparison

| Feature | FileUpload | SimplePhotoUpload | PhotoUpload | |---------|------------|-------------------|-------------| | Endpoint | /upload | /photo-upload | /photo-upload | | File Types | Configurable | Images only | Images only | | Processing | None | Auto-resize (3 versions) | Auto-resize (3 versions) | | Interface | Generic upload | FileUpload-like | Gallery view | | Callback | Single URL | Original URL + all versions | All versions array | | Use Case | General files | Photo upload replacement | Photo galleries |

Props

FileUpload Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | workerUrl | string | required | URL of your Cloudflare Worker | | onUploadComplete | (fileUrl: string, fileName: string) => void | undefined | Callback when upload succeeds | | onUploadError | (error: string) => void | undefined | Callback when upload fails | | maxFileSize | number | 10 | Maximum file size in MB | | acceptedFileTypes | string[] | ['image/*', 'application/pdf', '.doc', '.docx'] | Accepted file types | | className | string | undefined | Additional CSS classes |

SimplePhotoUpload Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | workerUrl | string | required | URL of your Cloudflare Worker with /photo-upload | | onUploadComplete | (fileUrl: string, fileName: string, allVersions?: PhotoUploadResult[]) => void | undefined | Callback with original URL + all versions | | onUploadError | (error: string) => void | undefined | Callback when upload fails | | maxFileSize | number | 10 | Maximum file size in MB | | className | string | undefined | Additional CSS classes |

PhotoUpload Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | workerUrl | string | required | URL of your Cloudflare Worker with /photo-upload | | onUploadComplete | (uploads: PhotoUploadResult[]) => void | undefined | Callback with all photo versions | | onUploadError | (error: string) => void | undefined | Callback when upload fails | | maxFileSize | number | 10 | Maximum file size in MB | | className | string | undefined | Additional CSS classes |

Styling

All components use Tailwind CSS classes. Import the styles:

import 'evodeus-b2-react/dist/style.css'

Or customize with your own classes using the className prop.

Photo Upload Features

Automatic Image Resizing

When using SimplePhotoUpload or PhotoUpload, each uploaded image automatically generates:

  • Original: Full-size original image
  • Preview: Resized to max 800x600 (maintains aspect ratio)
  • Thumbnail: Resized to max 150x150 (maintains aspect ratio)

PhotoUploadResult Interface

interface PhotoUploadResult {
  version: 'original' | 'preview' | 'thumbnail'
  fileName: string
  fileId: string
  url: string
  fullUrl: string
  dimensions: { width: number; height: number }
}

Advanced Usage

Custom File Types

<FileUpload
  workerUrl="https://your-worker.workers.dev"
  acceptedFileTypes={[
    'image/jpeg',
    'image/png', 
    'application/pdf',
    '.docx',
    '.xlsx'
  ]}
  maxFileSize={25}
/>

With Custom Styling

<FileUpload
  workerUrl="https://your-worker.workers.dev"
  className="border-blue-500 bg-blue-50"
  onUploadComplete={(url, name) => {
    // Handle success
  }}
/>

Worker Setup

This component requires a Cloudflare Worker for handling uploads. The worker should:

  1. Accept POST requests to /upload
  2. Handle multipart f

Development

# Install dependencies
npm install

# Start development server
npm run dev

# Build for production
npm run build

# Preview production build
npm run preview

Architecture

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   React App     │───▶│ Cloudflare Worker │───▶│  Backblaze B2   │
│                 │    │                  │    │                 │
│ FileUpload      │    │ /upload          │    │ File Storage    │
│ Component       │    │ proxy endpoint   │    │                 │
└─────────────────┘    └──────────────────┘    └─────────────────┘
  1. React Component sends file to Worker via /upload endpoint
  2. Cloudflare Worker authenticates with B2 and proxies the upload
  3. Worker uploads file to B2 and returns the file URL to React

Note: Files are uploaded through the Worker proxy to avoid CORS issues with direct B2 uploads.

Security Notes

  • Files are uploaded directly to B2, not through your server
  • The Worker only provides temporary upload URLs
  • No sensitive B2 credentials are exposed to the client
  • File validation happens on both client and server side

Troubleshooting

Common Issues

  1. CORS Errors:
    • Ensure your Cloudflare Worker has CORS enabled
    • Check B2 bucket CORS settings in Backblaze console
    • Use the Debug component to test CORS connectivity
  2. Upload Failures: Check B2 credentials in Worker environment variables
  3. File Size Limits: Verify both component and B2 bucket limits
  4. Network Issues: Check Worker URL accessibility
  5. "Failed to fetch" Error:
    • Usually indicates CORS issues
    • Test with the Node.js script: node test-b2-direct.js
    • Check browser network tab for detailed error info

Debug Mode

Enable console logging to debug upload issues:

const handleUploadError = (error: string) => {
  console.error('Upload error:', error)
  // Add your error handling logic
}

License

MIT License - feel free to use in your projects!