Using Anchors in Bitbucket Markdown Documents

Using Markdown to write content is a joy; gone are the days of clunky and slow word processors. Leveraging HTML tags for complete control over your document when needed and falling back to a simple and constant syntax for simpler operations works great. Most VCS hosting platforms will recognize the extension and render it. For larger documents typically organizing sections using a table of contents with anchors is an easy and streamlined process.

Leveraging HTML Anchors

In most documents I find myself doing something like this:

# Table of Contents

- [Battle Snake 2019](#orgf9ab559)
    -   [Strategy](#org889b147)
        -   [Drawbacks](#orgfb85c54)
    -   [Battle History](#orgcfa9a90)
    -   [Screenshots](#orgc2991ca)
    -   [Usage](#org7bfc615)
        -   [Prerequisites](#orge6d4f36)
        -   [Test Server](#orgf8ef52a)
        -   [Run Locally](#org73d091b)
        -   [Deployment](#org3a27619)
    -   [Acknowledgments](#org4a0f7fb)

And then later on linking the header to an HTML anchor:

<a id="org889b147"></a>

## Strategy

Then any other place you want to refer to a section can be done using that ID:

Another drawback of our [strategy](#org889b147) was ...

This renders great in Github and is done automatically when using Org mode's Markdown exporter.

This Should Work in Bitbucket Cloud Right?

You would be wrong

Bitbucket Cloud very closely follows John Gruber's original Markdown specification with the exception of HTML tags. Attempting to use HTML anchors will simply display the escaped HTML along with broken links.


Straight from Atlassian:

We don't support arbitrary HTML in Markdown, for example <table> tags.

Bitbucket Cloud uses Python Markdown to render its Markdown files with the Safe Mode option escape enabled. This option simply escapes all HTML tags to plain text.

This is inconvenient as it greatly limits the customizability of Bitbucket Markdown documents however it prevents Bitbucket from needing to worry about malicious HTML and scripts being injected.

I have seen claims that HTML tags on the self-hosted Bitbucket Server are enabled however I am not able to confirm this.

How Does Github Handle It Then?

Github uses CommonMarker which is a Ruby wrapper of cmark, a C implementation of the CommonMark spec which whitelists some HTML tags.

You won't be able to embed Youtube videos however the spec for Github's markup rendering is documented to a much greater extent than that of Bitbucket.

Exploiting HTML IDs

While there is no solution to allow the use of raw HTML in Bitbucket documents, anchors to headers and table of contents can still be displayed in Bitbucket Cloud.


This can be done by utilizing the fact that all Markdown headers when rendered in Bitbucket will contain an HTML ID in the form:


For example anchoring to this section in Bitbucket would be done with:


All spaces will be replaced with hyphens and all special characters (including dots) will be removed. The ID will also be all lower-case.

Linking to the strategy section from our previous example would look like:

Another drawback of our [strategy](#markdown-header-strategy) was ...

This is not ideal as it won't work in other Markdown renderers however it does allow the use of anchors for those who plan to display content exclusively in Bitbucket. An HTML anchor tag can't be enterted for compatibility since Bitbucket will render it as plain-text.

Table of Contents

While the above technique of using anchors could be employed to manually generate a table of contents, a better solution does exist. By simply inserting the TOC directive as follows, a table of contents should be generated in-place:


Why Does This Work?

The short answer is that the Bitbucket Wiki documents this directive. The more in-depth answer is that the Python Markdown extension TOC is being used. This means that this solution will only work for any renderers powered by Python Markdown.

Too Much Trouble for Anchors?

For those who were hoping Markdown would be the one true universal format, its not there yet. For now to avoid all of this craziness I would recommend simply using Github. However if you are really stuck with the Bitbucket ecosystem, as I am at work, the following should do the trick.