Syntax Differences Between .Qmd and .Md

.qmd is the Markdown file format used by Quarto. A regular .md file mainly handles text formatting. .qmd adds executable code, output control, cross-references, callouts, shortcodes, and more on top of that.

Quarto is based on Pandoc Markdown, so most Markdown syntax still works: headings, lists, tables, footnotes, and equations. The real differences come from the syntax below.

Quarto official docs: Quarto Guide

1. YAML does more than metadata

In a regular .md file, front matter usually stores a title, date, and tags. In .qmd, YAML also controls rendering, output formats, and code execution behavior.

---
title: "Quarto Example"
format:
  html:
    toc: true
    code-fold: true
  pdf:
    documentclass: report
execute:
  echo: true
  warning: false
jupyter: python3
---

Here, format decides what the document is rendered into, and execute controls how code blocks run. jupyter: python3 selects the Jupyter kernel.

The same .qmd file can be rendered to HTML, PDF, Word, or Reveal.js slides. A regular .md file is usually just static text, unless another tool processes it.

2. Code blocks can run

Code blocks in .md are mostly for display. Code blocks in .qmd can actually run, and Quarto inserts the results back into the rendered document.

```{python}
import pandas as pd

df = pd.DataFrame({"name": ["Alice", "Bob"], "score": [95, 88]})
df
```

For R:

```{r}
summary(cars)
```

Quarto binds an execution engine automatically based on code blocks. {r} usually uses knitr. {python}, {julia}, {bash}, and similar blocks generally use Jupyter. You can also set it manually:

---
engine: jupyter
---

If you only want to show Quarto code block syntax without running it, use double braces:

```{{python}}
print("display only, do not execute")
```

3. #| defines code block options

The most common special syntax in .qmd is #|. It appears at the top of an executable code block and controls the current cell.

```{python}
#| label: fig-sales
#| fig-cap: "Monthly sales trend"
#| echo: false
#| warning: false

import matplotlib.pyplot as plt

plt.plot([12, 18, 15, 22])
plt.show()
```

Common options:

Option Purpose
echo Whether to show source code
eval Whether to execute the code
output Whether to show execution output; asis emits raw Markdown
warning Whether to show warnings
error Whether to include errors in the document
include Whether to include code and results
label Names a code cell for references and debugging
fig-cap Figure caption
tbl-cap Table caption

Document-level defaults can go in YAML, then a single code block can override them with #|:

---
execute:
  echo: false
  warning: false
---

4. Inline code can execute too

Inline code in regular Markdown is just monospace text, like `code`. .qmd can execute expressions inside prose.

```{python}
radius = 5
```

The radius of the circle is `{python} radius`.

After rendering:

The radius of the circle is 5.

R uses the same style:

The radius of the circle is `{r} radius`.

This is useful when narrative text needs computed values. It is better to compute variables in normal code blocks first, then keep inline expressions simple.

5. Callout blocks

.qmd supports callouts wrapped in :::. Compared with plain blockquotes, they are better for notes, warnings, extra context, and pitfalls.

::: {.callout-note}
This is a normal note.
:::

::: {.callout-tip title="Writing habit"}
The same note can keep a similar callout style in HTML and PDF.
:::

::: {.callout-caution collapse="true"}

### Expand to read

Put longer text here. It is collapsed by default.
:::

Quarto includes five built-in types: note, tip, warning, caution, and important.

You can adjust the appearance in YAML:

---
callout-appearance: simple
callout-icon: false
---

6. Panel tabsets

Tutorials often need to show commands for different languages or operating systems. .qmd can use .panel-tabset to create tabs.

::: {.panel-tabset}

### Python

```python
print("hello")
```

### R

```r
print("hello")
```

### Bash

```bash
echo "hello"
```

:::

When rendered to HTML, readers can switch between tabs. Regular .md can use multiple headings, but it does not have this built-in interactive structure.

7. Cross-references

Quarto cross-references are much stronger than plain Markdown links. Figures, tables, code listings, equations, and sections can be numbered automatically, then referenced with @id.

Figure reference:

