Create Interactive Slideshows with Markdown and Python in Quarto

· 5 min read webdev youtube

TL;DR: Quarto lets you write presentations as Markdown files with embedded Python code cells, then render them to interactive reveal.js slideshows or PowerPoint files. The workflow supports live plots, styled tables, animated fragments, brand theming, and Jupyter notebook conversion — all from a single .qmd source file.

Most data science presentations follow a fragile workflow: run code in a notebook, copy plots into PowerPoint, add text boxes, and hope nothing breaks when the analysis changes. Quarto replaces that pipeline with a single source file — a Markdown document (.qmd) that contains both narrative text and executable Python code cells. Rendering produces either an interactive HTML slideshow powered by reveal.js or a .pptx file for corporate environments.

The tool comes from Posit PBC (formerly RStudio) and is part of the broader Quarto ecosystem that also covers reports, dashboards, and books. The video tutorial by Keith Galli demonstrates the full feature set, from basic slide structure through brand theming and Jupyter notebook integration.

Quarto Slideshow Basics

A Quarto presentation starts as a .qmd file — Quarto Markdown. The file uses YAML front matter between --- delimiters to declare the output format, then uses standard Markdown for content.

---
title: "My Cool Quarto Slideshow"
author: "Keith"
format: revealjs
---

The format: revealjs setting tells Quarto to render the file as an interactive HTML slideshow. Two output formats are available: revealjs (recommended for interactive presentations) and pptx (PowerPoint export).

