Building a Browser-Based Media Manager for Cloudflare R2

6 min read

Managing media files for a static site shouldn't require deploying a whole backend. With Cloudflare R2's S3-compatible API, you can build a client-side media manager that runs entirely in the browser—no server needed.

I built one, and you can too. Here's why and how.

Why Cloudflare R2?

R2 is S3-compatible object storage with zero egress fees. That's the killer feature. With AWS S3, you pay every time someone downloads a file. With R2, you pay for storage—that's it.

The cost difference is massive:

  • S3: $0.023/GB storage + $0.09/GB egress
  • R2: $0.015/GB storage + $0 egress

For a blog serving images to thousands of readers, S3 can cost hundreds. R2 costs dollars.

Plus, R2 runs on Cloudflare's global network (300+ data centers), so it's fast everywhere. And since it's S3-compatible, you can use the AWS SDK directly.

The Problem: Managing Media for Static Sites

Static site generators like Gatsby, Hugo, and Next.js are great—until you need to add images.

Your options:

  1. Commit images to git - Repository bloats, slow clones
  2. Store in project - Build times increase, poor caching
  3. Use a CMS - Now you need a backend, database, auth
  4. Manual S3 uploads - Use AWS console, copy URLs by hand

None of these are great for quick updates.

What I wanted:

  • Drag-and-drop uploads
  • Instant shareable URLs
  • No backend to maintain
  • Works from localhost
  • Free (or nearly free)

The Solution: Browser-Based R2 Manager

I built a single-page application that talks directly to Cloudflare R2 from the browser.

Stack:

  • Pure HTML/CSS/JavaScript (no frameworks)
  • AWS SDK for JavaScript (S3-compatible API)
  • Cloudflare R2 (S3-compatible storage)

Features:

  • Drag-and-drop file uploads with progress tracking
  • Grid and list view of all files
  • Full-screen image preview
  • Generate presigned URLs (shareable links)
  • Delete files with confirmation
  • Search and filter
  • Dark mode
  • Responsive (mobile-friendly)

It's just one HTML file, one CSS file, and two JavaScript files. No build process. No dependencies. No server.

How It Works

1. R2 is S3-Compatible

Cloudflare R2 speaks the same language as AWS S3. That means you can use the battle-tested AWS SDK:

// Configure AWS SDK to point to R2
AWS.config.update({
    accessKeyId: 'YOUR_R2_ACCESS_KEY',
    secretAccessKey: 'YOUR_R2_SECRET_KEY',
    region: 'auto'
});

const s3 = new AWS.S3({
    endpoint: 'https://YOUR_ACCOUNT.r2.cloudflarestorage.com',
    s3ForcePathStyle: true
});

2. CORS Makes Browser Uploads Possible

By default, browsers can't make requests to other domains. But R2 supports CORS (Cross-Origin Resource Sharing).

Configure it once in the R2 dashboard:

{
  "AllowedOrigins": ["http://localhost:8000"],
  "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
  "AllowedHeaders": ["*"]
}

Now your browser can upload directly to R2.

3. Upload Files with Progress Tracking

const upload = s3.upload({
    Bucket: 'my-bucket',
    Key: fileName,
    Body: file,
    ContentType: file.type
});

upload.on('httpUploadProgress', (progress) => {
    const percent = (progress.loaded / progress.total) * 100;
    updateProgressBar(percent);
});

await upload.promise();

No backend. The file goes straight from your browser to R2.

4. List and Display Files

const response = await s3.listObjectsV2({
    Bucket: 'my-bucket'
}).promise();

const files = response.Contents.map(file => ({
    key: file.Key,
    size: file.Size,
    lastModified: file.LastModified
}));

renderFileGrid(files);

5. Generate Shareable URLs

For private buckets, use presigned URLs:

const url = await s3.getSignedUrlPromise('getObject', {
    Bucket: 'my-bucket',
    Key: 'image.jpg',
    Expires: 86400  // 24 hours
});

// Copy to clipboard
navigator.clipboard.writeText(url);

For public buckets, you can use custom domains:

https://media.yourdomain.com/image.jpg

