TOC component and header IDs

The TOC component generates header element id attrs based on the header text.

Here’s a header for example.

main outlet

The spacing comes from the styles applied to #main-outlet, which is a Discourse app element having nothing to do with this post.

This isn’t the end of the world if I could specify an alternative id for the header. The TOC component supports this:

Unfortunately, Discourse removes id attrs from post elements before the above code is run so the logic is thwarted.

Here’s a header that uses <h4 id="alt-main-outlet">main outlet</h4>:

main outlet

Note that it still uses main-outlet rather than alt-main-outlet.

I guess Discourse does this in anticipation of someone hacking a page element. But with the TOC component this is easy enough to bypass.

If there’s a solution to this or this is otherwise a well discussed problem that I’ve missed, apologies for the unsolicited opinions that follow :slight_smile:


Personally I think Discourse should NOT strip element id attrs. This is a fundamental web page construct and is used in page navigation with URL fragments. If someone wants to create a link to an element in a post, I think that’s their right as an author.

The fact that TOC opens up ID hacking suggests that this measure of stripping IDs in the first place can potentially be dropped.

If the Discourse team is that concerned about ID hijacking, all Discourse IDs should be prefixed e.g. discourse-main-outlet and ID stripping should apply only to those prefixed IDs.

I can appreciate the impossibility of this given existing code!

One can imagine a set of excluded IDs (e.g. main-outlet, etc.) that are stripped. This still leaves the problem of TOC being used to bypass this safeguard but it would at least let authors create useful anchors.


Directly related to this is the problem of headers with the same text payload.

Topic One


Topic Two


With TOC, both Examples headers get the same ID, which is obviously quite broken. TOC could append incrementing indexes (e.g. examples, examples-2, etc.) — and it probably should as a default course — but still better would be a solution to the problem discussed above, which lets authors decide how to describe their content. E.g. <h5 id="topic-one-examples">Examples</h5>, etc.

@Johani what do you think of this ?


If you need to add a heading then it needs to be prefixed with heading-- you can find more information about that here Deep Linking to Headings (Anchors)

ids that are not prefixed like that will be stripped, regardless of what the id value is.

Headings with duplicate text are not currently supported - and have never been - in the TOC component. The component generates the id based on the text of the heading. So, two headings with the same exact text will get the same id.

There are no plans to patch this up in the component right now since automatic id generation is on our list of features for the next release of Discourse and we plan to address the issue of duplicate headings by adding the heading’s index to its id.

When we implement this in core, I think adding a prefix to the generated headings should be enough.


That makes sense. Thanks for the detailed explanation!