![Example image](image.png){#fig-demo}

See @fig-demo.

Table reference:

| name  | score |
| ----- | ----: |
| Alice |    95 |
| Bob   |    88 |

: Score table {#tbl-score}

See @tbl-score.

Equation reference:

Euler's formula is shown in @eq-euler.

$$
e^{i\pi} + 1 = 0
$$ {#eq-euler}

Section references require a #sec- ID on the heading and section numbering enabled:

---
number-sections: true
---
### Data processing {#sec-data}

See @sec-data.

The ID prefix matters. Use fig- for figures, tbl- for tables, eq- for equations, and sec- for sections. Quarto uses these prefixes to determine the reference type.

8. Code-generated figures and tables can be numbered

A figure produced by executable code can also be numbered. Add label and fig-cap to the cell.

```{python}
#| label: fig-line
#| fig-cap: "Line chart"

import matplotlib.pyplot as plt

plt.plot([1, 2, 3], [1, 4, 9])
plt.show()
```

See @fig-line.

Tables work the same way:

```{r}
#| label: tbl-cars
#| tbl-cap: "First rows of the cars dataset"

knitr::kable(head(cars))
```

See @tbl-cars.

The label must start with fig- or tbl-. Otherwise, Quarto will not treat it as a referenceable object.

9. Diagram code blocks

If you need flowcharts, sequence diagrams, state diagrams, Gantt charts, or similar diagrams, .qmd can write Mermaid and Graphviz directly. Whether a regular .md file can render these diagrams depends on the platform; Quarto treats them as executable cells.

Official docs: Diagrams

I. Mermaid

Mermaid uses {mermaid} code blocks. Its cell options are not written with #|, but with %%|. The options must appear immediately after the opening fence.

As shown in @fig-flow, the deployment flow is written directly in the document.

```{mermaid}
%%| label: fig-flow
%%| fig-cap: "Automated deployment flow"
flowchart LR
  A[Commit code] --> B{CI check}
  B -- Pass --> C[Build image]
  B -- Fail --> D[Notify developer]
  C --> E[Deploy]
```

Because the label starts with fig-, you can reference it with @fig-flow. fig-cap becomes the caption, and Quarto handles numbering.

Sequence diagrams use the same pattern:

```{mermaid}
%%| label: fig-seq
%%| fig-cap: "Login request sequence"
sequenceDiagram
  participant Browser
  participant API
  participant DB
  Browser->>API: POST /login
  API->>DB: Query user
  DB-->>API: Return result
  API-->>Browser: Return token
```

II. Graphviz

Graphviz uses {dot} code blocks. It is good for topology and dependency graphs, and it also works well for complex network structures. Its cell options are written as //|.

```{dot}
//| label: fig-service-map
//| fig-cap: "Service dependency map"
digraph G {
  rankdir=LR;
  node [shape=box];

  "User" -> "Gateway";
  "Gateway" -> "Service A";
  "Gateway" -> "Service B";
  "Service A" -> "Redis";
  "Service B" -> "PostgreSQL";
}
```

See @fig-service-map.

Mermaid is concise and works well for tutorials or rough architecture sketches. Graphviz gives you more control over layout, so it tends to be steadier for dense relationship graphs.

III. PlantUML

PlantUML is not part of Quarto’s native diagram support. To write PlantUML directly in .qmd, use the pandoc-ext/diagram Lua filter.

Project: pandoc-ext/diagram

Install the Quarto extension first:

quarto install extension pandoc-ext/diagram

Then enable the filter in YAML:

---
filters:
  - diagram
---

The filter calls external programs to generate images. For PlantUML, the system needs a working plantuml command. Common installations also need Java. If the command cannot be found, specify its path with an environment variable:

export PLANTUML_BIN=/usr/local/bin/plantuml

You can also pin the path in YAML:

---
filters:
  - diagram
diagram:
  engine:
    plantuml:
      execpath: /usr/local/bin/plantuml
---

PlantUML code blocks use {plantuml}. The cell options use PlantUML’s comment syntax, so the prefix is '|, not #|.

As shown in @fig-login-puml, this login flow is drawn with PlantUML.

```{plantuml}
'| label: fig-login-puml
'| fig-cap: "User login sequence"
@startuml
actor User
participant "Web Portal" as Web
database "Auth DB" as DB

User -> Web: Enter credentials
Web -> DB: Verify user
DB --> Web: Return result
Web --> User: Login successful
@enduml
```

Class diagrams are also a natural fit for PlantUML:

```{plantuml}
'| label: fig-class-puml
'| fig-cap: "User model class diagram"
@startuml
package "Domain" {
  class User {
    - username: String
    - passwordHash: String
    + login(): boolean
  }

  class Profile {
    - avatar: String
    - bio: String
  }

  User "1" *-- "1" Profile
}
@enduml
```

Diagrams generated by pandoc-ext/diagram can still use Quarto-style captions and cross-references. The label above starts with fig-, so the prose can reference it with @fig-login-puml.

The output format is selected based on the target format. It is commonly SVG or PNG, and can also be PDF. If one format does not work in your environment, disable it with mime-type:

---
filters:
  - diagram
diagram:
  engine:
    plantuml:
      mime-type:
        application/pdf: false
---

This filter runs local commands, so do not use it with untrusted .qmd files. The repository README calls out this security issue as well.

Mermaid and Graphviz are built into Quarto and need less setup. PlantUML requires the filter and an external command-line tool, but it is better suited to UML modeling, such as class diagrams and use case diagrams.

IV. External files

If a diagram gets long, you do not need to put all of it inside the .qmd file. Put Mermaid in .mmd, or Graphviz in .dot, then include it with file.

```{dot}
//| label: fig-kernel
//| fig-cap: "Linux kernel structure diagram"
//| file: linux-kernel-diagram.dot
```

Mermaid can use the same style:

```{mermaid}
%%| label: fig-state
%%| fig-cap: "Order state machine"
%%| file: order-state.mmd
```

This works well for complex diagrams. The .qmd file handles prose and references; the diagram source is maintained separately.

V. Size and output formats

By default, Quarto renders diagrams at their natural size. In HTML output, diagrams are responsive to the page width, so they usually do not overflow the content column.

Use fig-width and fig-height when you need explicit sizing:

```{mermaid}
%%| fig-width: 6.5
flowchart LR
  A[Start] --> B[Process]
  B --> C[End]
```

If you do not want HTML diagrams to resize responsively, turn it off:

---
fig-responsive: false
---

Mermaid is handled differently across output formats. HTML usually uses Mermaid’s JavaScript renderer, while gfm keeps the Mermaid code block. Print-oriented formats like PDF and docx generate PNG. Rendering diagrams for print formats uses Chrome or Edge; without a browser environment, install Quarto’s headless shell:

quarto install chrome-headless-shell

You can also specify the Mermaid output format manually:

---
format:
  html:
    mermaid-format: svg
---

Common values include js and png; svg is also available. If your target is a LaTeX-generated PDF, svg may require extra tools, so the default png is often simpler.

VI. Themes and source display

Mermaid can follow the Quarto theme, or you can specify a Mermaid built-in theme:

---
format:
  html:
    mermaid:
      theme: forest
---

Mermaid’s built-in themes include default, dark, forest, and neutral.

Diagram cells do not show their source code in the rendered document by default. If you are writing a tutorial and want to show both source and rendered output, enable echo:

```{mermaid}
%%| echo: true
flowchart LR
  A --> B
```

```{dot}
//| echo: true
graph G {
  A -- B
}
```

This is where .qmd is more complete than regular .md: diagrams are not just code blocks. They can participate in numbering, references, output conversion, and theme control. With filters like pandoc-ext/diagram, PlantUML can join the same writing workflow.

10. Citations

.qmd can handle references directly with Pandoc citation syntax. The common pattern is to declare bibliography in YAML, then cite entries in the body with @key or [@key].

Official docs: Citations

Start with a BibTeX file such as references.bib:

@book{knuth1984,
  author    = {Donald E. Knuth},
  title     = {The TeXbook},
  year      = {1984},
  publisher = {Addison-Wesley}
}

@book{wickham2015,
  author    = {Hadley Wickham},
  title     = {R Packages},
  year      = {2015},
  publisher = {O'Reilly Media}
}

Then point the document at it in the front matter:

---
title: "Quarto citation example"
bibliography: references.bib
---

In the body, you can write:

This is a parenthetical citation [@knuth1984].

This adds a locator [@knuth1984, pp. 33-35].

You can cite multiple entries at once [@knuth1984; @wickham2015].

@knuth1984 brings the author name directly into the sentence.

If you do not place it manually, Quarto puts the bibliography at the end of the document. If you want to control its position, add a refs container:

### References

::: {#refs}
:::

The default style is Chicago author-date. To switch to another style such as Nature or APA, add a CSL file:

---
bibliography: references.bib
csl: nature.csl
---

If you want an entry in the bibliography without citing it in the body, use nocite:

---
bibliography: references.bib
nocite: |
  @knuth1984
---

You can also include every entry from the .bib file:

---
bibliography: references.bib
nocite: |
  @*
---

A regular .md file usually cannot do this on its own unless the publishing pipeline adds Pandoc or another citation processor on top.

11. Div and Span attributes

.qmd inherits Pandoc Markdown’s attribute syntax. You can add classes, IDs, and attributes to block-level or inline content.

Block-level content:

::: {#example .border}
This block has an ID and a class.
:::

Inline content:

[These words are marked]{.mark}

This syntax may not work in regular Markdown renderers. Quarto passes it to Pandoc, then generates the right output for the target format.

Attribute order also matters: ID first, then classes, then key-value attributes.

[Correct]{#id .class key="value"}
[Unreliable]{.class key="value" #id}

11. Shortcode

Quarto has its own shortcodes, with a form similar to Hugo:

{{< meta title >}}
{{< var version >}}
{{< pagebreak >}}
{{< kbd Ctrl-C >}}
{{< video https://www.youtube.com/embed/xxx >}}

Common shortcodes:

Shortcode Purpose
meta Reads document metadata
var Reads variables from _variables.yml
env Reads environment variables
include Inserts content from another file
embed Embeds a notebook cell
pagebreak Inserts a page break
kbd Displays keyboard shortcuts
video Embeds a video

If the article itself is talking about shortcodes, Quarto may try to process examples inside code blocks. You can use shortcodes=false:

```{shortcodes=false}
{{< var version >}}
```

Or escape it with one extra pair of braces:

{{{< var version >}}}

12. Reusing content with include

.qmd can use include to insert another file. This is useful for shared introductions, setup code, and common notes.

{{< include _intro.qmd >}}

Included files usually start with an underscore, such as _intro.qmd. This prevents project rendering from treating them as standalone pages.

You can also include script content inside a code block:

```python
{{< include _demo.py >}}
```

Two details are easy to miss:

  • include must be on its own line, with blank lines around it.
  • Include is basically copy and paste. Relative paths inside the included file are resolved from the main file’s location.

13. Raw content blocks

.qmd can write raw content for a specific output format. For HTML:

```{=html}
<iframe src="https://quarto.org/" width="600" height="400"></iframe>
```

LaTeX:

```{=latex}
\newpage
```

This content only applies to the matching format. Be careful when writing multi-format documents. An HTML fragment usually does not mean much in PDF output.

14. Extensions and filters

Quarto can attach Lua filters or extensions. They are usually configured in YAML:

---
filters:
  - diagram
---

Extensions can modify the AST, add output features, or package shortcodes. A regular .md file does not have this kind of project-level extension mechanism by itself, unless an external build system adds one.

15. When to choose .qmd or .md

If you are only writing a regular article, README, or project note, .md is lighter and can be viewed almost anywhere.

Use .qmd when you need these features:

  • The document needs to run Python, R, Julia, or Bash code.
  • Figures and tables need automatic numbering and references in the text.
  • One source file needs to output HTML, PDF, Word, or slides.
  • You want Mermaid, Graphviz, or PlantUML diagrams directly in the document.
  • You want callouts, tabsets, variables, or includes to reuse content.
  • The article and the data analysis workflow should live in the same file.

For migration, you can rename .md to .qmd first, keep the original Markdown content, then gradually add #|, callouts, and cross-references. This keeps the diff small and easy to roll back.