Bulk File Rename Tool
Add prefixes, suffixes, sequence numbers, find & replace, change case, modify extensions — all in your browser
Drop files here, or click the button below
Any file type and quantity, processed only in your browser — never uploaded
What is bulk file rename?
Bulk file rename is the operation of changing a group of file names in one pass according to a set of rules. Typical jobs: turning camera dumps like IMG_1234.JPG into 2026-06-trip-001.jpg, prepending @2x_ to design exports, sequencing scanned receipts by invoice date. macOS Finder's built-in rename covers Find & Replace and a name + counter format but cannot rewrite extensions; Windows Explorer's Tab-rename only adds simple sequence numbers (PowerToys PowerRename adds regex but needs a separate install). Anything more involved usually goes to desktop apps like ReNamer or Bulk Rename Utility, or to hand-rolled PowerShell / shell scripts. This tool exposes each operation — prefix/suffix, numbering, find & replace, case, extension — as a visual control with live preview and duplicate detection, so you can verify the result before downloading. The whole flow runs in browser memory, so filenames containing project codes, client names, or personal information stay on your machine.
How to use
Steps
- Drop files into the upload zone, or click "Select files" to pick them — adding more times appends to the list
- Reorder with "Sort by name / size", or use the up/down arrows on each row for fine-tuning
- Configure rules across the three cards: prefix/suffix, find & replace, numbering, case, extension
- The "New name" column updates live — blue means it differs from the original, red means it collides with another file
- Click "Download this file" to save individual files, or "Download as ZIP" to grab everything at once
Rule order
- Replace the base name first (leave it empty to keep the original) — useful when you want every file to end up as e.g. "photo" plus a sequence number, regardless of its original name
- Then find/replace runs on the base name only, never the extension
- Numbering inserts next, as prefix, suffix, or full replacement of the base
- Prefix and suffix wrap around the result
- Case transformation and extension handling come last; the extension is processed independently
Use cases
How it works
The whole tool runs on the browser's File API, Blob, and URL.createObjectURL — every byte stays in memory. When you drop or pick files, the browser hands JavaScript File objects (Blob subclasses) to the JS heap; the tool only reads each file's `name` property for renaming, never touching the binary content. Rules apply in a fixed order to avoid interference: first the base name is determined (user-supplied or original base), with the extension split at lastIndexOf('.') (dotfiles like .gitignore are treated as base only, no extension); then find/replace runs as String.prototype.split + Array.prototype.join (no regex — avoids unintentional special-character matching); next the sequence number is computed via `String(start + i).padStart(pad, '0')` and placed as prefix/suffix/replacement of the base; the prefix and suffix are then concatenated around the result; case transformation applies to the whole prefix+base+suffix string at once; the extension is processed independently (keep/upper/lower/replace). Duplicate detection counts each new name in a Map<string, number> during preview; names appearing more than once enter a duplicateSet that the UI highlights in red. Clicking "Download as ZIP" while the set is non-empty surfaces an error toast and aborts the pack — the original list is preserved so you can adjust the rules and try again. This runs inside useMemo([files, opts]), so rule edits incrementally recompute without re-rendering the file list. Single-file download wraps the File in URL.createObjectURL, triggers a synthetic `<a download>` click, and revokes the URL immediately. ZIP packing dynamically imports JSZip on first click (~80 KB, no first-paint cost), reads each file's arrayBuffer(), writes it under the new name, and calls generateAsync({type:'blob', compression:'DEFLATE', compressionOptions:{level:6}}). DEFLATE is the ZIP default; level 6 balances ratio against speed. Rule computation is O(n) in file count; pack time scales with total bytes. Browsers typically read File contents lazily — arrayBuffer() is when bytes actually leave disk — so even thousands of queued files cost almost nothing during preview, with the memory peak only on "Download as ZIP".
- lastIndexOf('.') splits the extension; if the only dot is at index 0 (e.g. .gitignore) the file has no extension and the leading dot stays in the base name
- Find & replace uses String.prototype.split + Array.prototype.join instead of replaceAll, avoiding accidental interpretation of replacement-string tokens like $1 or $& in the replacement field
- padStart(pad, '0') generates the zero-padded number; pad=0 outputs the raw number for cases where alignment isn't needed
- Case transformation runs after prefix/suffix are joined, so it normalizes the whole base segment at once; the extension is handled independently
- Duplicate detection runs inside useMemo with a Map; O(n) recompute, and the UI only swaps a className for highlighted rows — no DOM rebuild
- Blob URLs are revoked immediately via URL.revokeObjectURL to free the URL allocation pool, since reserved object URLs accumulate quickly under heavy use
- JSZip is loaded via dynamic import only when packing is triggered, so the ~80 KB code never affects first-paint or LCP
- DEFLATE level 6 is the browser-side sweet spot; level 9 adds 1–2% compression at 2–3× the time and barely helps for already-compressed images/video
Examples
Prefix + 3-digit zero-padded numbering
Input: photo.jpg, sunset.jpg, beach.jpg
Rules: prefix=trip2026_, start=1, pad=3, position=after
Output:
trip2026_photo001.jpg
trip2026_sunset002.jpg
trip2026_beach003.jpgReplace base name entirely
Input: GH010001.MP4, GH010002.MP4, GH010003.MP4
Rules: base=SceneA_take, start=1, pad=2, position=after, ext=lowercase
Output:
SceneA_take01.mp4
SceneA_take02.mp4
SceneA_take03.mp4Strip redundant suffix via find/replace
Input: report_v1_FINAL.docx, summary_v1_FINAL.docx
Rules: find=_v1_FINAL, replace=(empty), case=lowercase
Output:
report.docx
summary.docxReplace extension
Input: index.htm, about.htm, contact.htm
Rules: extension=replace, new ext=html
Output:
index.html
about.html
contact.htmlPrefix + space-to-hyphen + lowercase
Input: Banner Image.PNG, Hero Photo.JPG
Rules: prefix=web-, find=(space), replace=-, case=lowercase, extension=lowercase
Output:
web-banner-image.png
web-hero-photo.jpgFAQ
Are my files uploaded to a server?
No. From adding files to producing new names to packing the ZIP, everything runs locally in your browser. No upload requests are made. You can verify in your browser's DevTools Network panel.
Why isn't my .gitignore / .bashrc treated as having an extension?
The tool splits on the last dot, but if the only dot is at the very start (a dotfile), the whole name is treated as the base — this avoids mistaking a hidden-prefix marker for an extension.
What happens if my new names collide?
The preview list highlights duplicates in red and shows a count badge. Clicking "Download as ZIP" while there are duplicates is blocked with an error message — your file list isn't lost, just adjust the rules and try again. Common fixes: enable numbering, increase pad width, add a prefix/suffix.
Can it rename folders / preserve nesting?
Not yet. The current version handles a single flat file list. For directory-aware bulk renaming, rename the files here and then assemble the directory tree using the ZIP Pack tool.
Does find & replace support regex?
No. It uses literal string matching (split-join) on purpose, so users can't accidentally trigger replacement-string tokens like $1 or $& in their replacement field. Plain matching covers the vast majority of cleanup needs.
How many files can it handle?
There's no hard cap on count — preview is essentially free. Packing is bounded by browser memory: very large totals (multi-GB) may fail to allocate a single Blob, in which case split your files into batches and pack each separately.
The downloaded ZIP shows garbled non-ASCII names. Why?
JSZip writes UTF-8 filenames by default, which modern extractors (macOS Archive Utility, Windows 11 Explorer, 7-Zip, WinRAR 5+) read correctly. Old Windows Explorer on Win 7/8 may still mis-decode non-Latin names — use 7-Zip or Bandizip instead.
Can I undo a rename if I made a mistake?
Renames only happen at download time — until you click "Download as ZIP" or "Download this file", the original files on disk are untouched. The preview is fully reversible: clear the rules, edit them, or remove individual files from the list. If you've already downloaded, just adjust the rules and download again with the same source files.