---
title: Mapping AsciiDoc includes and level offsets to Jinja
description: Includes map to Jinja natively. Level offsets do not exist in Jinja at all, so DogsBay extends it — and the extension has to be fence-aware.
created: "2026-08-22"
author: Gabriel McGoldrick
tags:
  - topic/asciidoc
  - topic/jinja
---

# Mapping AsciiDoc includes and level offsets to Jinja {#mapping-asciidoc-includes-and-level-offsets-to-jinja}

Jinja natively supports including text files with `{% include ... %}`:

:::note{title="AsciiDoc to Markdown — part 7 of 7"}
1. [Migrating AsciiDoc to Markdown](/blog/migrating-asciidoc-to-markdown/)
2. [Why existing AsciiDoc converters lose your structure](/blog/why-existing-converters-lose-structure/)
3. [Why Jinja is the right target](/blog/why-jinja-is-the-right-target/)
4. [Mapping AsciiDoc variables to Jinja](/blog/asciidoc-variables-in-jinja/)
5. [Mapping AsciiDoc conditionals to Jinja](/blog/asciidoc-conditionals-in-jinja/)
6. [Mapping AsciiDoc ifeval to Jinja](/blog/asciidoc-ifeval-in-jinja/)
7. **Mapping AsciiDoc includes and level offsets to Jinja** — you are here
:::

- AsciiDoc:

  ```asciidoc
  include::snippets/deployment-config-deprecated.adoc[]
  ```
- Jinja:

  ```jinja
  {% include "./snippets/deployment-config-deprecated.md" %}
  ```

That much is free. Level offsets are not.

## Level offsets {#level-offsets}

By default Jinja does not support level offsets — shifting heading levels up or down when merging files together.

Level offsets solve a real problem in technical writing: modular reuse. They let you write a file as a standalone document, with its own primary `= Title`, and then embed it as a nested subsection inside a master book or assembly without breaking the global table of contents.

DogsBay extends Jinja with `{% leveloffset ... %}` and `{% endleveloffset %}`:

- AsciiDoc:

  ```asciidoc
  include::modules/ldap-syncing-about.adoc[leveloffset=+1]

  include::modules/ldap-syncing-config-rfc2307.adoc[leveloffset=+2]

  include::modules/ldap-syncing-config-activedir.adoc[leveloffset=+2]

  include::modules/ldap-syncing-running.adoc[leveloffset=+1]
  ```
- Jinja:

  ```jinja
  {% leveloffset +1 %}{% include "./modules/ldap-syncing-about.md" %}{% endleveloffset %}

  {% leveloffset +2 %}{% include "./modules/ldap-syncing-config-rfc2307.md" %}{% endleveloffset %}

  {% leveloffset +2 %}{% include "./modules/ldap-syncing-config-activedir.md" %}{% endleveloffset %}

  {% leveloffset +1 %}{% include "./modules/ldap-syncing-running.md" %}{% endleveloffset %}
  ```

> [!WARNING]
> **Watch the code blocks.** A heading shift must not change `#` characters that are shell prompts or comments inside a fenced code block. The shift has to be fence-aware, so `# oc adm ...` in a terminal block stays a comment and does not become a heading.

That warning is not hypothetical. In an operations corpus, `#` opens a large proportion of the lines inside code blocks — a naive regex shift corrupts them all, and the damage looks like ordinary prose in the diff.

Reference: [Syncing LDAP groups](https://raw.githubusercontent.com/openshift/openshift-docs/refs/heads/main/authentication/ldap-syncing.adoc)

That completes the mapping. Back to [the series index](/blog/migrating-asciidoc-to-markdown/).
