Includes

You can include content from another file into the current AsciiDoc document using the include directive. The included content can be AsciiDoc or it can be any other text format. Where that content is included in the document determines how it will be processed.

What is an include directive?

An include directive imports content from a separate file or URL into the content of the current document. When the current document is processed, the include directive syntax is replaced by the contents of the include file. Think of the include directive like a file expander. The include directive is a preprocessor directive, which means it has no awareness of the surrounding context.

When is an include directive useful?

The include directive is useful when you want to:

  • Partition a large document into smaller files for better organization and to make restructuring simpler.[1]

  • Insert source code from the external files where the code is maintained.

  • Populate tables with output, such as CSV data, from other programs.

  • Create document variants by combining the include directive with conditional preprocessor directives.

  • Reuse content snippets and boilerplate content, such as term definitions, disclaimers, etc., multiple times within the same document.

Include directive syntax

An include directive must be placed on a line by itself with the following syntax:

include::target[leveloffset=offset,lines=ranges,tag(s)=name(s),indent=depth,opts=optional]

The target is required. The target may be an absolute path, a path relative to the current document, or a URL. Since the include directive is a line-oriented expression, the target may contain space characters. However, the target must not start with a space character (since that would turn it into a description list term). An absolute or relative path outside the directory of the outermost document will only be honored if the safe mode is unsafe. A URL target will only be resolved if the security settings on the processor allows it (e.g., allow-uri-read). See Include Content by URI.

The leveloffset, lines, tag(s), indent, and opts attributes are optional, thus reducing the simplest case to the following:

include::partial.adoc[]

When using consecutive include directives, you should always separate them by an empty line unless your intention is to adjoin the content in the include files so it becomes contiguous.

For example, if you’re using the include directive to include individual chapters, the include directives should be offset from each other by an empty line. This strategy avoids relying on empty lines imported from the include file to keep the chapters separated. That separation should be encoded in the parent document instead.

include::chapter01.adoc[]

include::chapter02.adoc[]

include::chapter03.adoc[]

On the other hand, if you’re using the include directive to lay down contiguous lines, such as common document attribute entries, then you would put the include directives on adjacent lines to avoid inserting empty lines.

= Document Title
Author Name
include::attributes-settings.adoc[]
include::attributes-urls.adoc[]
:url-example: https://example.org

Document body.

In either case, don’t rely on the empty lines at the boundaries of the include file. And mind where empty lines are used in that include file.

Include processing

Although the include directive looks like a block macro, it’s not a macro and therefore isn’t processed like one. It’s a preprocessor directive; it’s important to understand the distinction.

A preprocessor directive is processed when the lines of a document are read, but before the document structure is parsed. Therefore, it’s not aware of the surrounding document structure. A preprocessor directive merely adds lines to the reader or takes lines away. The include directive is a preprocessor directive that always adds lines.

The best way to think of the include directive is to imagine that it is being replaced by the lines from the include file (i.e., the imported lines). Only after the lines from the target of the include directive are added to the current document does the parser read and interpret those lines.

The include directive is disabled when Asciidoctor is run in secure mode. In secure mode, the include directive is converted to a link in the output document. See Safe Modes to learn more.

Escaping an include directive

If you don’t want the include directive to be processed, you must escape it using a backslash.

\include::just-an-example.ext[]

Escaping the directive is necessary even if it appears in a verbatim block since it’s not aware of the surrounding document structure.

Include file resolution

The path used in an include directive can be relative or absolute.

If the path is relative, the processor resolves the path using the following rules:

  • If the include directive is used in the primary (top-level) document, relative paths are resolved relative to the base directory. (The base directory defaults to the directory of the primary document and can be overridden from the CLI or API).

  • If the include directive is used in a file that has itself been included, the path is resolved relative to the including (i.e., current) file.

These defaults make it easy to reason about how the path to the include file is resolved.

If the processor cannot locate the file (perhaps because you mistyped the path), you’ll still be able to convert the document. However, you’ll get the following warning message during conversion:

asciidoctor: WARNING: my-document.adoc: line 3: include file not found: /.../content.adoc

The following message will also be inserted into the output:

Unresolved directive in my-document.adoc - include::content.adoc[]

To fix the problem, edit the file path and run the converter again. If you don’t want the AsciiDoc processor to emit a warning, but rather drop the include that cannot be found, add the opts=optional attribute to the include directive.

If you store your AsciiDoc files in nested folders at different levels, relative file paths can quickly become awkward and inflexible. A common pattern to help here is to define the paths in attributes defined in the header, then prefix all include paths with a reference to one of these attributes:

:includedir: _includes
:sourcedir: ../src/main/java

include::{includedir}/fragment1.adoc[]

[source,java]
----
include::{sourcedir}/org/asciidoctor/Asciidoctor.java[]
----

Keep in mind that no matter how Asciidoctor resolves the path to the file, access to that file is limited by the safe mode setting under which Asciidoctor is run. If a path violates the security restrictions, it may be truncated.

AsciiDoc vs non-AsciiDoc files

The include directive performs a simple file merge, so it works with any text file. The content of all included content is normalized. This means that the encoding is forced to UTF-8 (or converted from UTF-16 to UTF-8 if the file contains a BOM) and trailing whitespace and endlines are removed from each line and replaced with a Unix line feed. This normalization is important to how an AsciiDoc processor works.

If the file is recognized as an AsciiDoc file (i.e., it has one of the following extensions: .asciidoc, .adoc, .ad, .asc, or .txt), the AsciiDoc processor runs the preprocessor on the lines, looking for and interpreting the following directives:

  • includes

  • preprocessor conditionals (e.g., ifdef)

This allows includes to be nested, and provides lot of flexibility in constructing radically different documents with a single primary document and a few command line attributes.

Including non-AsciiDoc files is normally done to merge output from other programs or populate table data:

.2016 Sales Results
,===
include::sales/2016/results.csv[]
,===

In this case, the include directive does not do any processing of AsciiDoc directives. The content is inserted as is (after being normalized).


1. Always separate consecutive include directives by an empty line unless your intent is to adjoin the content in the include files so it becomes contiguous.