# Adding section numbers (e.g., 2.2.1) to heading elements

**URL:** <https://discourse.gohugo.io/t/adding-section-numbers-e-g-2-2-1-to-heading-elements/54386>\
**Category:** support\
**Created:** [April 15, 2025, 1:51pm UTC](https://discourse.gohugo.io/t/adding-section-numbers-e-g-2-2-1-to-heading-elements/54386 "2025-04-15T13:51:17Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![gvaley](https://avatars.discourse-cdn.com/v4/letter/g/9fc348/32.png) [@gvaley](https://discourse.gohugo.io/u/gvaley)\
**Post date:** [April 15, 2025, 1:51pm UTC](https://discourse.gohugo.io/t/adding-section-numbers-e-g-2-2-1-to-heading-elements/54386/1 "2025-04-15T13:51:17Z")

</div>

Is there a way to act on different MD elements before they end up in .Page.Content?

In my case, I want to add “hard” numbering to headings like this:

```auto
## Heading 2 -> ## 1.2. Heading 2
### Heading 3 -> ## 1.2.1. Heading 3

```

I also need to to this for figure and table captions.

I need this because my requirements state that links to headings, figures, and tables must show the respective section, figure, or table number like this: “See 1.2.1. Heading 3”

Render hooks are not an option as they can only add ephemeral numbers that cannot be targeted. I can’t use CSS counters as well because the PDF generation toolkit I’m using does not understand custom counters.

Is regex replacement in .Content the only option?

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 15, 2025, 2:49pm UTC](https://discourse.gohugo.io/t/adding-section-numbers-e-g-2-2-1-to-heading-elements/54386/2 "2025-04-15T14:49:15Z")

</div>

Your options:

1. Add the numbers manually. This is prone to error and labor intensive, particularly if you start moving thing around.
2. Use a regex replacement on content. Now you have two problems.
3. Use a render hook.

> [@gvaley](#):
>
> Render hooks are not an option as they can only add ephemeral numbers that cannot be targeted.

I’m not sure what that means. You can store a counter (heading render hooks don’t have an `Ordinal`) and current level in a `Page` store then access them the next time the hook is fired (the are fired sequentially top-to-bottom). I haven’t thought a lot about how to code this, but it could be complicated.

Keep in mind, if you use a heading render hook and modify the heading element’s `id` attribute, you will not be able to use the `TableOfContents` method because the IDs are determined before the hook. You could use something like tocbot.js instead.

To add a counter to table captions, create a table render hook and use its `Ordinal`.

To add a counter to a shortcode you must use a `Page` store. Unlike render hook ordinals, shortcode ordinals are shared across all shortcodes; they are not tracked separately for each shortcode.

---

<div class="post-metadata">

**Author:** ![davidsneighbour](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/davidsneighbour/32/24507_2.png) [@davidsneighbour](https://discourse.gohugo.io/u/davidsneighbour)\
**Post date:** [April 15, 2025, 4:11pm UTC](https://discourse.gohugo.io/t/adding-section-numbers-e-g-2-2-1-to-heading-elements/54386/3 "2025-04-15T16:11:19Z")

</div>

CSS. Not everything has to be done with Hugo.

> **[Using CSS counters - CSS: Cascading Style Sheets | MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters)**
>
> CSS counters let you adjust the appearance of content based on its location in a document.
> For example, you can use counters to automatically number the headings on a webpage or to change the numbering on ordered lists.

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 15, 2025, 4:17pm UTC](https://discourse.gohugo.io/t/adding-section-numbers-e-g-2-2-1-to-heading-elements/54386/4 "2025-04-15T16:17:28Z")

</div>

CSS counters are not an option…

> [@gvaley](#):
>
> I can’t use CSS counters as well because the PDF generation toolkit I’m using does not understand custom counters.

---

<div class="post-metadata">

**Author:** ![davidsneighbour](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/davidsneighbour/32/24507_2.png) [@davidsneighbour](https://discourse.gohugo.io/u/davidsneighbour)\
**Post date:** [April 15, 2025, 4:25pm UTC](https://discourse.gohugo.io/t/adding-section-numbers-e-g-2-2-1-to-heading-elements/54386/5 "2025-04-15T16:25:32Z")

</div>

Argh. That’s bad. ☹ Maybe looking into the “PDF generation toolkit” might help. I have only used MPDF, a PHP library. That one understands counters.

Without knowing the specifics, there are tools like [Remark](https://www.npmjs.com/package/remark) that might work directly on the markdown files and could be used to update the numbering automatically. I am still relatively sure this should/could be done outside of Hugo. In Hugo it will be renderhooks (for the headers and figures at least) with `Store` and in the case of tables regexpes. That would have to run every time the site is built which is counter productive. I am a fierce proponent of content hygiene outside of Hugo… That’s all ;)=

---

<div class="post-metadata">

**Author:** ![gvaley](https://avatars.discourse-cdn.com/v4/letter/g/9fc348/32.png) [@gvaley](https://discourse.gohugo.io/u/gvaley)\
**Post date:** [April 16, 2025, 7:39am UTC](https://discourse.gohugo.io/t/adding-section-numbers-e-g-2-2-1-to-heading-elements/54386/6 "2025-04-16T07:39:58Z")

</div>

> [@jmooring](#):
>
> You can store a counter (heading render hooks don’t have an `Ordinal`) and current level in a `Page` store then access them the next time the hook is fired.

So far so good. This will take care of keeping the number order. However, I’ll also need to access the numbering from Link render hooks. What’s more, I’ll need to access the target page’s Store from other pages where the links to it are located. Is this possible?

> [@jmooring](#):
>
> (the are fired sequentially top-to-bottom).

That’s excellent news.

> [@jmooring](#):
>
> (heading render hooks don’t have an `Ordinal`)

Can I override it to add Ordinal in a template? Where’s the code at?

> [@jmooring](#):
>
> Keep in mind, if you use a heading render hook and modify the heading element’s `id` attribute, you will not be able to use the `TableOfContents` method because the IDs are determined before the hook. You could use something like tocbot.js instead.

Maybe not the best term, but that’s what I meant when I said ephemeral. I’m OK with that because I’m only messing with the `.print.html` render hook in Docsy, leaving the `.html` unchanged. I don’t need `TableOfContents` on the print page.

---

<div class="post-metadata">

**Author:** ![jmooring](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/jmooring/32/4214_2.png) [@jmooring](https://discourse.gohugo.io/u/jmooring)\
**Post date:** [April 16, 2025, 1:26pm UTC](https://discourse.gohugo.io/t/adding-section-numbers-e-g-2-2-1-to-heading-elements/54386/7 "2025-04-16T13:26:52Z")

</div>

> I’ll also need to access the numbering from Link render hooks. What’s more, I’ll need to access the target page’s Store from other pages where the links to it are located.

My response was scoped to your original request: how to add section numbering to headings without using regex replacements.

I suggest you build a prototype then experiment on your own.
