Hello, Quarto!
Objective
Learn to create professional blogs, reports, and more with Quarto.
Please read excellent Quarto Guide.
| Section | Time | |
|---|---|---|
| 1 | Intro: what Quarto is, why, the gallery | 20 min |
| 2 | Install & environment + checkpoint | 25 min |
| 3 | Markdown → Quarto: .md, .qmd, code blocks |
40 min |
| 4 | Build a blog + checkpoint | 35 min |
| 5 | Publish to GitHub Pages | 20 min |
| 6 | Group exercise: collaborative blog | 30 min |
| 7 | Recap & questions | 10 min |
Tip
Two checkpoints mark the points where everyone must be working before we move on. If you are stuck at a checkpoint, say so in chat — do not quietly fall behind. There is a “When It Breaks” slide near the end, and the fixes are usually 30 seconds.
Setup problems should not cost you the rest of the session. There is a known-good blog you can clone at any point to rejoin:
git clone https://github.com/project-delphi/quarto-blog-starter my-blog
cd my-blog && conda env create -f environment.yml -p "$PWD/.conda"
conda activate "$PWD/.conda" && quarto previewIt contains a post with an executing {python} cell, so if it renders a chart, your whole toolchain works — Quarto, Python and the Jupyter kernel.
Tip
Use it. Sort your own environment out afterwards, or in the break — do not spend the workshop debugging conda while everyone else is writing posts.
gh) — used to create the repo and publishEvery command in this workshop assumes a POSIX shell. On Windows, get one via WSL2.
In PowerShell as administrator, then reboot:
Open the “Ubuntu” terminal and install conda inside WSL, not on Windows:
Install Quarto and gh inside WSL too.
Important
Work from the Linux home directory (~/code/...), not /mnt/c/.... Rendering across the Windows filesystem boundary is slow and hits permission errors.
Quarto is a modern, interactive publishing system built on pandoc:
origin: org-mode (Emacs) => Sweave / knitr => RMarkdown
Identify one example to recreate during the workshop.
Quarto: A transpiler that converts markdown to various formats. The foundational technologies are:
One, Two & Three.
WARNING: Quarto is under active development!
Create a new directory for the workshop:
Create a Conda environment:
curl -fsSL -o environment.yml \
https://raw.githubusercontent.com/project-delphi/quarto-workshop/main/environment.yml
head -3 environment.yml # expect YAML, not "<!DOCTYPE html>"
conda env create -f environment.yml -p "$PWD/.conda"
conda activate "$PWD/.conda"curl -f fails loudly on an HTTP error instead of saving the error page.
.gitignore file to git ignore conda environment:
Initialize Git:
Write a small example.md file:
Write some markdown in a file.
code blocks: formatted but not executed in markdown.
Use 3 backticks to create code blocks in markdown & the name of the language.
Python
R
Javascript
Add code blocks to your markdown file for Python, Javascript & R.
Transpile example.md using quarto:
Write and convert a markdown file to other format. Commit changes
Basic features:
document metadata YAML format file header:
::: fenced blocks are used for quarto & custom css divs and classes
::: {.class}::: {#id}::: {.class #id}new branch git switch -c qmd-demo
Write a small example.qmd file:
Render the file:
Write a .qmd file with fenced blocks.
Quarto supports executing code blocks in markdown files.
Use curly braces {} to specify the execution language: {python}, {r}, {julia}.
Use curly braces with a period {.} to specify formatting options, no execution: {.python}, {.r}, {.julia}.
Python
R
#| echo: true
#| warning: false
greet <- function(name) {
print(paste("Hello", name))
}
greet("Quarto") “Quarto”
Create a .qmd file with a Python and R code block.
The #| syntax adds control over code block execution & display.
| Option | Description |
|---|---|
#|eval |
Evaluate the code chunk. |
#|echo |
Include the source code in output. |
#|output |
Include the results of executing the code in the output (true, false, or asis). |
#|warning |
Include warnings in the output. |
#|error |
Include errors in the output. |
#|include |
Catch all for preventing any output (code or results) from being included. |
Create a .qmd file with a Python code block using the #| options like echo, warning, and eval.
The environment provides the toolchain, so build it before creating the project.
Make a directory and get the environment file:
mkdir -p ~/code/delete-me/quarto-blog && cd ~/code/delete-me/quarto-blog
curl -fsSL -o environment.yml \
https://raw.githubusercontent.com/project-delphi/quarto-workshop/main/environment.yml
head -3 environment.yml # expect YAML, not "<!DOCTYPE html>"
conda env create -f environment.yml -p "$PWD/.conda"Activate it:
Note
The Python-only environment solves in a couple of minutes. R is a separate, optional environment used later — see the RStudio section.
Run all four. Do not move on until each one is right.
which quarto # expect a path, not "not found"
quarto check # expect Python + Jupyter both OK
which python # expect .../quarto-blog/.conda/bin/python
jupyter kernelspec list # expect python3 -> your .conda, NOT some other projectThe last one catches the sneakiest failure: an old user-level kernel can shadow your environment, so import matplotlib fails even though it is installed. If python3 points elsewhere, prefix commands with JUPYTER_PATH="$PWD/.conda/share/jupyter".
Post your quarto check output in chat if anything looks off.
quarto create project blog . --no-open --no-prompt
git init --initial-branch=main && git switch -c blog-setup--no-open skips launching an editor, --no-prompt skips the confirmation — without them the command stops and waits for input.
Quarto generates:
| Path | What it is |
|---|---|
_quarto.yml |
Site config: title, navbar, theme. Edit this — never replace it. |
index.qmd |
The listing page that indexes your posts |
posts/ |
One folder per post, each with an index.qmd |
posts/_metadata.yml |
Defaults applied to every post |
styles.css |
Your CSS overrides (note: styles, not style) |
about.qmd |
An about page wired into the navbar |
Open _quarto.yml and change the site title. Re-render and find your change.
Posts live in their own folder. Create posts/my-first-post/index.qmd (the template already ships a posts/welcome/, so pick a new name):
---
title: "My First Blog Post"
date: 2025-01-20
---
# Welcome!
My first Quarto post. The chart below is generated when the post renders.
```{python}
import matplotlib.pyplot as plt
years = [2021, 2022, 2023, 2024]
posts = [1, 4, 9, 16]
plt.plot(years, posts, marker="o")
plt.title("Posts per year")
plt.show()
```{python} has no dot — so the code runs and its output is part of the post. Write {.python} and you get syntax highlighting and nothing else.
The preview is a live preview of the blog post in the browser. The render command generates the output file.
Preview:
Render:
posts/ directory.quarto preview & quarto render the notebook.Write and convert a jupyter notebook to a blog post.
Publishing is a command, not configuration. There is no publish: key in _quarto.yml.
Commit your work:
Create the GitHub repo — this also creates the origin remote:
Publish:
Quarto renders the site, creates the gh-pages branch, adds .nojekyll, pushes, and prints your URL. No repo settings to click through.
Fold your work back into main (GitHub flow):
Publish your blog and post the URL in chat.
Optional section. Needs RStudio Desktop installed, plus the R environment (not in the main one) — create it first:
curl -fsSL -o environment-r.yml \
https://raw.githubusercontent.com/project-delphi/quarto-workshop/main/environment-r.yml
conda env create -f environment-r.yml -p "$PWD/.conda-r" && conda activate "$PWD/.conda-r"open -na rstudiorstudio & (requires RStudio Desktop installed)Create a quarto blog post using RStudio.
| You see | It means | Do this |
|---|---|---|
quarto: command not found |
Not installed, or env not activated | which quarto, then quarto check |
CondaError: Run 'conda init' |
Shell not set up for conda | conda init "$(basename $SHELL)", restart shell |
conda env create fails parsing YAML |
You downloaded an error page, not the file | head -3 environment.yml — expect name: |
ModuleNotFoundError for a package you know is installed |
A user-level Jupyter kernel is shadowing your env | jupyter kernelspec list; prefix with JUPYTER_PATH="$PWD/.conda/share/jupyter" |
which R points outside your env |
R env not created or not activated | R is optional — see the RStudio section |
Address already in use on preview |
An old preview is still running | quarto preview --port 4201 |
gh: please run: gh auth login |
Not authenticated | gh auth login |
| Site published but 404s | Pages is still building | Wait a minute; check the repo’s Actions tab |
The fourth row is the sneaky one: the package really is installed, just not in the interpreter Quarto is using.
Still stuck after two minutes? Clone quarto-blog-starter (see “If You Get Stuck”) and keep moving — debug afterwards.
Post the exact error text in chat — not a screenshot of your whole screen.
Form groups of 3–4 persons, have one person who is advanced in github flow.
Each group member creates a branch:
Write posts in the posts/ directory.
Merge branches to main.
Push and share your blog URL.
Quarto Workshop