Security Considerations

This is designed for local development, not public deployment.

You're putting API credentials in JavaScript. That's fine for:

  • Running on localhost only
  • Your personal machine
  • Tools you never deploy

Don't do this:

  • Deploy to production without authentication
  • Share your config file with credentials
  • Commit credentials to public repos

For production, you'd want:

  • Backend API that stores credentials securely
  • User authentication
  • Cloudflare Workers as a proxy layer

Building It Yourself

I created a full tutorial with 14 steps and AI prompts for each:

👉 Make A Media Manager With Cloudflare R2

The tutorial covers:

  1. Creating an R2 bucket and getting credentials
  2. Configuring CORS for browser access
  3. Setting up the project structure
  4. Initializing the AWS SDK for R2
  5. Implementing drag-and-drop uploads
  6. Listing and displaying files
  7. Building the preview modal
  8. Generating shareable URLs
  9. Adding delete functionality
  10. Image optimization
  11. Dark mode and responsive design
  12. Error handling and security

Each step includes:

  • Clear explanation of what you're building
  • Complete AI prompt to generate the code
  • Code examples and screenshots

Time to complete: ~4 hours

Prerequisites:

  • Cloudflare account (free tier works)
  • Basic HTML/CSS/JavaScript knowledge
  • Text editor and browser

Use Cases

This isn't just for blogs. Here's where a browser-based R2 manager is useful:

Static Sites:

  • Upload images for Gatsby/Hugo/Jekyll posts
  • Manage assets for Next.js projects
  • Host downloads and resources

Personal Projects:

  • Photo backup and sharing
  • File hosting for side projects
  • Asset CDN for hobby apps

Development:

  • Quick image uploads during prototyping
  • Share screenshots with clients
  • Temporary file hosting

Backup:

  • Store important documents
  • Archive photos
  • Keep copies of project files

Cost Breakdown

Let's say you upload 10GB of images and serve them to 10,000 visitors who view 100GB total.

AWS S3

  • Storage: 10GB × $0.023 = $0.23
  • Egress: 100GB × $0.09 = $9.00
  • Total: $9.23/month

Cloudflare R2

  • Storage: 10GB × $0.015 = $0.15
  • Egress: 100GB × $0 = $0.00
  • Total: $0.15/month

You save $9.08/month, or $109/year.

And that's conservative. If your site gets popular and serves 1TB of images, S3 costs $90/month. R2 stays at $0.15/month.

What I Learned

R2 is S3, but Better (for Public Content)

If you're serving public content (images, videos, downloads), R2 beats S3 on price and speed. The S3-compatible API means migration is easy.

Client-Side File Management is Viable

With CORS and presigned URLs, you can build powerful file management tools without a backend. This pattern works for more than just R2—any S3-compatible service supports it.

Vanilla JS is Underrated

This entire project is 500 lines of JavaScript. No React, no build process, no npm packages (except the AWS SDK). It loads instantly, works offline (with caching), and is trivial to debug.

Security by Design, Not by Obscurity

Keeping credentials out of client-side code matters. This tool works because it's designed to run locally, not because credentials are "hidden" somehow. For production, you need a different architecture.

Try It

The full tutorial with step-by-step instructions and AI prompts is here:

👉 Make A Media Manager With Cloudflare R2

Or clone the finished code from the tutorial and customize it for your needs.

R2 is genuinely useful—cheap, fast, and compatible with everything. And building your own tools to manage it is easier than you'd think.

What's Next?

Potential improvements:

  • Image optimization - Compress images before upload using Canvas API
  • Batch operations - Select multiple files to delete at once
  • Folders - Organize with virtual folders (key prefixes)
  • Metadata - Add tags, descriptions, and custom fields
  • Gatsby integration - Add upload buttons in dev environment

For now, it does what I need: upload images, get URLs, manage files. Simple, fast, and free (almost).

If you build something with R2, let me know. I'm curious what other use cases exist for browser-based object storage tools.

Fred

Fred

AUTHOR

Full-stack developer with 10+ years building production applications. I've been deploying to Cloudflare's edge network since Workers launched in 2017.