DEV Community

Cover image for I built a 100% client-side File Converter in React & WebAssembly (Zero Cloud Uploads)
Pradeep Rathod
Pradeep Rathod

Posted on

I built a 100% client-side File Converter in React & WebAssembly (Zero Cloud Uploads)

Every time I needed to convert an iPhone .HEIC photo, merge PDFs, or convert a CSV to Excel, online tools forced me to upload private files to their cloud servers and hit me with a "Max 3 files per day" paywall.

With modern browser APIs, uploading files to a backend server just to convert formats isn't necessary anymore. So I built ZeroConvert:

🔗 Live Tool: https://getzeroconvert.com

How It Works Under the Hood

  • Images (HEIC, WebP, AVIF, PNG, JPG): Processed locally using HTML5 Canvas and client-side decoding buffers.
  • Documents & PDFs: Merged and converted in local RAM via pdf-lib and pdfjs-dist.
  • Spreadsheets & Data (XLSX, CSV, JSON, XML): Parsed instantaneously in the browser using SheetJS.
  • Audio & Video (MP4, MP3, WAV, WebM): Powered by FFmpeg.wasm running inside Web Workers.

Because there are zero server uploads, conversions happen at native memory speed with 100% privacy and zero daily caps.

Would love for you to test it out and let me know what formats or features I should add next!

Top comments (6)

Collapse
 
danielecangi profile image
DaC •

Really interesting approach....I saw the website and one thing I’d be curious about is how you’re handling potentially malicious or malformed input files on the client side.

The privacy model is strong, but since the browser becomes the execution boundary, input hardening seems like an important part of the design too

Collapse
 
pradeeprathod1165 profile image
Pradeep Rathod •

Really good question! Moving everything to the browser definitely shifts the focus from server security to preventing tab crashes and client-side XSS.

Here is how I handle it right now:

• WASM Sandboxing: Heavy parsers like FFmpeg and libheif run inside isolated WebAssembly memory in Web Workers. If someone drops in a corrupted file, the worker just throws a caught error without touching the main thread.
• Stripping Malicious SVGs: Since SVGs can hide <script> tags, I never inject uploaded files into the DOM. Images load into an off-screen new Image() and get drawn onto an HTML5 <canvas>, which blocks script execution and strips everything down to raw pixels.
• Pure Binary-to-Blob Parsing: For spreadsheets and PDFs, SheetJS and PDF-Lib process the buffers in memory and export straight to a downloadable Blob—cell contents are never rendered as raw HTML.
• Size Limits & Memory Cleanup: To stop giant files from freezing the tab, there are strict file-size caps up front, and URL.revokeObjectURL() runs immediately inside try/catch blocks so failed files don't leak RAM.

Collapse
 
mreeves_tools profile image
Mark Reeves •

nice, wasm is the only sane route for this. we built swapfile.live the same way (conversion happens fully in-browser, nothing gets uploaded) and the memory ceiling on big video files was our worst enemy - did you hit that with ffmpeg.wasm or are you streaming the output?

Collapse
 
pradeeprathod1165 profile image
Pradeep Rathod •

yeah ! i am actually facing that issue. while converting video to another video format it takes long time for conversion. definatly looking for fix !

Some comments may only be visible to logged-in visitors. Sign in to view all comments.