Right, so you’ve got some code you want to show off, and you want it to look good. Hugo’s highlight shortcode is your first-class ticket out of bland, unstyled code blocks. It’s the difference between a mumbled explanation and pointing at a diagram with a laser pointer. But here’s the kicker: its behavior depends entirely on which Markdown renderer you’ve told Hugo to use. If you’re on the default, Goldmark (and you probably are), you get to play with a slightly different, albeit more powerful, set of rules.

The Goldmark Gatekeeper

First, a quick reality check. By default, Goldmark is configured with unsafe = false in your hugo.toml/config.toml. This is a sensible security choice, but it means Goldmark will stubbornly ignore any raw HTML you slap directly into your Markdown files—including the classic HTML approach to code highlighting. This is why the highlight shortcode becomes your new best friend; it’s the sanctioned, secure way to get this job done. Think of it as the bouncer at an exclusive club; you need the right pass (the shortcode) to get your fancy code inside.

Basic Syntax: No Nonsense

The shortcode is refreshingly straightforward. You call it, you tell it what language to use for syntax highlighting, and you feed it the code. It handles the rest.

{{</* highlight go-html-template */>}}
<!DOCTYPE html>
<html>
<head>
  <title>{{ .Title }}</title>
</head>
<body>
  {{ .Content }}
</body>
</html>
{{</* /highlight */>}}

The result is a <pre><code> block, beautifully wrapped in a <div> with a class, ready for your CSS to make it pop. The go-html-template argument tells Chroma (Hugo’s syntax highlighter) which lexer to use. Get this right. Telling it your CSS is “javascript” is a party foul.

Why You Want Line Numbers (And How to Get Them)

Line numbers are crucial for tutorials, error explanations, or any time you need to say, “look right here, at line 23, where you inevitably forgot a semicolon.” Adding them is a simple matter of tossing lineNos into the mix.

{{</* highlight go-html-template "lineNos=inline" */>}}
{{ range .Pages }}
  <h2><a href="{{ .Permalink }}">{{ .Title }}</a></h2>
{{ end }}
{{</* /highlight */>}}

The inline option is the one you want 99% of the time. It literally injects the line numbers into the code itself, as <span> elements. This means they’ll copy-paste along with the code, which is both a pro and a con. The alternative, lineNos=table, creates an old-school, two-column table structure. It’s more fragile for styling and the numbers won’t copy, so I recommend avoiding it unless you have a very specific reason.

Taming the Copy-Paste Problem

Ah, the dark side of lineNos=inline: you copy the code, you get the stupid line numbers. It’s incredibly annoying. This is where the hl_lines parameter swoops in to save the day, not just for highlighting, but for a clever workaround.

{{</* highlight go-html-template "lineNos=inline,hl_lines=2 3" */>}}
package main
import "fmt"
func main() {
    fmt.Println("Hello, World!")
}
{{</* /highlight */>}}

The hl_lines parameter takes a space-separated list of line numbers to highlight. But notice the syntax: the parameters are chained together with commas, not spaces. This is a common trip-up. Now, about that copy-paste issue: you can use a dash of CSS to make the line numbers unselectable. It’s not a perfect solution, but it’s a darn good one.

/* In your stylesheet */
.highlight .ln {
    user-select: none;
    -webkit-user-select: none; /* For Safari */
}

Style It Your Way

Out of the box, Hugo provides a bunch of CSS classes but zero actual CSS. You have to bring your own style, literally. The quickest way is to pick a theme from the gallery (hugo gen chromastyles --style=monokai > assets/css/syntax.css) and then include that generated stylesheet in your head. But the best way is to generate that stylesheet as a starting point and then tear it apart, tweaking it to perfectly match your site’s aesthetic. Don’t just settle for the defaults; make it yours.