Multi-file projects (\input / \include / subfiles)

Split a multi-file LaTeX project across a dozen .tex files and the compiler will not blink: pdflatex main.tex reads them all and produces one PDF. It is the editor that loses track. Press the build key with chapter three in front of you and back comes ! LaTeX Error: Missing \begin{document}., because the editor obediently typeset the file you were looking at. The fix is a single line at the top of every chapter, % !TEX root = ../main.tex, and the curious thing about it is that LaTeX never reads that line at all. It is a comment, addressed to your editor rather than to the compiler. This page is about that second layer of a split project: which editors read the magic comment and what the rest use instead, how SyncTeX finds its way back into the right chapter file, where the build files actually land, and what breaks when the file in front of you is not the main one. The commands that do the splitting — \input, \include, \includeonly — are a separate story, linked at the foot of the page.

% !TEX root: building the main file while a chapter is open

Put % !TEX root = ../main.tex at the top of every file that is not the main file and the build key does the right thing no matter which one is in front. Two details from TeXShop’s own documentation are worth having, because both of them catch people. First, the line has to appear within the first twenty lines of the file — bury it under a long licence header and it is simply not seen. Second, the path is resolved relative to the file the line sits in, not relative to the project root: a chapter living in chapters/ needs ../main.tex, not main.tex. An absolute path works too, at the price of making the project unmovable. The main file itself needs no line; it is already the root.

text
thesis/
  main.tex                 <- the root; needs no magic comment
  chapters/
    03-results.tex         <- carries the line below
latex
% chapters/03-results.tex -- the first lines of the file
% !TEX root = ../main.tex     % relative to THIS file, not to thesis/
% !TEX TS-program = xelatex   % pin the engine for the whole project
% !TEX encoding = UTF-8 Unicode

\chapter{Results}

The line has a predecessor, and why the successor won is instructive. TeXShop used to offer a menu command called “Set Project Root…”, which recorded the answer in a sidecar file next to the chapter: two.tex acquired a two.texshop. Throw that invisible file away and TeXShop went straight back to typesetting the chapter. TeXShop’s documentation now describes the command as removed from the menus because the % !TEX root method is more robust, and the reason is contained in that one word. A line inside the file travels with the file. It survives a copy, a rename of the enclosing folder, a Git clone, and a co-author who has never opened your editor. Configuration parked beside a file always eventually loses the file.

Why LaTeX itself never reads % !TEX root

Because % starts a comment, and comments are thrown away by TeX’s scanner before anything else happens. pdflatex, xelatex and lualatex see nothing whatever on that line. It is a message from one program (your editor) to another program (your editor’s build command) that merely happens to travel through the source file. Two practical consequences follow. First, nothing will ever warn you that the line is wrong. Point it at a file that does not exist and the editor quietly falls back on its own guess, usually the file you have open, and you are looking at ! LaTeX Error: Missing \begin{document}. again. Second, a build that runs from a terminal or in CI — latexmk main.tex, a Makefile, a GitHub Actions step — names the main file on the command line and ignores the magic comment entirely. The line is a convenience for interactive editing, not part of the project’s definition.

There is exactly one comment TeX does read, and it is worth knowing about so the two never get confused. If the very first line of the main input file begins with %&, the engine itself parses it to choose a format%&pdflatex, %&latex — behaviour that the tex manual page describes as controlled by the -parse-first-line option and the parse_first_line configuration variable. That one descends from TeX’s own format-loading machinery and lives in the engine. Everything spelled % !TEX ... lives in the editor. The visual resemblance is a coincidence of both wanting to hide instructions somewhere LaTeX would not trip over them.

latex
% main.tex -- first line only: the ENGINE parses this one
%&pdflatex

% chapters/03-results.tex -- the engine never sees this one
% !TEX root = ../main.tex

Which editors read % !TEX root, and what the rest use instead

