The LaTeX team began work on a general argument parser, xparse, in the late 1990s; its centrepiece, \NewDocumentCommand, only graduated from an experimental package into the LaTeX kernel with the 2020-10-01 release. Two decades is a long apprenticeship, and there is a reason for it. \newcommand can only count how many arguments a command takes, whereas \NewDocumentCommand describes what kind each one is, as a string called the argument specification (arg-spec). The difference between counting and describing buys you starred variants, several independent optional arguments, and arguments fenced by delimiters of your own choosing — syntaxes \newcommand cannot express at all. This page walks through what each specifier letter promises, when to reach for \IfNoValueTF rather than \IfBooleanTF, and when not to reach for any of it.
What \newcommand cannot express: one optional argument, first position only
\newcommand can build exactly one shape: at most one bracketed optional argument, and only in first position, followed by zero or more mandatory ones. Anything richer, LaTeX News 32 records, required dropping down to the TeX \def primitive and low-level macro programming. That is why the sources of older packages are full of hand-written machinery for peeking at the next token — \@ifstar to spot a star, \@ifnextchar to spot any given character. Because those names contain @ they have to be wrapped in \makeatletter, they break easily around spaces and nesting, and nobody enjoys reading them.
\NewDocumentCommand replaces all that peeking with a declarative grammar. You hand it a string of letters instead of a number; the parser reads the input and always delivers normalized arguments to your body as #1, #2, and so on. The interface your users see is thereby separated from the code that implements it. Inside the kernel this machinery lives in a module called ltcmd, and since the 2020-10-01 release \usepackage{xparse} is unnecessary. The xparse package is still on CTAN, but the README of the l3packages bundle that ships it now titles itself “Deprecated” and says the material is retained to support older files. The exception is the deprecated argument types g/G, l and u: reach for one and you get Invalid argument type "g" in command "\zzz" (requires xparse). New code has essentially no reason to want them.
Writing \NewDocumentCommand, and how New, Renew, Provide and Declare differ
The basic form takes three arguments: \NewDocumentCommand{\cmd}{⟨arg-spec⟩}{⟨body⟩} — the command name, the argument specification, and the body, in which the arguments arrive as #1, #2, and so on. Swapping the leading verb changes how the declaration behaves when the name already exists. Aim \NewDocumentCommand at a name that is already taken and it stops with LaTeX cmd Error: Command "\section" already defined. That is a safety net, not an obstacle: to replace an existing definition use \RenewDocumentCommand, and to define only when nothing is there use \ProvideDocumentCommand.
| Definer | Behaviour toward a name that already exists |
|---|---|
\NewDocumentCommand | Stops with an error if it is taken — the default choice |
\RenewDocumentCommand | Errors if the name is not defined; use it to rework an existing command |
\ProvideDocumentCommand | Defines only if nothing is there; how a package fills a compatibility gap |
\DeclareDocumentCommand | Overwrites unconditionally — the official documentation says to use it sparingly |
Commands built by any of these four come with a property you never had to ask for: they are robust from the start. Apply \meaning to one and the terminal shows \protected macro:->…, confirming that the engine-level \protected mechanism of ε-TeX is doing the work. Put such a command in a moving argument — a section heading, a figure caption — and no \protect prefix is needed. Why a \newcommand definition breaks in exactly those places, and what \DeclareRobustCommand was doing about it, belongs to the “Defining macros” page.
The argument specifiers: what m, o, O{}, s, t, r, d, e, v and b mean
An argument specification is a string in which one letter describes one argument, and the specifiers fall into two families. The mandatory family is m, r, R, v, b; the optional family is o, O, d, D, s, t, e, E. One rule runs through the whole set: an uppercase type lets you name a default value, while its lowercase counterpart returns the special marker -NoValue- instead. Read o against O{...}, d against D, e against E, r against R, and the pattern holds every time. Internally, the documentation notes, o, d and O are all shortcuts to an appropriately constructed D-type argument.
| Specifier | Meaning | How it arrives in the body |
|---|---|---|
m | A mandatory argument: braced group or a single token | An ordinary #1, outer braces stripped |
r | r⟨d1⟩⟨d2⟩ — required, fenced by delimiters you choose | -NoValue- after an error if the opener is missing |
R | R⟨d1⟩⟨d2⟩{default} — as r, but with your own recovery value | The default you wrote, if it is missing |
v | A verbatim argument, read like \verb; the delimiter may not be %, \, #, {, } or a space | The literal characters; it cannot appear inside another command’s argument |
b | The body of an environment; only in \NewDocumentEnvironment, and only last | Everything between \begin and \end |
o | The standard optional [...] argument | -NoValue- if it was not supplied |
O | O{default} — o with a default value | The default if absent, so a value is always present |
d | d⟨d1⟩⟨d2⟩ — optional, delimited by characters of your choice | -NoValue- if it was not supplied |
D | D⟨d1⟩⟨d2⟩{default} — d with a default value | The default if absent |
s | Detects a leading star * | Either \BooleanTrue or \BooleanFalse |
t | t⟨char⟩ — tests for one given character; the generalization of s | Either \BooleanTrue or \BooleanFalse |
e | e{⟨tokens⟩} — a set of embellishments such as ^ and _; the tokens must all differ | One argument per token, -NoValue- for each absent one |
E | E{⟨tokens⟩}{⟨defaults⟩} — e with defaults attached | If the default list is shorter, the rest fall back to -NoValue- |
The delimited types (r, R, d, D) come with constraints worth knowing. First, TeX’s grouping characters { and } cannot serve as delimiters: write r{} and you are turned away with LaTeX cmd Error: Argument delimiter "" invalid in command "\zzz". Pick naturally paired characters instead — [], (), <>, "". Second, when the delimiter is a character token the parser memorizes the category code it had at definition time. Change < to a letter later on and the very same < is no longer recognized as a delimiter. A control sequence used as a delimiter (something like \x) is immune to this, because it is identified by name regardless of its current meaning.
% t<char> tests for one character; r()...() is a required delimited argument
\NewDocumentCommand{\pt}{t+ r()}{%
\IfBooleanTF{#1}{\mathbf{(#2)}}{(#2)}%
}
$\pt(1,2)$ % -> (1,2)
$\pt+(3,4)$ % -> (3,4) in bold
% e{^} picks up an optional ^ embellishment wherever it appears
\NewDocumentCommand{\deriv}{e{^} m m}{%
\frac{\mathrm{d}\IfNoValueF{#1}{^{#1}}#3}{\mathrm{d}#2\IfNoValueF{#1}{^{#1}}}%
}
$\deriv{x}{f}$ % -> df/dx
$\deriv^{2}{x}{f}$ % -> d^2 f / dx^2The modifiers +, !, > and = that go in front of a specifier
+ makes an argument long, meaning it may swallow a blank line and thus a paragraph break. Here is the first landmine for anyone arriving from \newcommand: the default is reversed. \newcommand makes every argument long, and you write the starred \newcommand* when you want them short. \NewDocumentCommand does the opposite — arguments are short by default, and you put + in front of each one that should be long. So a freshly ported command handed a body with a blank line in it greets you with ! Paragraph ended before \remark was complete. The per-argument granularity is the point, though: it lets one declaration say “the short title is a single paragraph, the body may run to several.”
The other three are quickly stated. ! forbids a space immediately before an optional argument, and it may only be applied to a trailing optional argument — put it first and you get Invalid argument prefix "!" in command "\remark". It is what you want when the brackets in \foo{x} [x] should be read as ordinary text. > introduces an argument processor: write >{\SplitArgument{2}{;}} m and a;b;c is split into three arguments before your body ever sees it. The kernel ships \SplitArgument, \SplitList, \TrimSpaces, \ProcessList and \ReverseBoolean. = is a newer modifier that forces an optional argument to be interpreted as key–value pairs; it exists so that commands with a long history of taking free text — \caption and the sectioning commands — can grow a keyval interface without breaking the old syntax.
% + makes ONE argument long; ! on a trailing optional argument forbids a space
\NewDocumentCommand{\remark}{+m !o}{\par\textbf{Note.} #1 (#2)\par}
\remark{first paragraph
second paragraph}[tag]
\remark{x} [these brackets stay ordinary text]
% > runs a processor before the body sees the argument
\NewDocumentCommand{\triple}{>{\SplitArgument{2}{;}} m}{\tripleaux#1}
\NewDocumentCommand{\tripleaux}{m m m}{(#1/#2/#3)}
\triple{a;b;c} % -> (a/b/c)\IfNoValueTF versus \IfBooleanTF, and the difference between o and O{}
There are two families of tests, and the specifier decides which one applies. For types that return -NoValue- — o, d, e — use \IfNoValueTF{#1}{⟨if absent⟩}{⟨if present⟩}; for types that return a boolean — s, t — use \IfBooleanTF{#1}{⟨true⟩}{⟨false⟩}. The logically inverted \IfValueTF exists too, and both families come with single-branch forms: \IfNoValueT, \IfNoValueF, \IfValueT, \IfValueF, \IfBooleanT, \IfBooleanF. The reason \IfNoValueTF had to be invented is the interesting part: an omitted optional argument is genuinely a different thing from one supplied as empty. The default-value mechanism of \newcommand cannot express that distinction at all — the default simply appears, and the fact that the user wrote nothing never reaches the body.
-NoValue- is a well-made forgery guard: it is constructed so that it does not match the literal text -NoValue-, which means \IfNoValueTF{-NoValue-} is logically false. String comparison is therefore no substitute — always test with \IfNoValueTF. The classic trap is confusing o with O{}. With o an omitted argument really is -NoValue-, so \IfNoValueTF branches correctly; with O{} a value is always present (an empty one when omitted), so \IfNoValueTF always takes the false branch. And if you forget to test at all, the giveaway is the literal string -NoValue- turning up in the typeset PDF.
So what do you use to see whether an O{} argument is empty? This is exactly where the official recommendation changed in June 2022. The kernel provides \IfBlankTF (with \IfBlankT and \IfBlankF), which reports true when the argument is genuinely empty or contains nothing but blanks. For designs with two optional arguments in a row, the documentation now says that O{} combined with \IfBlankTF is preferable to testing separately for emptiness and for -NoValue-. There is no need to summon expl3’s \tl_if_blank:nTF or etoolbox’s \ifblank. One nuance: \IfBlankTF counts a command such as \space as real content — it prints as a space, but as a token it is substance.
% s = optional star, o = optional [..], m = mandatory
\NewDocumentCommand{\heading}{s o m}{%
\IfBooleanTF{#1}
{\section*{#3}}% starred: unnumbered
{\IfNoValueTF{#2}
{\section{#3}}% no short title given
{\section[#2]{#3}}}% short title for the ToC
}
\heading{A Long Introduction} % numbered section
\heading[Intro]{A Long Introduction} % short title in the table of contents
\heading*{Preface} % unnumbered
% with O{} the value is always there, so test for blankness instead
\NewDocumentCommand{\tagged}{O{} m}{\IfBlankTF{#1}{#2}{[#1] #2}}That heading example hides one more property \newcommand cannot imitate: optional arguments created by \NewDocumentCommand nest safely. In the documentation’s own example, \foo[\baz[stuff]]{more stuff} parses correctly even though an optional argument contains a command that itself takes one. The brackets of a \newcommand definition naively grab everything up to the next ], so the same input is cut short at the inner bracket. The moment you want to put a command with an optional argument inside another optional argument, you have reason enough to move to \NewDocumentCommand.
\NewDocumentEnvironment and the b type: taking the environment body as an argument
Environments get exactly the same machinery through \NewDocumentEnvironment{⟨env⟩}{⟨arg-spec⟩}{⟨start code⟩}{⟨end code⟩} (with \Renew…, \Provide… and \Declare… alongside). The arguments are given right after \begin{⟨env⟩} and are visible to both the start and the end code. And one specifier exists here that has no counterpart for commands: b, the body of the environment itself. Put b at the end of the argument specification and everything between \begin and \end arrives as a single argument, ready to be transformed, typeset twice, or conditionally thrown away.
Three conventions come with b. First, the body has its leading and trailing spaces trimmed by default, so you need not fuss over the whitespace at the ends of lines; write !b if you want that trimming suppressed. Second, use +b if the body may contain several paragraphs. Third — and this one is easy to forget — once b is in play the end code is effectively redundant, but the empty fourth argument still has to be written. Omit it and \NewDocumentEnvironment miscounts its arguments. Environments that use b can be nested inside one another. For the basics of \newenvironment and for plain environments that do not need b, see the “Custom environments” page.
% b grabs the whole body; + allows several paragraphs; the empty 4th
% argument is still required even though there is no end code left to run
\NewDocumentEnvironment{shout}{O{\bfseries} +b}{#1#2}{}
\begin{shout}[\itshape]
Loud and clear.
\end{shout}When you need \NewExpandableDocumentCommand: the start of a tabular cell, and inside \edef
That the standard definer produces robust commands — commands that do not casually expand — is a benefit almost everywhere, and an obstacle in a few places. The most practical example is the start of a tabular cell: the standard tabular machinery requires that any command wrapping \multicolumn be expandable, and a command from \NewDocumentCommand deliberately blocks expansion using an engine feature. The same applies when you need the content settled inside \edef or \write. That is what \NewExpandableDocumentCommand (with its \Renew…, \Provide… and \Declare… siblings) is for. The official documentation is blunt about it, though: use this facility only when necessary, because restrictions come with it.
- If there are arguments at all, the last one must be
m,rorR— one of the mandatory types. - The verbatim type
vis not available, and neither are the>argument processors nor the=keyval modifier. - It cannot tell
\foo[from\foo{[}: either bracket is read as the start of an optional argument, so optional-argument detection is less robust than in the standard version. - The boolean types
sandtdo work, on the other hand.\IfBooleanTFis itself expandable, so the branch resolves as expected even inside\edef.
% a command wrapping \multicolumn must be expandable to work in a cell
\NewExpandableDocumentCommand{\wide}{m}{\multicolumn{3}{c}{#1}}
\begin{tabular}{lcr}
a & b & c \\
\wide{spans three columns} \\
\end{tabular}Which to use: \newcommand or \NewDocumentCommand?
For an abbreviation with no arguments, or one or two mandatory ones, \newcommand is entirely sufficient. Rewriting \newcommand{\R}{\mathbb{R}} as a \NewDocumentCommand buys nothing but extra characters. \newcommand has neither aged nor been deprecated; it remains a first-class LaTeX tool alongside the newer interface. The signals to switch are, happily, quite crisp: when you want a starred variant, when you need a second optional argument, when the input syntax should be something other than [...], and when a command taking an optional argument has to sit inside another optional argument. If any one of those applies, a single line of argument specification is shorter and far more legible than hand-written \@ifstar plumbing.
One closing guideline points the other way. \NewDocumentCommand is a tool for designing input syntax, not a language for writing what the body does. Once you find yourself splitting strings, stacking conditionals or looping after the arguments have arrived, you have crossed into the territory of expl3, the LaTeX3 programming layer — which is, after all, what ltcmd itself is written in. Conversely, when designing user-facing commands inside a package or a class, \NewDocumentCommand is the first choice, because the argument specification doubles as a readable specification of the interface.