Using `transform.HTMLToMarkdown` for human- and machine-readable alternate pages

I have been using transform.HTMLToMarkdown in production to publish a Markdown alternate for every article on my Hugo site. After reading #13946, #14025, and #14026, I wanted to share the approach and ask whether it fits Hugo’s intended direction.

The product model

My Markdown pages are intended for both machines and humans.

A reader may open the .md URL directly, download it, save it in an editor such as Obsidian or Typora, or share it independently of the HTML page. It is therefore more than an LLM-oriented text extraction format.

At the same time, it is not a byte-for-byte copy of the source Markdown. It represents the published article, so shortcodes must be expanded and private or build-only resource paths must become public URLs.

The intended properties are:

  • semantically faithful to the article;
  • independently readable and portable;
  • reasonably natural for human Markdown readers;
  • suitable for agents and other machine clients;
  • based on published content rather than raw authoring details.

Current pipeline

The current pipeline is:

Markdown source
→ Markdown output-specific render hooks and shortcodes
→ content-level HTML
→ transform.HTMLToMarkdown
→ Markdown alternate

The main layout is approximately:

{{- $body := .Content | transform.HTMLToMarkdown | strings.TrimSpace -}}

The site also provides output-specific templates such as:

layouts/_default/_markup/render-image.markdown.md
layouts/shortcodes/image-row.markdown.md

When .Content is evaluated for the Markdown output format, these templates first produce intermediate HTML designed for the Markdown alternate. transform.HTMLToMarkdown then converts that HTML.

This allows the site to:

  • keep image syntax inside fenced code unchanged;
  • expand shortcodes;
  • convert private relative image paths into absolute, hashed public URLs;
  • give shortcodes a simpler Markdown-specific representation;
  • convert only article content, without navigation, forms, comments, or the footer.

This is working well, but I could not find the complete pattern documented. Is this combination of output-specific hooks and shortcodes with .Content | transform.HTMLToMarkdown considered an intended long-term use of the API?

An official example and an integration test for this pipeline might be useful.

Relationship to #14026

Issue #14026 proposes allowing a later output format to read an earlier published output, for example converting the completed HTML output to Markdown.

That could be useful, but converting a complete HTML page raises two questions.

First, the converter cannot generally know which elements are part of the article. A page may also contain navigation, a table of contents, comments, forms, and footer content. The underlying html-to-markdown library supports features such as include selectors, but Hugo currently exposes no conversion options.

Second, published HTML commonly contains relative URLs:

<img src="/images/hash.avif">

An agent may resolve this against the request URL, but a human may download the Markdown file and open it without that context. A portable document should preferably contain:

![](https://example.com/images/hash.avif)

A full-output conversion workflow may therefore need explicit content selection and a base URL or URL-resolution policy.

The output-specific .Content pipeline lets my site handle both concerns before conversion, so it currently seems more suitable than converting the complete HTML page.

Human-readable conversion fidelity

For an agent-only representation, many normalization differences are harmless. They are more visible when the result is also presented as a human-readable document.

Footnotes are the clearest example. Goldmark produces semantic HTML with doc-noteref, doc-endnotes, and doc-backlink roles, but the converted Markdown currently looks like:

Text[1](#opaque-generated-id)

1. [Reference](https://example.com/) [↩︎](#another-generated-id)

The information remains available, but this is less natural and portable than:

Text[^1]

[^1]: [Reference](https://example.com/)

The generated fragment identifiers are implementation details that add noise for human readers.

Similar questions may eventually apply to strikethrough, task lists, table alignment, and semantic HTML generated by shortcodes.

The Hugo implementation already notes that HTMLToMarkdown is experimental and may gain more options. A controlled subset of the underlying converter configuration could eventually cover:

  • a base URL or absolute-URL policy;
  • include or exclude selectors;
  • optional conversion plugins or profiles;
  • semantic footnote conversion;
  • rules for retaining or removing selected elements.

I am not suggesting that Hugo needs a separate native Markdown renderer. The HTML-to-Markdown approach has the important advantage of representing published content regardless of whether it originated from Markdown, another markup format, a shortcode, or a content adapter.

What seems most useful is a documented and configurable conversion stage.

Broader context and example

Cloudflare’s article on Agent Readiness shows the growing interest in delivering appropriate content formats to agents. However, Markdown alternates are not necessarily agent-only artifacts. Markdown is also an established interchange format with the registered text/markdown media type described by RFC 7763.

My site is primarily written in Chinese, which may also make it a useful non-English and CJK test case:

The example contains processed remote images, internal and external links, blockquotes, and footnotes.

Does this output-aware intermediate HTML model fit the intended direction of transform.HTMLToMarkdown? Would documenting this pattern be useful, and should converter options and footnote handling be discussed separately?