Get Started
Installation
quarto add jeroenjanssens/quarto-termOn first render, the correct binary for your platform is automatically downloaded from GitHub Releases and cached locally. Supported platforms: macOS (ARM and Intel), Linux (x86_64 and ARM), and Windows.
To build from source instead (requires Rust):
cargo build --releaseYour first document
Create a file called hello.qmd:
---
title: "Hello Terminal"
format: html
engine: markdown
extensions:
term:
shell: zsh
---
```{term}
echo 'Hello, world!'
```
```{term}
for i in 1 2 3; do
echo "Item $i"
done
```
```{term}
sleep 100 #! expect-prompt: false
ctrl-c #! literal: false, delay: 0.5
```Render it:
quarto render hello.qmdHere’s how the rendered output looks:
echo 'Hello, world!'
Hello, world!
for i in 1 2 3; do
echo "Item $i"
done
Item 1
Item 2
Item 3
sleep 100
^C
The engine: markdown line tells Quarto to skip its built-in execution engines. The extensions: term: key holds all configuration.
How it works
- The Lua filter collects all
```{term}cells from your document. - It sends them to the
quarto-termbinary, which spawns a persistent shell. - Each line is typed into the shell (simulating a real user), and output is captured via a terminal emulator.
- The rendered output (with ANSI colors, prompts, etc.) replaces the original code cells in the document.
Because the shell session persists, variables, the working directory, and all other state carry over between cells.
What it captures
Multi-line constructs like loops work automatically — the shell’s PS2 continuation prompt (>) is detected and lines are grouped together:
for planet in Mercury Venus Earth Mars; do
echo "- $planet"
done
- Mercury
- Venus
- Earth
- Mars
ANSI colors render faithfully, so colored output from compilers, linters, and CLI tools looks just like it does in your terminal:
printf '\033[1;31mError:\033[0m file not found\n'
Error: file not found
printf '\033[1;33mWarning:\033[0m deprecated API\n'
Warning: deprecated API
printf '\033[1;32mSuccess:\033[0m all tests passed\n'
Success: all tests passed
Multi-line pipes work naturally:
seq 1 5 |
paste -sd+ |
bc
15
Configuration
All configuration goes under extensions: term: in your YAML front matter:
extensions:
term:
shell: zsh
spacing: true
timeout: 15.0
style:
colorscheme: solarized-darkSee the Reference for the full list of options, Styling for colorschemes and fonts, and Recipes for common patterns.
Using in a website or book
For standalone .qmd files, the filter is discovered automatically. In a Quarto project (website or book), you need to explicitly declare the filter in your _quarto.yml:
project:
type: website
filters:
- term
extensions:
term:
shell: zsh
style:
colorscheme: solarized-darkIndividual pages still need engine: markdown in their front matter (or use the knitr/Jupyter setup described below).
Using in a presentation
quarto-term works in RevealJS presentations. Terminal cells render on each slide with full ANSI color support and persistent state across slides.
---
title: "My Presentation"
format: revealjs
engine: markdown
extensions:
term:
shell: zsh
style:
colorscheme: catppuccin-mocha
cols: 64
---Use smaller cols (e.g., 64) and optionally a smaller font-size to fit terminal output on slides. Here’s a live demo:
Using with other output formats
quarto-term supports all major Quarto output formats. Use format-specific style overrides to tailor the terminal appearance:
extensions:
term:
style:
colorscheme: dracula
typst:
font-family: "Fira Code"
font-size: 9pt
docx:
font-family: "Consolas"
font-size: 10ptFor PDF via Typst, use format: typst in your document — terminal output renders as styled monospace blocks with full ANSI color support.
For Word (format: docx), terminal output renders as a monospace table with background color, font styling, and ANSI colors.
For EPUB, the HTML renderer is used automatically — all styling works as in HTML.
See the Reference for the complete format support matrix and Styling for override examples.
Using with knitr or Jupyter
For documents that only contain terminal cells, engine: markdown is recommended. If you need to mix {term} cells with {r} or {python} cells:
Jupyter works without changes. {term} blocks pass through to the Lua filter.
knitr requires registering a pass-through engine in a setup chunk:
```{r}
#| include: false
knitr::knit_engines$set(term = function(options) {
knitr:::one_string(c("```{term}", options$code, "```"))
})
```Note that {term} cells are always executed after all knitr/Jupyter cells have finished. The terminal session runs during the Pandoc filter phase, not during engine execution.