Custom environments (\newenvironment)

When you wrap a region in the same formatting again and again, you can build your own \begin{name}…\end{name}. The foundation is \newenvironment. If a macro is a do-it-yourself *command*, an environment is its block counterpart. This page walks through the whole basis for defining environments: the separate code that runs at the start and at the end, how to pass arguments and the pitfall that comes with them, redefining an existing environment, and the modern \NewDocumentEnvironment.

Defining an environment with \newenvironment

The basic tool is **\newenvironment{name}{⟨begin-def⟩}{⟨end-def⟩}. The first argument is the name of the environment you want (no backslash); the next two are the code to run at the start and the code to run at the end**. When LaTeX meets \begin{name} it runs ⟨begin-def⟩; when it meets \end{name} it runs ⟨end-def⟩; and the body wrapped between them is typeset in the usual way.

latex
% プリアンブルで定義 / define in the preamble
\newenvironment{warning}{%
  \par\noindent\textbf{注意:}\itshape
}{%
  \par
}

% 本文で使う / use it in the body
\begin{warning}
  この操作は元に戻せません。
\end{warning}

A decisive property: an environment is itself an implicit group — a local scope. The LaTeX rules state that “the ⟨begin-def⟩ code, the environment body, and the ⟨end-def⟩ code are all processed within a group.” So any change to fonts or spacing you make inside ⟨begin-def⟩ is undone automatically at \end{name} and never leaks into the surrounding text. That is why, in the example above, only the body comes out italic even though we never switch \itshape (italics) back off.

This automatic grouping is a real convenience when you want to confine a font or spacing change to a fixed span. Doing the same with a macro (\newcommand) means wrapping the contents in { … } yourself to form a group; with an environment, \begin\end already plays that role.

Environments with arguments — and why the end code can’t see them

To vary the contents at each call, give the environment arguments. Exactly as with \newcommand, you write the number of arguments in square brackets after the name and refer to them inside ⟨begin-def⟩ as **#1, #2, and so on: \newenvironment{name}[⟨nargs⟩]{⟨begin-def⟩}{⟨end-def⟩}** (from #1 through #9, nine at most).

latex
% 見出しの語を引数で受け取る / take the heading word as an argument
\newenvironment{point}[1]{%
  \par\noindent\textbf{#1}\quad
}{%
  \par
}

\begin{point}{結論}
  早めにバックアップを取ること。
\end{point}

And just as with \newcommand, you can make the first argument optional, with a default value. Doubling the brackets — **\newenvironment{name}[⟨nargs⟩][⟨default⟩]{...}{...}** — makes #1 an optional argument whose default is ⟨default⟩. You then open it as \begin{name} (which uses the default) or \begin{name}[x] (which sets #1 to x). As with \newcommand, the ⟨nargs⟩ in [⟨nargs⟩] is the total number of arguments, including the optional one.

Here lies the pitfall unique to environments — and the biggest one. **The arguments #1, #2, … may be used only in ⟨begin-def⟩; you cannot use them in ⟨end-def⟩.** The rules say so outright: the end code “may not contain any parameters; that is, you cannot use #1, #2, etc., here.” In a word, by the time \end{name} runs the arguments are no longer around. Writing #1 in ⟨end-def⟩ does not expand the argument — it raises an error.

If you do need an argument’s value at the end, the standard move is to **save that value while you are still in ⟨begin-def⟩. For text, stash it in a box** (reserve one with \newsavebox and fill it with \sbox), or have a macro remember it via \newcommand/\def, and retrieve it in ⟨end-def⟩. Because the whole environment is one group, whatever you saved in ⟨begin-def⟩ survives to ⟨end-def⟩. The example below prints an attribution at the end of a quotation: it takes the source as #1 (default Shakespeare), saves it in a box \quoteauthor, and uses it at the close.

latex
\newsavebox{\quoteauthor}
\newenvironment{citequote}[1][Shakespeare]{%
  \sbox\quoteauthor{#1}%   begin-def で引数を保存 / save the arg here
  \begin{quotation}
}{%
  \hspace{1em plus 1fill}---\usebox{\quoteauthor}%   end-def で取り出す
  \end{quotation}%
}

\begin{citequote}
  To be, or not to be.
\end{citequote}

\begin{citequote}[Knuth]
  Premature optimization is the root of all evil.
\end{citequote}

\renewenvironment and names that already exist

Environments have the same safety net as \newcommand: **\newenvironment succeeds only if the name is not already defined.** Use it on an environment that already exists and it stops with LaTeX Error: Command \name already defined. (an environment name is backed internally by a command \name, hence the wording). This is deliberate, to keep you from silently clobbering an existing environment.

To rework an existing environment, use **\renewenvironment**. It takes arguments exactly like \newenvironment (including [⟨nargs⟩][⟨default⟩]); only the behavior differs. \renewenvironment errors if the target is not already defined — the mirror image of \newenvironment. You reach for it to replace the contents of a built-in environment such as quote or itemize.

latex
% 既存の quote を、本文を斜体にする版へ作り直す
\renewenvironment{quote}{%
  \list{}{\rightmargin\leftmargin}\item\relax\itshape
}{%
  \endlist
}

A few rules apply. First, **an environment name may not begin with the string end** (processing \end{...} uses an internal command named \end<name>, so this avoids a clash). Second, \newenvironment and \renewenvironment each have a starred form, written with a * after the name, which differs in how it treats trailing spaces in arguments (the unstarred form is usually all you need). There is no standard “define only if absent” counterpart to \providecommand for environments — if you need that, use the modern interface in the next section.

Common patterns

In practice, defining your own environment tends to fall into three patterns. All share the same skeleton: set things up in ⟨begin-def⟩, then tidy up in ⟨end-def⟩.

  • Wrap content in formatting — set the font, size, or alignment in ⟨begin-def⟩ and let the body take that look. Thanks to grouping, ⟨end-def⟩ can be empty and the change still reverts automatically.
  • Add space before and after — put vertical space such as \par\medskip at the start of ⟨begin-def⟩ and the end of ⟨end-def⟩, framing the body with margins above and below.
  • Build on an existing environment — open another environment with \begin{...} in ⟨begin-def⟩ and close the matching \end{...} in ⟨end-def⟩. This is how you season quote, center, or list with a little extra.

The third — building on an existing environment — is the most common. A smallquote environment, say, that reuses quotation but sets it in smaller type, simply switches on \small and opens \begin{quotation} in ⟨begin-def⟩, then closes \end{quotation} in ⟨end-def⟩. The underlying environment’s behavior (its indentation and margins) carries over unchanged.

latex
% 1) 書式で包む / wrap in formatting
\newenvironment{aside}{\par\small\itshape}{\par}

% 2) 前後に空きを入れる / add space around
\newenvironment{spaced}{\par\medskip\noindent}{\par\medskip}