TeXShop, TeXworks, TeXstudio and VS Code with the LaTeX Workshop extension all read the line; Emacs with AUCTeX uses a file-local variable of its own, and Overleaf takes the answer from a project setting rather than from the source. LaTeX Workshop is the one worth studying, because it documents its entire decision procedure: it looks first at the magic comment in the active editor, then at whether the active file itself contains \documentclass or \begin{document}, then scans the .tex files at the workspace root for one that pulls the active file in, then recognises the subfiles pattern \documentclass[main.tex]{subfiles}, and finally falls back on the .fls file list left by the last compile. The magic comment wins because it is consulted first — and if you ever want it not to, the setting is latex-workshop.latex.build.enableMagicComments.

EditorWhat it readsNote
TeXShop% !TEX rootwhere the directive comes from; siblings % !TEX TS-program, encoding, spellcheck
TeXworks% !TEX rootadopted the same magic-comment scheme
TeXstudio% !TeX rootdetects the root automatically first; the line overrides that
LaTeX Workshop% !TEX rootfor VS Code; first of five fallback steps, switchable via latex-workshop.latex.build.enableMagicComments
AUCTeXTeX-masterfor Emacs; a file-local variable, conventionally at the end of the file
Overleafa project settingchosen from the project menu as the main document; nothing is stored in the source

Emacs is the interesting outlier. AUCTeX asks the same question but stores the answer as a file-local variable, conventionally in a block at the end of the file. Since each editor reads only its own convention, carrying both costs nothing: the AUCTeX block is an ordinary comment to every other editor, and % !TEX root is an ordinary comment to Emacs. Shared repositories often end up with chapter files that have both, which is correct and costs two lines. Overleaf sits outside the argument altogether. The main document is a property of the project, set from the project menu, so there is nothing in the source that can drift out of sync — and equally, nothing that comes along with the file when you download the project and open it locally.

latex
% chapters/03-results.tex -- both conventions in one file
% !TEX root = ../main.tex

\chapter{Results}

%%% Local Variables:
%%% mode: latex
%%% TeX-master: "../main"
%%% End:

SyncTeX across files: why clicking the PDF opens the right chapter

Because SyncTeX records, for every box on every page, which input file and which line it came from. Double-click a paragraph of chapter three in the PDF and chapters/03-results.tex opens, not main.tex. Switch it on with -synctex=1 and you get exactly one main.synctex.gz at the project root, named after the root file. There is no per-chapter synctex file: a single index covers the whole project, which is precisely why it can point at any file inside it.

terminal
$ pdflatex -synctex=1 main.tex
$ synctex edit -o "2:100:200:main.pdf"
SyncTeX result begin
Output:main.pdf
Input:./chapters/ch2.tex
Line:2
SyncTeX result end

Trying the command-line client once makes the mechanism concrete. synctex edit takes a page and a point in the PDF and returns a file name and a line number; synctex view goes the other way, from a source line to a place on the page. The Synchronize TeXnology behind it is, in the words of its own manual page, essentially due to Jérôme Laurens, and it is now maintained as part of TeX Live. TeXShop’s documentation makes the connection to the previous section explicit: it is the % !TEX root line that lets a reverse-search click open and activate the correct chapter window instead of dumping you in the main file. The two features are usually configured together for exactly that reason.

The trap arrives when you compile a chapter on its own. An engine run drops its by-products into the working directory the build ran from, not next to the input file. Run pdflatex -synctex=1 chapters/03-results.tex from the project root and 03-results.synctex.gz appears at the root, right beside main.synctex.gz. Now two indexes describe the same source lines, and one of them points into a one-chapter PDF that starts at page 1. Which one the viewer happens to read decides where your click lands, and the page numbers stop agreeing. When you go back to building the whole book, delete the PDF and synctex file the chapter-level build left behind.

Where the build files land, and what to put in .gitignore

\include writes one .aux per chapter, and it writes it next to the chapter file. Build a project containing chapters/01-intro.tex and you will find chapters/01-intro.aux sitting beside it. Everything else stays at the root next to the main file: main.aux, main.log, main.toc, main.out, main.synctex.gz, and with latexmk, main.fls and main.fdb_latexmk. The generated files are not in one place; they are sprinkled thinly across the whole source tree.

