TL;DR —
Worksheets.Addreturns the sheet it just created. Capture that return value —Set ws = Worksheets.Add(...)— and you never have to guess which sheet is the new one. The two things that trip everyone up are position and name: a bareWorksheets.Adddrops the sheet to the left of the active sheet (not at the end), and you cannot pass the name in theAddcall — you set.Nameafterwards, where a duplicate, blank, or over-long name raises run-time error 1004.
Sub AddMonthSheet()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets.Add(After:=Worksheets(Worksheets.Count))
ws.Name = "Sep 2026" ' <= 31 chars, unique, no : \ / ? * [ ]
End Sub
Worksheets.Add is the code behind Home ▸ Insert ▸ Insert Sheet and the little + next to the
tabs. It is one of the first things a report macro does, and it fails in the same two places every
time — the sheet appears somewhere you did not expect, or the rename line blows up. Both problems
disappear the moment you treat Add as a factory that hands you the finished sheet, rather than as a
command that leaves you to go find it.
What you'll learn
- The mental model —
Addreturns the new sheet, so capture it and stop usingActiveSheet - The one rule that prevents most bugs — assign the return value to a
Worksheetvariable - Why a bare
Addlands the sheet to the left, and howBeforeandAfterfix it - Why you cannot name the sheet inside
Add, and the rules that make.Namethrow 1004 - How to add a sheet only if it does not already exist
The mental model: Add hands you the sheet
Worksheets.Add is not a fire-and-forget command. It is a function that creates a sheet and returns a
reference to it. The single most common mistake is throwing that reference away:
Worksheets.Add ' the new sheet is created... and then lost
Worksheets(Worksheets.Count).Name = "Data" ' guessing where it went
The second line is a guess. It assumes the new sheet is the last one, which it is not by default, so it renames the wrong tab. Everything downstream — writing headers, formatting, adding formulas — then aims at a sheet chosen by luck. Capture the return value instead and the guesswork is gone for good:
Dim ws As Worksheet
Set ws = Worksheets.Add
ws.Name = "Data" ' this is unambiguously the sheet you just made
Once ws holds the real object, position stops mattering for correctness — you can put the sheet
wherever you like and your code still talks to the right one.
The rule that matters most: assign the return value
Every reliable add-sheet routine starts the same way: Set ws = Worksheets.Add(...). This is the rule
that removes an entire category of bugs, because the two fragile alternatives both break under ordinary
conditions:
ActiveSheetafterAddusually points at the new sheet — but not if an event, aCalculate, or another line moved focus first. It is a convention, not a guarantee.Sheets(Sheets.Count)assumes the new sheet is last, which is only true if you added it there.
The return value is the only reference that is always correct, immediately, no matter where the sheet landed or what runs next. Treat "add a sheet" and "hold on to the sheet" as one indivisible step.
Positioning: Before, After, and why the default lands left
Worksheets.Add takes Before:= and After:= arguments — a sheet to insert in front of, or behind.
Pass at most one. With neither, Excel inserts the new sheet immediately before the active sheet,
which surprises almost everyone the first time: people expect "the end," and instead the tab shows up in
the middle of the workbook.
To append to the end — the position most macros actually want — anchor to the last sheet with After:
Set ws = Worksheets.Add(After:=Worksheets(Worksheets.Count)) ' always last
Set ws = Worksheets.Add(Before:=Worksheets(1)) ' always first
Worksheets(Worksheets.Count) is the idiom for "the last worksheet." Note it counts worksheets, not
all sheets — if the workbook also holds chart sheets, Sheets.Count and Worksheets.Count differ. When
in doubt, position explicitly; never rely on the left-of-active default in code other people will read.
Naming is where Add crashes
There is no Name argument on Add. You create the sheet, then set .Name on the object you captured.
That second step is where the run-time error 1004 lives, because Excel enforces strict rules on sheet
names — and they are the same rules whether you are naming a brand-new sheet or renaming an old one:
- 31 characters maximum
- Must be unique within the workbook (case-insensitive) — a duplicate is the number-one crash
- Cannot be blank
- Cannot contain
: \ / ? * [ ]
Break any of these and .Name = ... raises 1004. The most common trigger is re-running a macro that
already created "Data" once — the second run collides. Because the rules are identical for creating
and renaming, it is worth guarding the name in one place:
Function SafeName(nm As String) As String
nm = Left$(nm, 31)
Dim bad As Variant, ch As Variant
For Each ch In Array(":", "\", "/", "?", "*", "[", "]")
nm = Replace(nm, ch, "-")
Next ch
SafeName = nm
End Function
That handles length and illegal characters; uniqueness you handle by checking first, which is the next section.
Add only if it does not already exist
There is no built-in Exists for sheets, so the re-run collision is yours to prevent. Check the
collection by name before adding, and reuse the sheet if it is already there:
Function GetOrAddSheet(nm As String) As Worksheet
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
If StrComp(ws.Name, nm, vbTextCompare) = 0 Then
Set GetOrAddSheet = ws ' reuse the existing one
Exit Function
End If
Next ws
Set GetOrAddSheet = ThisWorkbook.Worksheets.Add(After:=Worksheets(Worksheets.Count))
GetOrAddSheet.Name = nm ' free to name - we know it is unused
End Function
This is the pattern that makes an add-sheet macro idempotent — safe to run twice — instead of one that
works the first time and throws 1004 forever after. The judgment: never call Add and .Name blindly
in a routine that might run more than once; look the name up first, and either reuse or create.
How ExcelMaster helps
Adding a sheet looks like one line and hides three decisions — capture the return value, choose a position, and produce a legal, unique name — and getting any of them wrong fails quietly (wrong tab renamed) or loudly (error 1004 on the second run).
ExcelMaster lets you say what you
want — "add a monthly sheet at the end named for the current month, and reuse it if it is already there"
— and it writes the Set ws = Worksheets.Add(After:=...) capture, appends in the right place, sanitizes
the name against the 31-character and illegal-character rules, and checks the collection first so a
second run reuses the sheet instead of crashing. You keep the workbook and the code.
Frequently asked questions
How do I add a sheet and name it in VBA?
Capture the return value and set .Name on it: Set ws = Worksheets.Add(After:=Worksheets(Worksheets.Count))
then ws.Name = "Data". There is no Name argument on Add itself. The name must be 31 characters or
fewer, unique in the workbook, non-blank, and free of : \ / ? * [ ], or .Name raises run-time error
1004.
Why does my new sheet appear in the wrong position?
Because a bare Worksheets.Add inserts the sheet immediately before the active sheet, not at the end.
Control it with Before:= or After:=. To append to the end, use
Worksheets.Add(After:=Worksheets(Worksheets.Count)); to put it first, use
Worksheets.Add(Before:=Worksheets(1)).
Why do I get error 1004 when adding or naming a sheet?
Almost always a name-rule violation, and usually a duplicate. Sheet names must be unique, so re-running a
macro that already created "Data" collides on the second run. Names must also be 31 characters or
fewer, non-blank, and free of : \ / ? * [ ]. Check the collection for the name before adding, and
sanitize the string first.
How do I add a sheet only if it does not already exist?
There is no Exists method, so loop the Worksheets collection and compare names with
StrComp(ws.Name, nm, vbTextCompare) = 0. If a match is found, reuse that sheet; otherwise call Add
and name it. Wrapping this in a GetOrAddSheet function makes the macro safe to run repeatedly.
How do I add a sheet at the very end of the workbook?
Anchor to the last worksheet: Worksheets.Add(After:=Worksheets(Worksheets.Count)).
Worksheets(Worksheets.Count) is the last worksheet, so inserting after it appends to the end. Use
Sheets.Count instead of Worksheets.Count only if you deliberately want to count chart sheets too.
Tested in
Tested in: Excel 365 (Windows 11), VBA 7.1 — last verified 2026-09-14.
Related guides: VBA Copy Sheet · VBA Delete Sheet · VBA Worksheets · VBA Worksheet · VBA ThisWorkbook