% 3) 既存の環境の上に作る / build on quotation
\newenvironment{smallquote}{%
  \small\begin{quotation}
}{%
  \end{quotation}
}

Take care to close in ⟨end-def⟩ whatever you opened in ⟨begin-def⟩, keeping the pairing balanced. It also helps to end code lines with % so that stray spaces at the end of ⟨begin-def⟩ or a code line do not creep into the output — that is why each line in the multi-line examples above ends with %.

The modern \NewDocumentEnvironment

Modern LaTeX offers one more, more powerful way: **\NewDocumentEnvironment{name}{⟨arg-spec⟩}{⟨begin-code⟩}{⟨end-code⟩}**. The counterpart to \NewDocumentCommand on the macro side, it was originally a feature of the xparse package but has been part of the LaTeX kernel since October 2020, available without \usepackage. Instead of a number of arguments, you declare an argument specification (arg-spec): a string of letters such as m (mandatory), o (optional), O{default} (optional with a default), and s (an optional star) — the “xparse” page covers the specifiers in detail.

This interface beats the old \newenvironment in two ways. First, it can take several optional arguments and handle starred variants properly. Second, and decisively, **the arguments are available in ⟨end-code⟩ as well as ⟨begin-code⟩.** The kernel docs state it plainly: “Both the ⟨beg-code⟩ and ⟨end-code⟩ may access the arguments as defined by ⟨arg spec⟩.” That removes the need for the saved-box dance shown earlier.

latex
% #1 を begin でも end でも使える / #1 usable in both begin and end
\NewDocumentEnvironment{citequote}{O{Shakespeare}}{%
  \begin{quotation}
}{%
  \hspace{1em plus 1fill}---#1%
  \end{quotation}%
}

\begin{citequote}[Knuth]
  Premature optimization is the root of all evil.
\end{citequote}

\NewDocumentEnvironment also comes with the family that mirrors \newenvironment’s siblings: **\RenewDocumentEnvironment (redefine), \ProvideDocumentEnvironment (define only if absent), and \DeclareDocumentEnvironment (define regardless).** For new code, this modern interface is the recommended choice, both for its flexible argument handling and because its end code can see the arguments.