text
thesis/
  main.tex  main.pdf
  main.aux  main.log  main.toc  main.out
  main.synctex.gz  main.fls  main.fdb_latexmk
  chapters/
    01-intro.tex   01-intro.aux    <- one .aux per \include, here
    02-method.tex  02-method.aux

For Git this is less trouble than it looks, because a .gitignore pattern containing no slash matches at any depth: a plain *.aux line already covers chapters/01-intro.aux. What does not cover it is a root-anchored /*.aux, and neither does the habit of cleaning up with rm *.aux in the project root. Nor, more surprisingly, does latexmk: tested on TeX Live 2024, latexmk -c and even latexmk -C remove the root-level intermediates and leave chapters/*.aux behind. So when you suspect a stale .aux — the kind of bug where the error points at a chapter you never touched — clean them out explicitly, with something like find . -name "*.aux" -delete.

There is one place where the split genuinely breaks a tool: -output-directory. Ask for an out-of-tree build with pdflatex -output-directory=build main.tex on a project that uses \include and the run dies. TeX tries to open build/chapters/01-intro.aux, the subdirectory does not exist, and you get ! I can't write on file followed by a fatal error and no PDF at all. TeX does not create directories. There are two fixes: dig the mirrored subdirectories yourself beforehand, or hand the job to latexmk -outdir=build, which creates them for you. That is why multi-file projects that build out of tree are almost always driven by latexmk rather than by the engine directly.

terminal
# fails: TeX will not create build/chapters/ by itself
pdflatex -output-directory=build main.tex
# ! I can't write on file ... chapters/01-intro.aux

# works: mirror the subdirectories first
mkdir -p build/chapters && pdflatex -output-directory=build main.tex

# works: latexmk creates them for you
latexmk -pdf -outdir=build main.tex

That same .fls file is what makes on-save rebuilds work across a split project. Run with -recorder — which latexmk adds for you — and the engine records every file it opened, so main.fls carries an INPUT chapters/01-intro.tex line for each chapter. latexmk keeps the resulting dependency list in main.fdb_latexmk and watches all of it, which is why saving chapter three rebuilds the book without your ever having told it that chapter three is part of the book. The structure of the split never has to be declared twice: the list of \include lines is already the dependency declaration.

What breaks when the file you have open is not the main file

Three symptoms, and they look nothing like one another. First, an ordinary chapter file compiled on its own stops immediately: ! Undefined control sequence. on the first \chapter, then ! LaTeX Error: Missing \begin{document}., then ! Emergency stop. and no PDF — inevitable for a file with no \documentclass. Second, a subfiles chapter compiled on its own is worse, because it succeeds: you get a plausible one-chapter PDF that starts at page 1 and whose cross-references into other chapters print as ??. Third, a run started from the wrong working directory fails on its images instead, because every relative path in the project resolves from where the build ran, not from where the file lives.

  • The build key typesets the wrong file → put % !TEX root in every non-main file, within the first twenty lines, with the path written relative to that file.
  • ! LaTeX Error: Missing \begin{document}. → you are compiling a chapter directly; that file has no preamble and is not meant to have one.
  • Images vanish, or the run stops on a missing file → the build is running somewhere other than the project root; relative paths resolve from the working directory.
  • Clicking in the PDF opens the main file instead of the chapter → that run had no -synctex=1, or the viewer is reading a stale .synctex.gz.
  • Stray .log and .pdf files appear at the root after a failed experiment → an engine run writes its by-products into the working directory, not next to the input file.

In the end two habits keep this layer quiet. One: no spaces in file or folder names. Every tool on this page eventually hands the path to a shell or to a %-delimited magic comment, and a space is where quoting bugs live. Two: always start the build from the project root, whether by hand, from a Makefile, or by letting the editor do it. The working directory is the single reference point that \includegraphics, \include and -output-directory all share, so when it slips, all three slip together. Get those two right and the multi-file layer becomes invisible — which is the only state in which it is doing its job.