import arrow
arrow.utcnow().to("local").format(arrow.FORMAT_RSS)
'Fri, 28 Feb 2025 16:55:31 -0700'
Quarto lets you write technical and scientific documents in Markdown, complete with code execution. A very nice approach to literate programming. You can use it for papers, books, dashboards, nerdy blogs, and nerdy Web sites. There’s a Quarto static site generator of course, which is probably the direction most folks should go.
It can also work with existing sites built by other tools, such as Hugo. That’s handy.
I don’t have any grand plans, but want to make sure the option is open for me when I next have a blog post idea that would be improved by inline code execution.
And if this doesn’t work out there’s always Org Babel.
Setup
Install tools
Configure Hugo
The Quarto Hugo Markdown guide provided some important starting details. We need to make sure Hugo ignores files handled by Quarto. While we’re there, let Goldmark know we allow raw HTML in Markdown.
ignoreFiles:
- "\\.qmd$"
- "\\.py$"
# ...
markup:
goldmark:
renderer:
unsafe: true
Make it a Quarto project
project:
type: hugo
render:
- "*.qmd"
- "!archetypes/"
format: hugo-md
Add a .qmd
file
#- content/post/2025/hello-quarto/index.md
---
title: "Hello Quarto"
jupyter: python3
---
```{python}
print("Yo")
```
Check it
Since Quarto knows this is a Hugo site, it can handle interaction with the
hugo
executable during preview mode.
quarto preview
I get noticable flicker as Quarto does its thing and then Hugo does its thing. But it’s all pretty quick and gets us roughly where I opened the post.
warning
Quarto’s Hugo extension does not use -D
when launching the Hugo local server.
Draft posts will not be generated.
Automate it a little
I drive my site workflow with just, so rather than fix my muscle memory I’ll
see to it that my muscle memory invokes quarto
where needed.
serve:
quarto preview
build:
quarto render
hugo --environment production
Caveats
The folks behind Quarto put a lot of work into making this as easy as possible. There are some fiddly bits, of course. Only to be expected when you’re mashing two static site generator workflows together.
If you’re writing about Quarto code blocks, you’ll want extra backticks for the code block and extra curly braces for the language identifier.
````{markdown}
```{python}
print("Yo")
```
````
It will almost certainly confuse Hugo syntax highlighting when you do this. I temporarily worked around the problem in my code block render hook, by disabling support for syntax highlighting of Markown.
That template is getting chunky, but here’s the most relevant bit:
{{- if eq .Type "markdown" }}
{{- $shouldHighlight = false }}
{{- end }}
...
<pre tabindex="0" class="chroma">
<code class="language-{{ .Type }}" data-lang="{{ .Type }}">
{{- if $shouldHighlight }}
{{- with transform.HighlightCodeBlock . -}}
{{- .Inner -}}
{{- end -}}
{{- else }}{{ .Inner }}{{- end -}}
</code>
</pre>
The other fun part is shortcodes. Using {=markdown}
for the language indicator should make Quarto
pass the contents of that code block unchanged for Hugo.
```{=markdown}
{{% warning %}}
Quarto's Hugo extension does not use `-D` when launching the Hugo local server.
Draft posts will not be generated.
{{% /warning %}}
```