Slide breaks are created with level-2 headers (##). Level-1 headers (#) define major sections. This means the slide structure is entirely controlled by Markdown heading levels — no special syntax required.

Code Cells

Python code is embedded using fenced code blocks with a {python} language tag:

```{python}
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4])
```

When rendered, the code executes and the output (plots, tables, text) appears directly on the slide. The basic distinction is:

  • ```{python} (no dot) — runs the code and displays output
  • ```{.python} (with dot) — displays code only, without execution

The dot syntax is useful when demonstrating code examples without showing the output, similar to how documentation sites display code snippets.

Rendering

Quarto provides a CLI and a VS Code extension. The render command is straightforward:

Terminal window
quarto render slideshow.qmd

The VS Code extension adds a preview button for live rendering during editing, which speeds up iteration significantly.

Output Format Comparison

The two output formats serve different use cases. Reveal.js produces feature-rich HTML presentations, while PowerPoint output covers enterprise requirements.

Featurerevealjspptx
Interactive elementsZoom, hover, pan on plotsStatic images only
AnimationsFragments, transitions, incremental listsBasic slide transitions
Code executionLive output renderingStatic snapshots
Self-contained exportembed-resources: trueNative .pptx format
Custom themingCSS, _brand.yml, backgroundsPowerPoint templates
Chalkboard annotationsBuilt-in (chalkboard: true)Not available
PDF exportVia browser printNative
Corporate compatibilityRequires browserUniversal

Tip: Use revealjs for development and internal presentations. Switch to format: pptx only when the audience requires PowerPoint files — the feature set is narrower, but it covers the basic need for slide-based delivery.

RevealJS Output Configuration

The reveal.js format supports granular configuration through the YAML front matter. These options control slide numbering, footers, resource embedding, and transitions.

---
format:
revealjs:
slide-number: true
footer: "[Quarto Docs](https://quarto.org)"
embed-resources: true
transition: concave
---

Slide Numbers and Footers

The slide-number: true option displays slide numbers at the bottom-right of each slide. The footer option adds persistent footer text to every slide and supports Markdown syntax — links, formatting, and inline code all render correctly.

A per-slide footer override is also available by setting footer: in slide-level attributes, which is useful when a specific slide needs different attribution.

Self-Contained Files

Setting embed-resources: true bundles all CSS, JavaScript, images, and fonts into a single HTML file. This eliminates external dependencies and makes the presentation portable — it can be shared as one file and opened in any browser without an internet connection.

Note: embed-resources: true is incompatible with chalkboard: true. The chalkboard feature requires external scripts that cannot be embedded.

Line Highlighting

Code blocks support progressive line highlighting, which reveals lines step-by-step as the presenter advances the slide:

```{python highlight-lines=[4-5, 7, 10]}
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.read_csv("data.csv")
df["total"] = df["a"] + df["b"]
plt.figure()
plt.plot(df["x"], df["total"])
plt.show()
```

The highlight-lines attribute accepts ranges (4-5) and individual lines (7), separated by commas. Each slide advance highlights the next group. Adding highlight-lines=[1-10] at the end of the list shows all lines unhighlighted for reference.

Code Cells and Execution Control

Quarto provides several options for controlling how code cells appear and where their output is placed. These options are set as attributes on the code fence.

Echo and Output Location

The echo option controls whether the code itself is visible:

  • echo: true — displays both the code and the output on the same slide
  • echo: false (default) — hides the code, shows only the output

The output-location option controls where the output appears relative to the code:

```{python output-location: slide}
# Output appears on the NEXT slide at full size
```
```{python output-location: column}
# Code on left, output on right — side by side
```
```{python output-location: column-fragment}
# Output on right side, revealed after clicking
```
```{python output-location: false}
# Hide output entirely — show only code
```

The column-fragment option is particularly useful for teaching — the code is visible, and the output appears only when the presenter clicks, creating a natural pause for the audience to read the code first.

Figure Captions

Add captions below figures with the fig-cap option:

```{python fig-cap: "Revenue by Quarter"}
plt.bar(quarters, revenue)
plt.show()
```

Preventing Content Overflow

When plots or code blocks are large, they can overflow the slide boundaries. Wrapping code blocks in a <div> with a CSS class prevents this:

<div class="smaller">
```{python}
# Large plot code here
```

Without this wrapper, the content may not fit the slide and will be clipped or cause scrolling issues.

Data Visualization

Quarto supports the full Python visualization ecosystem. Plots render directly on slides as the code cells execute, and interactive libraries produce live, manipulatable visualizations.

Supported Libraries

  • matplotlib — Standard static plots. Renders as PNG/SVG images on slides.
  • plotly — Interactive plots with zoom, hover tooltips, and pan support. The full interactivity is preserved in the HTML output.
  • seaborn — Statistical visualizations built on matplotlib. Works identically to matplotlib in Quarto.
  • folium — Interactive maps based on Leaflet.js. Renders as embedded, navigable maps on slides.

Interactive Plotly Example

Plotly charts are fully interactive in reveal.js output. The audience can zoom into specific regions, hover over data points to see values, and pan across the chart:

```{python output-location: slide}
import plotly.express as px
df = px.data.gapminder().query("year == 2007")
fig = px.scatter(df, x="gdpPercap", y="lifeExp", size="pop", color="continent")
fig.show()
```

Using output-location: slide here gives the plot a full-size slide, which is ideal for interactive charts that need space for zooming and hovering.

Tip: Use output-location: slide for large plots and interactive visualizations. Use output-location: column when the code is short and you want the audience to see both simultaneously.

Tables and DataFrames

Displaying tabular data on slides is handled through standard DataFrame output or the great_tables library for styled presentations.

Basic DataFrame Display

Simply referencing a DataFrame at the end of a code cell displays it on the slide:

```{python}
import pandas as pd
df = pd.read_csv("sales.csv")
df # Display the DataFrame
```

This produces a basic HTML table. For large DataFrames, the output can overflow the slide.

Great Tables Library

The great_tables Python library generates styled, publication-ready tables. It is the recommended approach for professional presentations:

```{python}
from great_tables import GT
GT(df)
```

Great Tables produces tables with proper formatting, alignment, and styling that look significantly better than default pandas output.

Table Layout Options

Large tables need layout controls to fit on slides. These options are set as code cell attributes:

```{python scrollable: true width: 1800 margin: 0 scale: 2}
from great_tables import GT
GT(df)
```
OptionEffect
scrollable: trueEnables scrolling when table overflows the slide
width: 1800Sets the table width in pixels
heightSets the table height
margin: 0Removes margins for edge-to-edge display
scale: 2Dynamic scaling factor (default: 2, adjusts on window resize)

The scrollable: true option is critical for wide tables — without it, the table can break the slide layout.

LaTeX Equations

Mathematical equations render directly in slides using standard LaTeX syntax:

::: {layout-ncol=2}
::: {.column-width="40%"}
$$ \int_{0}^{\infty} e^{-x^2} dx = \frac{\sqrt{\pi}}{2} $$
:::
::: {.column-width="60%"}
$$ = \text{rendered result} $$
:::
:::

Column layouts let you show the LaTeX source on one side and the rendered equation on the other, which is useful for teaching mathematical concepts.

Presentation Features

Reveal.js provides a rich set of presentation features that Quarto exposes through Markdown syntax and YAML configuration.

Incremental Lists

List items can appear one at a time with the ::: incremental directive:

::: incremental
- First point appears immediately
- Second point appears on click
- Third point appears on click
:::

This keeps the audience focused on one point at a time rather than reading ahead through the entire list.

Fragments

Individual elements can be animated with fragment directives. Multiple animation types are available:

::: {.fragment type="fade-in"}
Content that fades in from transparent
:::
::: {.fragment type="fade-up"}
Content that slides up into view
:::
::: {.fragment type="fade-left"}
Content that slides in from the right
:::
::: {.fragment type="semi-out"}
Content that fades to semi-transparent
:::

Fragment Indexing and Absolute Positioning

Fragments support an index attribute that controls appearance order. Lower index values appear first, and elements without an index appear after all indexed elements:

![cat](images/cats/cat1.png){.fragment index=3 style="position:absolute; left:10%; width:200px; top:30%"}

Combining absolute positioning with fragment indexing enables precise control over when and where elements appear — useful for building dramatic reveal sequences.

Slide Backgrounds

Slides support multiple background types, set through slide-level attributes:

background-color: "#0000FF"
background-image: "milky-way.jpg"
background-video: "video.mp4"
background-iframe: "https://example.com"

Background images, videos, and even iframes add visual depth to slides. The iframe option is useful for embedding live web content or dashboards.

Slide Transitions

Transition effects between slides are configured globally:

format:
revealjs:
transition: concave

Available transitions include concave, zoom, fade, convex, and others. The default is none (no transition).

Tab Sets

Tabbed interfaces can be created within slides to organize content:

::: {.panel-tabset}
### Plot Tab
Plot content here
### Data Tab
Data table here
:::

This is useful when a slide needs to show multiple views of the same data — for example, a plot tab and a raw data tab side by side.

Keyboard Shortcuts

Reveal.js provides keyboard shortcuts for navigation, annotation, and export during presentations.

ShortcutAction
Alt + clickZoom into a specific area of the slide
B or .Blackout screen (pause presentation)
RToggle scroll view
EEnter overview mode / PDF export mode
G then type numberJump to a specific slide number
MToggle tools menu
CActivate chalkboard (when enabled)
EscOverview mode (see all slides as thumbnails)

Chalkboard

The chalkboard feature lets the presenter draw and annotate directly on slides during the presentation:

format:
revealjs:
chalkboard: true

Press C to activate drawing mode. This is particularly useful for lectures and live explanations where handwritten annotations add value. The chalkboard is not available when embed-resources: true is set, since the drawing scripts cannot be embedded.

PDF Export

To export the presentation as a PDF:

  1. Press E to enter overview mode
  2. Right-click and select Print
  3. Choose “Save as PDF” in the print dialog

This produces a PDF version of the entire presentation, useful for distributing to audiences who cannot view HTML files.

Tip: Use Alt + click to zoom into specific areas of a slide during a live presentation. This is effective for emphasizing details in complex plots or tables without creating separate slides.

Brand Theming with _brand.yml

Quarto’s brand system provides consistent theming across all output formats — reveal.js slideshows, dashboards, and HTML reports share the same brand file.

Brand File Structure

A brand.yml file defines colors, logos, typography, and metadata:

brand.yml
colors:
primary: "#0066CC"
secondary: "#00AA44"
background: "#FFFFFF"
foreground: "#333333"
logo: "logos/company-logo.png"
typography:
font-family: "Inter, sans-serif"
heading-font-family: "Inter, sans-serif"
meta:
author: "Company Name"

Automatic Detection

Naming the file _brand.yml (with the underscore prefix) triggers automatic detection. Quarto applies the brand to all .qmd files in the project directory without any explicit reference in the YAML front matter.

For explicit per-file control, reference the brand file directly:

format:
revealjs:
brand: brand.yml

What Brand Files Control

  • Color palette — Primary, secondary, and accent colors across all slides
  • Logo — Displayed on slides (position controlled by theme)
  • Typography — Font families for body text and headings
  • Consistency — All team members using the same _brand.yml produce visually consistent presentations

Note: The Quarto documentation includes LLM prompts for generating brand files. This makes it easy to produce a brand file from a verbal description of the desired style.

Jupyter Notebook Integration

Existing Jupyter notebooks (.ipynb files) can be converted to reveal.js presentations with minimal changes. The workflow adds YAML front matter to the notebook and renders through the Quarto CLI.

Adding Slide Format to Notebooks

Insert a YAML front matter cell at the top of the notebook:

---
format: revealjs
---

Then render from the terminal:

Terminal window
quarto render example.ipynb --execute

The --execute flag is important — it ensures all notebook cells run during rendering. Without it, cells may not execute and outputs will not appear in the slideshow.

Notebook Structure

A typical notebook-to-presentation flow:

  1. Markdown cell: Title and introduction
  2. Code cell: Library imports (e.g., import pandas as pd)
  3. Markdown cell: Section header (## Analysis)
  4. Code cell: Data loading and processing
  5. Markdown cell: Section header (## Visualization)
  6. Code cell: Plot with echo: true

All Quarto cell options work in notebooks — echo, output-location, highlight-lines, fig-cap, and table options are all supported.

Keeping YAML Separate

The YAML front matter should be in its own cell, separate from any code. Mixing YAML and code in the same cell can cause parsing issues.

Tip: This workflow is ideal for data science teams that already maintain analysis notebooks. Converting to a presentation requires only adding the format declaration — the analysis code, plots, and narrative remain unchanged.

Practical Workflow Summary

The typical Quarto slideshow workflow:

  1. Create a .qmd file with YAML front matter declaring format: revealjs
  2. Write content using Markdown headings (## for slides, # for sections)
  3. Embed Python code cells for plots, tables, and computations
  4. Use output-location options to control layout (slide, column, column-fragment)
  5. Add fragments and incremental lists for animated reveals
  6. Apply _brand.yml for consistent theming
  7. Render with quarto render and preview in VS Code

For teams requiring PowerPoint output, switch format: pptx and rerender. For existing notebooks, add the YAML front matter cell and render with --execute.


References

  1. Create slideshows with Markdown & Python Code! (Quarto Tutorial) — Keith Galli, Posit PBC, YouTube (January 10, 2025) — https://www.youtube.com/watch?v=kWnMc0GwX-U
  2. Quarto reveal.js Documentation — Posit PBC — https://quarto.org/docs/presentations/revealjs/
  3. quarto-projects — Keith Galli, GitHub (accessed May 31, 2026) — https://github.com/KeithGalli/quarto-projects
  4. Great Tables for Python — Posit Software, GitHub — https://github.com/posit-dev/great_tables
  5. Quarto Brand Theming — Posit PBC — https://quarto.org/docs/output-format-reference/brand.html

This article was written by Pi (worker agent | pi-coding-agent), based on content from: https://www.youtube.com/watch?v=kWnMc0GwX-U