Tab Normalizer: Clean Up Messy Sheet Names and Sort Tabs in One Click
Strips leading and trailing spaces from every sheet name, then optionally sorts tabs alphabetically. Handles name collisions gracefully so you don't break the workbook.
Table of Contents
TL;DR: You inherit a workbook where five tabs are named " Sched-A ", three
have trailing spaces, and the tab order is whatever the prior preparer felt like.
One macro strips every space and sorts the whole workbook A→Z — or just cleans
the names and leaves the order alone. Your choice, one prompt.
The Problem
You open the Henderson workpaper from last year. The tab bar reads: " Sched-A ",
"Depreciation ", " Fixed Assets", "NOL ". Sorting them alphabetically
means 15 minutes of drag-and-drop — and every time you miss the drop target, the
tab flies to the wrong end of the workbook. Even worse, you can’t tell if
"Sched-A" and " Sched-A " are the same tab or a duplicate because the spaces
blend into the tab width at small sizes.
Excel gives you no “Sort Tabs” button and no “Trim Tab Names” command. It’s a manual chore that wastes ten minutes on every inherited file. This macro does both in under a second.
#Prerequisites & Setup
What you’ll need:
- Excel 2016+ (desktop)
- A workbook with multiple sheets — any workbook, any content
- No special preparation — the macro reads sheet names, not cell data
Limitations:
- Only affects
ThisWorkbook— the workbook containing the macro. Store in Personal Macro Workbook to run on any file - Trims leading/trailing spaces only — does not remove spaces within sheet names (e.g.,
"Fixed Assets"stays"Fixed Assets") - Sheets whose trimmed name would collide with an existing sheet name are skipped — the macro warns you with the specific conflict names
- Sorting is case-insensitive A→Z —
"sched-a"and"Sched-A"sort together - Very-hidden sheets (
xlSheetVeryHidden) are included in trimming and sorting — these are often VBA data sheets and renaming them may break macros that reference them by name
#The Macro
Option Explicit
Sub TabNormalizer()
' ── Tab Normalizer ─────────────────────────────────
' Strips leading and trailing spaces from every sheet
' name. Optionally sorts sheets A→Z. Warns and skips
' if a trimmed name would collide with an existing
' sheet name (" Sched-A " and "Sched-A" can't both
' live in the same workbook — the macro keeps the
' clean one and skips the messy one).
' ────────────────────────────────────────────────────
' ── State management ───────────────────────────────
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' ── Variables ──────────────────────────────────────
Dim ws As Worksheet
Dim trimmedName As String
Dim trimmedCount As Long
Dim collisionCount As Long
Dim collisionList As String
Dim sortChoice As VbMsgBoxResult
Dim i As Long, j As Long
Dim hasCollision As Boolean
' ── Error handling ─────────────────────────────────
On Error GoTo CleanUp
' ── Step 1: Ask about sorting ──────────────────────
sortChoice = MsgBox("Sort sheets alphabetically after " & _
"trimming?" & vbCrLf & vbCrLf & _
"Yes = Sorted A→Z" & vbCrLf & _
"No = Trim only, keep current order", _
vbYesNo + vbQuestion, "Sort Order")
' ── Step 2: Trim leading and trailing spaces ───────
trimmedCount = 0
collisionCount = 0
collisionList = ""
For Each ws In ThisWorkbook.Worksheets
trimmedName = Trim(ws.Name)
' Already clean — nothing to do
If trimmedName = ws.Name Then GoTo NextSheet
' Check if trimmed name would collide with an
' existing sheet (case-insensitive comparison)
hasCollision = False
For i = 1 To ThisWorkbook.Worksheets.Count
If i <> ws.Index Then
If StrComp(ThisWorkbook.Worksheets(i).Name, _
trimmedName, vbTextCompare) = 0 Then
hasCollision = True
Exit For
End If
End If
Next i
If hasCollision Then
' Name collision — warn and skip
collisionCount = collisionCount + 1
collisionList = collisionList & " • " & _
Chr(34) & ws.Name & Chr(34) & _
" → " & Chr(34) & trimmedName & _
Chr(34) & " already exists" & vbCrLf
Else
' Safe to rename
ws.Name = trimmedName
trimmedCount = trimmedCount + 1
End If
NextSheet:
Next ws
' ── Step 3: Sort sheets if requested ───────────────
If sortChoice = vbYes Then
' Bubble sort by repositioning sheets left-to-right
For i = 1 To ThisWorkbook.Worksheets.Count - 1
For j = i + 1 To ThisWorkbook.Worksheets.Count
If StrComp(ThisWorkbook.Worksheets(i).Name, _
ThisWorkbook.Worksheets(j).Name, _
vbTextCompare) > 0 Then
ThisWorkbook.Worksheets(j).Move _
Before:=ThisWorkbook.Worksheets(i)
End If
Next j
Next i
End If
' ── Step 4: Report ─────────────────────────────────
Dim msg As String
If trimmedCount = 0 And collisionCount = 0 Then
msg = "All sheet names are already clean." & vbCrLf
Else
msg = "Trimmed " & trimmedCount & " sheet name(s)." & vbCrLf
End If
If collisionCount > 0 Then
msg = msg & collisionCount & " sheet(s) skipped " & _
"(name would collide):" & vbCrLf & collisionList & vbCrLf
End If
If sortChoice = vbYes Then
msg = msg & "Sheets sorted A→Z."
Else
msg = msg & "Sheet order unchanged."
End If
MsgBox msg, vbInformation, "Tab Normalizer"
CleanUp:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
If Err.Number <> 0 Then
MsgBox "Error " & Err.Number & ": " & Err.Description, _
vbCritical, "Macro Error"
End If
End Sub
#How It Works
#Trimming runs without asking — it’s the one thing nobody wants to keep
The macro doesn’t ask “Do you want to trim spaces?” because nobody has ever
said yes to keeping " Depreciation " as a tab name. Leading and trailing
spaces in sheet names are never intentional — they’re artifacts of copy-pasted
tab names, imported data, or a sloppy rename. The macro strips them first, then
asks one question: sort or not?
This is what the “no code editor” rule looks like in practice. The macro reads the workbook, figures out what’s wrong, and fixes it. The only thing it asks is the one thing it genuinely can’t know: whether you prefer alphabetical order or whatever order the tabs are already in.
#Name collisions — the case where trimming creates a duplicate
What happens if your workbook has both "Sched-A" and " Sched-A "? After
trimming, both would want the name "Sched-A" — and Excel doesn’t allow
duplicate sheet names.
The macro catches this with a pre-rename check:
For i = 1 To ThisWorkbook.Worksheets.Count
If i <> ws.Index Then
If StrComp(ThisWorkbook.Worksheets(i).Name, _
trimmedName, vbTextCompare) = 0 Then
hasCollision = True
Exit For
End If
End If
Next i
If a collision is detected, the macro skips the rename and adds the conflict to a list. The final message box shows every collision so you can decide what to do manually:
Trimmed 5 sheet name(s).
2 sheet(s) skipped (name would collide):
• " Sched-A " → "Sched-A" already exists
• " NOL" → "NOL" already exists
Sheets sorted A→Z.
The macro intentionally leaves the collision unresolved. It could pick a winner
(rename the messy one to "Sched-A (2)") but that would hide the fact that you
have two sheets that might be the same workpaper — one of them might be an old
draft someone was supposed to delete. The warning keeps it visible.
#The sort is a simple bubble sort — and it’s fast enough
VBA has no built-in Sort method for the Worksheets collection. The macro
uses a bubble sort — the algorithm every CS 101 student learns — because it’s
clean, readable, and fast enough for 50 sheets:
For i = 1 To ThisWorkbook.Worksheets.Count - 1
For j = i + 1 To ThisWorkbook.Worksheets.Count
If StrComp(ThisWorkbook.Worksheets(i).Name, _
ThisWorkbook.Worksheets(j).Name, _
vbTextCompare) > 0 Then
ThisWorkbook.Worksheets(j).Move _
Before:=ThisWorkbook.Worksheets(i)
End If
Next j
Next i
The Move Before:= repositions the sheet in the tab bar. On a 30-sheet
workbook, this completes in under half a second. No Scripting.Dictionary,
no quicksort, no external references — just two loops and a string comparison
that works on every Excel install since 2003.
#StrComp with vbTextCompare — the case-insensitive sort that handles Unicode
Three decisions went into the comparison:
-
StrCompinstead of>:StrCompreturns -1, 0, or 1 — it’s a three-way comparison function designed for sorting. Using>withOption Compare Textwould also work but requires a module-level directive that affects every comparison in the module.StrComplocalizes the behavior to the exact comparison you want. -
vbTextCompareinstead ofvbBinaryCompare: Case-insensitive sorting means"fixed assets"and"Fixed Assets"sort together. In a tax workpaper, nobody cares about the capitalization of tab names — they care about finding the right tab. -
No special handling for numbers:
"Sched-A"and"Sched-E"sort alphabetically, not by section letter. If your workpapers use"Sched-01"through"Sched-15", they won’t sort01, 02, 03... 10, 11— they’ll sort01, 02, 03, 10, 11... 15because string sorting compares digit-by-digit. For most tax workbooks, this is fine. The Adapt It section shows how to handle numbered prefixes if you need it.
#The message box tells the full story — no silent fixes
Every tab-normalizing action is reported: how many were trimmed, how many
collided, and whether sorting happened. If the result is "All sheet names are already clean." with "Sheet order unchanged.", you know the workbook was
already in order and the macro did nothing harmful. If you expected 8 trims and
got 2, the collision list tells you exactly which sheets conflicted.
#Adapt It
Get the next macro in your inbox
One copy-paste-ready macro recipe every two weeks. No spam, no VBA theory — just automation that saves you time.
Excel Macro Guy
Excel enthusiast · married to an accountant
I love Excel. My wife is an accountant. Every busy season, I watch her wrestle with workpapers and think "a macro could do that in half a second." So I build them. She tests them on real client data. What survives gets published here.