# Syntax highlighting with markup: html

**URL:** <https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780>\
**Category:** support\
**Created:** [May 19, 2025, 2:26am UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780 "2025-05-19T02:26:05Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![ile](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/ile/32/21163_2.png) [@ile](https://discourse.gohugo.io/u/ile)\
**Post date:** [May 19, 2025, 2:26am UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/1 "2025-05-19T02:26:05Z")

</div>

I have trouble getting syntax highlighting to work.

In the front matter I have `markup: html`

In the index.md’s content section I have:

```html
<pre><code class="language-javascript">// Set these!
const API_KEY = '';
const FORM_ENDPOINT = '';</code></pre>

```

In config.yml I have:

```yml
theme: hugo-PaperMod
pygmentsUseClasses: false
params:
  assets:
    disableHLJS: true 
markup:
  goldmark:
    renderer:
      unsafe: true
  highlight:
    noClasses: false
    style: monokai # Choose a Chroma style (e.g., monokai, dracula, github)
    codeFences: true

```

The final output:

```html
<pre><code class="language-javascript">// Set these!
const API_KEY = '';
const FORM_ENDPOINT = '';</code><button class="copy-code">copy</button></pre>

```

So it adds the copy button at least.

I’m a bit of a new user, just throwing things at the wall here (what Grok suggests).

---

<div class="post-metadata">

**Author:** ![ile](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/ile/32/21163_2.png) [@ile](https://discourse.gohugo.io/u/ile)\
**Post date:** [May 19, 2025, 5:35am UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/2 "2025-05-19T05:35:02Z")

</div>

Grok and me came up with this, which seems to do the job:

```html
<!DOCTYPE html>
<html>
<head>
  <title>{{ .Title }}</title>
</head>
<body>
  <h1>{{ .Title }}</h1>
  {{ $content := .Content }}
  {{ $matches := findRE `<pre><code[^>]*language-([a-zA-Z0-9]+)[^>]*>([\s\S]*?)</code></pre>` $content }}
  {{ if $matches }}
    {{ range $index, $match := $matches }}
      {{ $langMatch := findRE `language-([a-zA-Z0-9]+)` $match 1 }}
      {{ $lang := "javascript" }} <!-- Fallback -->
      {{ if $langMatch }}
        {{ $lang = replaceRE `language-([a-zA-Z0-9]+)` "$1" (index $langMatch 0) }}
      {{ end }}
      {{ $code := replaceRE `<pre><code[^>]*language-[a-zA-Z0-9]+[^>]*>([\s\S]*?)</code></pre>` "$1" $match }}
      {{ $trimmedCode := trim $code "\n \t" }}
      {{ if and $lang $trimmedCode }}
        {{ $highlighted := highlight $trimmedCode $lang "" }}
        {{ $replacement := printf `<pre class="chroma"><code class="language-%s">%s</code></pre>` $lang $highlighted }}
        {{ $content = replace $content $match $replacement }}
      {{ end }}
    {{ end }}
  {{ end }}
  {{ $content | safeHTML }}
</body>
</html>

```

---

<div class="post-metadata">

**Author:** ![ile](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/ile/32/21163_2.png) [@ile](https://discourse.gohugo.io/u/ile)\
**Post date:** [May 19, 2025, 5:57am UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/4 "2025-05-19T05:57:55Z")

</div>

My source content is HTML, not markdown.

---

<div class="post-metadata">

**Author:** ![chrillek](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/chrillek/32/19797_2.png) [@chrillek](https://discourse.gohugo.io/u/chrillek)\
**Post date:** [May 19, 2025, 6:00am UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/5 "2025-05-19T06:00:26Z")

</div>

I got that in the meantime. Then you have no choice, and whatever you set for the Goldmark renderer is irrelevant. Those settings are only for MD files that are rendered to HTML.  
With MD, you’d simply use fenced or other code blocks.

---

<div class="post-metadata">

**Author:** ![ile](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/ile/32/21163_2.png) [@ile](https://discourse.gohugo.io/u/ile)\
**Post date:** [May 19, 2025, 7:11am UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/6 "2025-05-19T07:11:34Z")

</div>

Ok, this was a bit of work, but it seems to work mostly now.

themes/hugo-PaperMod/layouts/partials/code\_highlight.html:

```html
{{ $content := .content }}
{{ if .bypass }}
  {{ $content | safeHTML }}
{{ else }}
  <!-- Process only raw HTML <pre><code> blocks -->
  {{ $matches := findRE `<pre><code( class="language-[^"]*")?>[\s\S]*?</code></pre>` $content }}
  {{ if $matches }}
    {{ range $match := $matches }}
      {{ $lang := "plaintext" }}
      {{ $langMatch := findRE `language-([a-zA-Z0-9]+)` $match 1 }}
      {{ if $langMatch }}
        {{ $lang = replaceRE `language-([a-zA-Z0-9]+)` "$1" (index $langMatch 0) }}
      {{ end }}
      {{ $code := replaceRE `<pre><code[^>]*>([\s\S]*?)</code></pre>` "$1" $match }}
      {{ $trimmedCode := trim $code "\n \t" }}
      {{ if $trimmedCode }}
        <!-- Decode HTML entities before highlighting -->
        {{ $decodedCode := $trimmedCode | htmlUnescape }}
        {{ $highlighted := highlight $decodedCode $lang (dict "noClasses" false) }}
        {{ $innerCode := replaceRE `<div class="highlight"><pre[^>]*><code[^>]*>([\s\S]*?)</code></pre></div>` "$1" $highlighted }}
        <!-- Remove highlighting from HTML entities -->
        {{ $cleanedCode := replaceRE `<span class="ni">&lt;</span>` "&lt;" $innerCode }}
        {{ $cleanedCode = replaceRE `<span class="ni">&gt;</span>` "&gt;" $cleanedCode }}
        {{ $cleanedCode = replaceRE `<span class="ni">&amp;</span>` "&amp;" $cleanedCode }}
        {{ $replacement := printf `<div class="code-wrapper"><pre class="chroma"><code class="language-%s">%s</code></pre><button class="copy-code">copy</button></div>` $lang $cleanedCode }}
        {{ $content = replace $content $match $replacement }}
      {{ end }}
    {{ end }}
  {{ end }}
  {{ $content | safeHTML }}
{{ end }}

```

themes/hugo-PaperMod/layouts/\_default/single.html:

```html
{{- define "main" }}

<article class="post-single">
  <header class="post-header">
    {{ partial "breadcrumbs.html" . }}
    <h1 class="post-title entry-hint-parent">
      {{ .Title }}
      {{- if .Draft }}
      <span class="entry-hint" title="Draft">
        <svg xmlns="http://www.w3.org/2000/svg" height="35" viewBox="0 -960 960 960" fill="currentColor">
          <path
            d="M160-410v-60h300v60H160Zm0-165v-60h470v60H160Zm0-165v-60h470v60H160Zm360 580v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q9 9 13 20t4 22q0 11-4.5 22.5T862.09-380L643-160H520Zm300-263-37-37 37 37ZM580-220h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z" />
        </svg>
      </span>
      {{- end }}
    </h1>
    {{- if .Description }}
    <div class="post-description">
      {{ .Description }}
    </div>
    {{- end }}
    {{- if not (.Param "hideMeta") }}
    <div class="post-meta">
      {{- partial "post_meta.html" . -}}
      {{- partial "translation_list.html" . -}}
      {{- partial "edit_post.html" . -}}
      {{- partial "post_canonical.html" . -}}
    </div>
    {{- end }}
  </header>
  {{- $isHidden := (.Param "cover.hiddenInSingle") | default (.Param "cover.hidden") | default false }}
  {{- partial "cover.html" (dict "cxt" . "IsSingle" true "isHidden" $isHidden) }}
  {{- if (.Param "ShowToc") }}
  {{- partial "toc.html" . }}
  {{- end }}

  {{- if .Content }}
  <div class="post-content">
    {{- if not (.Param "disableAnchoredHeadings") }}
    {{- $anchoredContent := partial "anchored_headings.html" .Content }}
    {{- partial "code_highlight.html" (dict "content" $anchoredContent "bypass" false) }}
    {{- else }}
    {{- partial "code_highlight.html" (dict "content" .Content "bypass" false) }}
    {{- end }}
  </div>
  {{- end }}

  <footer class="post-footer">
    {{- $tags := .Language.Params.Taxonomies.tag | default "tags" }}
    <ul class="post-tags">
      {{- range ($.GetTerms $tags) }}
      <li><a href="{{ .Permalink }}">{{ .LinkTitle }}</a></li>
      {{- end }}
    </ul>
    {{- if (.Param "ShowPostNavLinks") }}
    {{- partial "post_nav_links.html" . }}
    {{- end }}
    {{- if (and site.Params.ShowShareButtons (ne .Params.disableShare true)) }}
    {{- partial "share_icons.html" . -}}
    {{- end }}
  </footer>

  {{- if (.Param "comments") }}
  {{- partial "comments.html" . }}
  {{- end }}
</article>

{{- end }}{{/* end main */}}

```

---

<div class="post-metadata">

**Author:** ![chrillek](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/chrillek/32/19797_2.png) [@chrillek](https://discourse.gohugo.io/u/chrillek)\
**Post date:** [May 19, 2025, 8:26am UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/7 "2025-05-19T08:26:46Z")

</div>

Changing the files of your theme might not be such a good idea, since your changes might be overwritten by a new version of the theme.  
You might be better off if you copy the files to the corresponding directory under `/layouts` and modify them there.

---

<div class="post-metadata">

**Author:** ![ile](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/ile/32/21163_2.png) [@ile](https://discourse.gohugo.io/u/ile)\
**Post date:** [May 19, 2025, 8:44am UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/8 "2025-05-19T08:44:30Z")

</div>

Thanks. I think I will fork/rename the theme at this point – I have made too many changes already for it to be updateable any more.

Good point if someone is going re-use the code.

---

<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:** [May 19, 2025, 1:33pm UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/9 "2025-05-19T13:33:19Z")

</div>

Is there some reason that you can’t just use the [highlight shortcode](https://gohugo.io/shortcodes/highlight/)?

---

<div class="post-metadata">

**Author:** ![ile](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/ile/32/21163_2.png) [@ile](https://discourse.gohugo.io/u/ile)\
**Post date:** [May 19, 2025, 1:46pm UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/10 "2025-05-19T13:46:58Z")

</div>

I’m writing the content with TipTap (web app), then save it to the database (the same web app). Then I create the blog with Hugo on my development machine, using that database as a “headless CMS” through GraphQL.

So, the source content is HTML and that’s the way I have to keep it.

There is no way for shortcodes to enter the equation.

---

<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:** [May 19, 2025, 1:49pm UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/11 "2025-05-19T13:49:06Z")

</div>

Well, at least the workflow’s not convoluted, so you’ve got that going for you.

---

<div class="post-metadata">

**Author:** ![ile](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/ile/32/21163_2.png) [@ile](https://discourse.gohugo.io/u/ile)\
**Post date:** [May 19, 2025, 4:02pm UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/12 "2025-05-19T16:02:52Z")

</div>

It seems that Hugo likes markdown more than HTML.

There might be an opportunity to handle HTML more ”natively”.

I think image processing is another thing, in addition to syntax highlighting.

---

<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:** [May 19, 2025, 4:06pm UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/13 "2025-05-19T16:06:55Z")

</div>

> [@ile](#):
>
> There might be an opportunity to handle HTML more ”natively”.

I don’t know what that means. We pass it through.

---

<div class="post-metadata">

**Author:** ![ile](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/ile/32/21163_2.png) [@ile](https://discourse.gohugo.io/u/ile)\
**Post date:** [May 19, 2025, 4:11pm UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/14 "2025-05-19T16:11:27Z")

</div>

Well, in markdown the images are processed, a srcset is created. Not sure if this is by a plugin or themes, but that seems to happen.

Similar things for HTML?

---

<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:** [May 19, 2025, 4:11pm UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/15 "2025-05-19T16:11:55Z")

</div>

That’s theme-specific.

If you’re just dumping HTML from A to B, you need to either change A or process it somehow when it gets to B.

---

<div class="post-metadata">

**Author:** ![ile](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.gohugo.io/ile/32/21163_2.png) [@ile](https://discourse.gohugo.io/u/ile)\
**Post date:** [May 19, 2025, 4:14pm UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/16 "2025-05-19T16:14:36Z")

</div>

I can create that processing in the theme. That was my plan earlier, wasn’t just sure where it happens.

After all, syntax highlighting seems to be more Hugo specific.

---

<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:** [May 19, 2025, 4:19pm UTC](https://discourse.gohugo.io/t/syntax-highlighting-with-markup-html/54780/17 "2025-05-19T16:19:24Z")

</div>

> [@ile](#):
>
> syntax highlighting seems to be more Hugo specific

Hugo provides native syntax highlighting via a shortcode, fenced code blocks (Markdown), or the `transform.Highlight` function.

But you can implement your own JS approach instead (e.g., [https://highlightjs.org](https://highlightjs.org)).
