Split Workbook By Sheet: Export Every Tab to Its Own File in One Click
Export each visible sheet to a separate .xlsx file in seconds. Choose all sheets or pick specific ones — no more Move or Copy, Save As, Close, repeat.
Table of Contents
TL;DR: You have a 20-entity consolidation workbook. Each entity owner needs only their own schedule. Right-click tab → Move or Copy → new workbook → Save As → name it → close → repeat 19 more times. This macro does all 20 in 3 seconds, exporting each sheet to a clean .xlsx file in a dedicated folder.
The Problem
It’s the final week before the extended deadline and each entity owner needs their individual tax schedule — and only theirs. You have a 20-tab consolidation workbook with sheets for Entity-A through Entity-T. The partner says “send each entity owner their schedule before noon.”
You right-click Entity-A’s tab. Move or Copy → New workbook → OK. File → Save As → navigate to the right folder → “Entity-A.xlsx” → Save → close the new workbook. Back to the consolidation file. Right-click Entity-B’s tab. Repeat.
By Entity-F you’ve lost track of which ones you’ve done. By Entity-K you’ve accidentally overwritten Entity-J. By Entity-T, 25 minutes have passed, you’ve created two copies of Entity-D, and Entity-M is mysteriously missing its last three rows because you somehow dragged something during the copy.
And that’s assuming you need all sheets. If the partner says “just send A through F and M through Q” — now you’re also tracking which ones to skip while you mechanically repeat the same five clicks for each.
#Prerequisites & Setup
What you’ll need:
- Excel 2016+ (desktop)
- A workbook that has been saved at least once (the macro needs a folder path for the output files)
- Sheets you want to export — the macro skips hidden sheets by default when exporting all
What the macro does:
- Asks whether to export all visible sheets or a comma-separated list you type in
- Creates a subfolder named after your workbook (e.g.,
Henderson-Consolidation-sheets/) in the same folder - Copies each selected sheet to a brand new
.xlsxworkbook, saves it, and closes the file — all in the background - Reports how many files were created and where they are
Limitations:
- Exports values, formulas, and formatting — but NOT VBA code (
.xlsxfiles are macro-free). If your sheets depend on VBA, the exported files won’t have it - Sheet names with invalid filename characters (
/ \ ? * [ ] :) get those characters replaced with underscores in the output filename - If a file with the same name already exists in the output folder, it’s overwritten silently. Run the timestamped backup macro first if that worries you
- Only exports from
ThisWorkbook— the workbook containing the macro. Store in your Personal Macro Workbook if you want to split any open workbook - The original workbook is not modified in any way
#The Macro
Option Explicit
Sub SplitWorkbookBySheet()
' ── Split Workbook By Sheet ────────────────────────
' Exports each selected sheet to its own .xlsx file
' in a dedicated subfolder. Choose all visible sheets
' or pick specific ones by name.
'
' Non-destructive — creates copies, never touches
' the original workbook.
' ────────────────────────────────────────────────────
' ── Configuration ──────────────────────────────────
Const SUBFOLDER_SUFFIX As String = "-sheets"
' ── State management ───────────────────────────────
Application.ScreenUpdating = False
Application.DisplayAlerts = False
' ── Variables ──────────────────────────────────────
Dim ws As Worksheet
Dim outFolder As String
Dim userChoice As VbMsgBoxResult
Dim sheetList As String
Dim names() As String
Dim i As Long, count As Long
Dim safeName As String
' ── Error handling ─────────────────────────────────
On Error GoTo CleanUp
' ── Pre-flight check ───────────────────────────────
If Len(ThisWorkbook.Path) = 0 Then
MsgBox "Save the workbook first. Macro needs a " & _
"folder path for the output files.", _
vbExclamation, "Split Workbook"
GoTo CleanUp
End If
outFolder = ThisWorkbook.Path & "\" & _
Left(ThisWorkbook.Name, InStrRev(ThisWorkbook.Name, ".") - 1) & _
SUBFOLDER_SUFFIX
' ── Ask: all sheets or specific? ───────────────────
userChoice = MsgBox("Export ALL visible sheets?" & _
vbCrLf & vbCrLf & _
"Yes = All visible sheets" & vbCrLf & _
"No = Pick specific sheets" & vbCrLf & _
"Cancel = Quit", vbYesNoCancel + vbQuestion, _
"Split Workbook")
If userChoice = vbCancel Then GoTo CleanUp
' ── Create output folder ───────────────────────────
On Error Resume Next
MkDir outFolder
On Error GoTo CleanUp
count = 0
If userChoice = vbYes Then
' ── Export all visible sheets ──────────────────
For Each ws In ThisWorkbook.Worksheets
If ws.Visible = xlSheetVisible Then
safeName = Replace(Replace(Replace(Replace( _
Replace(Replace(Replace(ws.Name, _
"/", "_"), "\", "_"), "?", "_"), _
"*", "_"), "[", "_"), "]", "_"), ":", "_")
ws.Copy
ActiveWorkbook.SaveAs outFolder & "\" & _
safeName & ".xlsx", xlOpenXMLWorkbook
ActiveWorkbook.Close False
count = count + 1
End If
Next ws
Else
' ── Export user-specified sheets ───────────────
sheetList = InputBox("Enter sheet names to export," & _
" separated by commas:" & vbCrLf & _
"(e.g., Entity-A, Entity-B, Entity-C)", _
"Choose Sheets")
If sheetList = "" Then GoTo CleanUp
names = Split(sheetList, ",")
For i = LBound(names) To UBound(names)
Dim target As String
target = Trim(names(i))
On Error Resume Next
Set ws = ThisWorkbook.Worksheets(target)
On Error GoTo CleanUp
If Not ws Is Nothing Then
safeName = Replace(Replace(Replace(Replace( _
Replace(Replace(Replace(ws.Name, _
"/", "_"), "\", "_"), "?", "_"), _
"*", "_"), "[", "_"], "]", "_"), ":", "_")
ws.Copy
ActiveWorkbook.SaveAs outFolder & "\" & _
safeName & ".xlsx", xlOpenXMLWorkbook
ActiveWorkbook.Close False
count = count + 1
Set ws = Nothing
End If
Next i
End If
' ── Report results ─────────────────────────────────
If count = 0 Then
MsgBox "No sheets were exported.", vbInformation, _
"Split Workbook"
Else
MsgBox "Created " & count & " file(s) in:" & _
vbCrLf & vbCrLf & outFolder, _
vbInformation, "Split Workbook"
End If
CleanUp:
Application.ScreenUpdating = True
Application.DisplayAlerts = True
If Err.Number <> 0 Then
MsgBox "Error " & Err.Number & ": " & Err.Description, _
vbCritical, "Macro Error"
End If
End Sub
#How It Works
#The MsgBox-as-menu pattern
This macro doesn’t ask you to edit constants or open a UserForm. It puts all decisions in front of you at runtime. The first thing you see is a three-button MsgBox:
Export ALL visible sheets?
Yes = All visible sheets
No = Pick specific sheets
Cancel = Quit
vbYesNoCancel gives three choices. vbYes triggers the “all sheets” path.
vbNo triggers the “pick sheets” path — it asks for a comma-separated list in
a follow-up InputBox. vbCancel exits cleanly without creating anything.
This pattern — one MsgBox branching into different code paths — is how you build flexible macros without forcing the user into the VBA editor.
#ws.Copy is the engine
VBA’s Worksheet.Copy method, called with no arguments, does three things at
once: it copies the sheet, creates a brand new workbook containing only that
sheet, and makes the new workbook the active workbook. It’s the equivalent of
right-click → Move or Copy → New workbook → OK, but from code.
ws.Copy ' Sheet → new workbook
ActiveWorkbook.SaveAs outFolder & "\" & _
safeName & ".xlsx", xlOpenXMLWorkbook ' Save as .xlsx
ActiveWorkbook.Close False ' Close without saving again
After ws.Copy, ActiveWorkbook refers to the new single-sheet workbook
(not the original). The macro saves it immediately, closes it, and loops to the
next sheet. The original workbook never loses focus — you won’t even see the
new workbooks flash because ScreenUpdating is off.
The False parameter on .Close means “don’t save changes” — because we
already saved with SaveAs. Without this, Excel would ask “Save changes?” for
every file. With DisplayAlerts = False, it would close without asking, but
explicitly providing False is cleaner.
#Filename sanitization: the 7 illegal characters
Windows filenames can’t contain \/?:*[]". Excel sheet names CAN contain some
of these — especially brackets [ ] (used for grouping in multi-entity
workpapers) and slashes / (used in date-formatted tab names).
If you try to SaveAs with a filename containing [, Excel raises error 1004
and the macro stops. The fix is a chain of Replace() calls that swap each
illegal character for an underscore:
safeName = Replace(Replace(Replace(Replace( _
Replace(Replace(Replace(ws.Name, _
"/", "_"), "\", "_"), "?", "_"), _
"*", "_"), "[", "_"), "]", "_"), ":", "_")
This converts Sched-A [Final] to Sched-A _Final_ and FY 2023/2024 to
FY 2023_2024. The output filename never matches the original sheet name
exactly — it’s close enough that anyone can identify which file came from which
sheet.
Seven characters, seven Replace calls, one safeName. This appears twice in
the code (once in each branch) because the “all sheets” and “pick sheets” paths
operate on different loop structures but need the same sanitization.
#Why .xlsx and not .xlsm
The macro exports to xlOpenXMLWorkbook (.xlsx), not xlOpenXMLWorkbookMacroEnabled
(.xlsm). This is deliberate:
- Security. The exported files are going to entity owners, clients, and
partners. You don’t want to ship VBA code when the recipient only needs data.
.xlsxfiles can’t contain macros — they’re safer by design - Surface area. If your original workbook has 17 modules of tax calculation
VBA, exporting to
.xlsmwould copy all of it into every output file, multiplying the total file size by the number of sheets..xlsxstrips the code, keeping each file lean - Compatibility.
.xlsxopens on macOS, Excel Online, and Google Sheets without security warnings..xlsmrequires macro support
The tradeoff: any formulas that reference other sheets in the original workbook
will become #REF! in the exported file (because those sheets no longer exist
in the single-sheet workbook). The exported file is a data snapshot, not a
functional model. If you need cross-sheet formulas to survive the export, use
the paste-values-everywhere macro first to convert formulas to values, then
run this macro.
#The MkDir guard with On Error Resume Next
MkDir creates a folder. If the folder already exists, MkDir raises error 75
(“Path/File access error”) and the macro would jump to CleanUp. But the folder
already existing is not an error — it’s the expected state when you run the
macro a second time.
On Error Resume Next
MkDir outFolder
On Error GoTo CleanUp
This three-line pattern says: “try to create the folder, ignore the error if it
already exists, then restore normal error handling.” It’s one of the few
places in this blog’s macros where On Error Resume Next is appropriate —
because we’re not ignoring a problem, we’re handling a specific, expected
condition that VBA’s API can’t distinguish from a real error.
#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.