Tutorial3 hours ago

2. We Turned Our Ubuntu Segfault Story Into a Slidev Talk, and the PDF Greyed Out the Fix

We built a 9 slide talk from a real article: line by line code highlights, Mermaid, a photo and speaker notes, exported to 550 KB in 6.21 seconds. Three of our four deliberate mistakes still exited 0.

The WJS Desk

Sep 24, 2026 · 9 min read

Photo by Igreja Dimensão on Pexels

In Part 1 we installed Slidev and exported a four slide toy. This time we built a real talk from one of our own articles, the one where we segfaulted Ubuntu 26.10's Rust rm at 17,000 directories. Nine slides, with code that highlights line by line, a diagram, a two column comparison, a photo, and a speaker note on every slide. The finished PDF is 9 pages and 550 KB, and it exported in 6.21 seconds.

We also broke it on purpose four ways. Three of the four produced a PDF with exit code 0 and no complaint, and one of those three hides the most important line on its slide. That is the part worth reading before you give a talk from this.

Why build a real talk instead of the demo

The starter deck is a feature tour, and every feature in it works, because it was written by the people who wrote the features. A real talk has different pressure on it: it has a table that needs to fit, a command that has to be readable from the back of the room, and a PDF you send afterwards to people who were not there. That last one is where most of our findings came from.

We picked the Ubuntu article because its content is naturally slide shaped: a package listing, a threshold table, a cause, a benchmark, a fix. It is also ours, so we know every number in it is real.

DeckSlidesPDF sizeExport time
Starter tour (Part 1)166.65 MB12.96 s
Our talk, no photo8133 KB4.3 to 6.4 s
Our talk, with one 410 KB photo9550 KB6.21 s
Our talk, --with-clicks18 pages153 KB4.2 to 4.4 s

We did not dig into exactly why the starter is 50 times bigger. It has a full bleed cover photograph, emoji, LaTeX and diagrams, and our talk has none of those until the photo goes in. The practical point stands: a text and code talk exports small, and one photo can quadruple it.

Prerequisites

You need the Part 1 setup: Node 22.12 or newer, a project with @slidev/cli, @slidev/theme-default and playwright-chromium installed. Same machine as before, an Apple M4 Pro on macOS 26.5.1, Slidev 53.0.0. Allow an hour to build your own version of this, most of which is deciding what goes on each slide rather than fighting the tool.

Step 1: code that highlights one line per click

The second slide shows what the ubuntu:26.10 image actually has installed. Four lines of dpkg output, and the point of the slide is line 3: GNU coreutils never left. A plain code block puts all four in front of the audience at once. A highlight sequence walks them through it:

---

# What is actually in the image

```text {1|2|3|4|all}
ii coreutils             9.5-1ubuntu2+0.0.0~ubuntu29  coreutils meta package
ii coreutils-from-uutils 0.0.0~ubuntu29               coreutils from the uutils project
ii gnu-coreutils         9.10-1ubuntu2                GNU core utilities
ii rust-coreutils        0.10.0-1ubuntu1              Universal coreutils utils, written in Rust
```

<v-click>

GNU is still installed: 105 binaries with a `gnu` prefix.

</v-click>

<!--
Click through one line at a time. The punchline is line 3: GNU never left.
-->

The {1|2|3|4|all} after the language is a list of highlight steps separated by pipes. Each click moves to the next step and dims everything else. <v-click> hides the sentence until one more click. We counted in presenter mode: this slide has 5 clicks, four from the code and one from the sentence, and the presenter view showed 0/5 with a progress bar.

Step 2: a table, and the default theme's grey subtitle

Markdown tables work as you would expect, and **bold** inside a cell works too, which is how the segfault rows stand out:

---

# Finding the edge

| Nesting depth | uutils rm 0.10.0 | GNU rm 9.10 |
|---|---|---|
| 8,000 | exit 0 | exit 0 |
| 16,000 | exit 0 | exit 0 |
| 17,000 | **exit 139, SIGSEGV** | exit 0 |
| 32,768 | **exit 139, SIGSEGV** | exit 0 |

<v-click>

After the crash, the directory is still there.

</v-click>

<!--
Measured in ubuntu:26.10 arm64 under Docker on an M4 Pro. Say "indicative", not exact.
-->

Then we added a photo slide and noticed the first paragraph under the heading came out at half brightness. That is not a bug. The default theme's layouts.css contains h1 + p { @apply -mt-2 opacity-50 mb-4; }, so any paragraph directly under a heading is styled as a subtitle at 50% opacity. On a projector in a bright room, that line is close to invisible. If the sentence matters, put something between it and the heading, or make it a list.

Step 3: a photo from the public folder

Files in a public/ folder next to slides.md are served from the root. We saved the article's hero photo there:

mkdir -p public
curl -sL "https://images.pexels.com/photos/5140412/pexels-photo-5140412.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" -o public/terminal.jpg

and used the image-right layout, which takes the image in the slide's frontmatter:

---
layout: image-right
image: /terminal.jpg
---

# The claim

Ubuntu 26.10 release notes: cp, mv and rm "have now been migrated" to uutils.

We pulled `ubuntu:26.10` and checked.

<!--
Read the release note sentence out loud, then say: so we checked.
-->

The 410 KB photo grew the PDF from 133 KB to 550 KB. We also tried image: terminal.jpg without the leading slash, and in the dev server and the PDF export it still worked. Keep the slash anyway. In Part 3 we host the deck under a sub-path, which is exactly the situation where relative paths stop being forgiving.

Step 4: a diagram in Mermaid

Slidev renders Mermaid code blocks as diagrams, no plugin to install. This one explains why rm crashed and what the fix changes:

---

# Why it happens

```mermaid
flowchart LR
  A[rm -rf deep] --> B[recurse per level]
  B --> C{stack left?}
  C -- yes --> B
  C -- no --> D[SIGSEGV, tree remains]
  E[PR 14554] --> F[explicit stack, 16 open fds]
```

<!--
The fix was an open pull request one month before release.
-->

Mermaid is the single largest package in the install at 127 MB, so we expected it to cost something at export. We exported the deck three times with the diagram and three times with it replaced by a sentence. With it: 5.36, 4.53 and 6.91 seconds. Without it: 4.37, 7.21 and 7.82 seconds. The run to run noise is bigger than the diagram, so for one diagram we could not measure any cost at all. We did not test a deck with twenty.

Step 5: two columns and a list that builds

---
layout: two-cols
---

# dd from a pipe

| bs | uutils | GNU |
|---|---|---|
| 32k | 1.9 GB/s | 1.9 GB/s |
| 1M | 251 MB/s | 1.9 GB/s |

::right::

# What it means

<v-clicks>

- Pipes plus big blocks: 7.5x slower
- Devices and files: within noise
- Drop to bs=32k and it vanishes

</v-clicks>

<!--
The thread quoted 350 to 30 MB/s on 0.8. We measured 0.10.0 ourselves.
-->

::right:: splits the slide for the two-cols layout, and <v-clicks> (plural) reveals list items one per click, which saves wrapping each bullet in its own <v-click>. Note the blank lines around the list inside the tags. We tested without them: the slide rendered the literal text - one - two on a single line instead of a list.

When a deck grows, you can move slides into their own files. We moved this slide to pages/dd.md and replaced it with a three line pointer:

---
src: ./pages/dd.md
---

The export was identical: 9 pages, same content, exit 0.

Step 6: speaker notes, checked twice

Every slide ends with an HTML comment, and as Part 1 established, only the last comment on a slide becomes its note. We checked the notes two ways. First in a headless browser against http://localhost:3030/presenter/3: the note text appeared in the notes pane next to the current and next slides, and the page was usable after 4.3 seconds. Second with the dedicated command:

npx slidev export-notes --output notes.pdf

That took 2.20 seconds and produced a single page PDF listing every slide number, title and note, which is the thing to print and keep in your pocket if the second screen fails.

What broke

The PDF hid the most important command. The escape hatch slide originally had ```bash {1|2-3}: highlight the apt install line, then the pinning lines. In presenter mode that is right. In the PDF export, which has no clicks, each code block is frozen at its last highlight step, so the apt install line came out dimmed to a faint grey. It is the one line anyone reading the PDF afterwards needs. The fix is to always end a sequence with all:

---

# The escape hatch

```bash {1|2-3|all}
apt install --allow-remove-essential coreutils-from-gnu coreutils-from-uutils-
printf 'Package: coreutils-from-uutils\nPin: release a=*\nPin-Priority: -10\n' \
  | sudo tee /etc/apt/preferences.d/uutils
```

After the swap, the 32,768 deep delete exited 0.

<!--
This is a supported package operation, not a hack.
-->

We checked the re-export by eye: all three lines at full strength. The alternative is npx slidev export --with-clicks, which renders every click as its own page. Our 9 slide talk became 18 pages that way.

A Mermaid typo costs 36 seconds and gives no PDF. We broke the diagram by changing one node to B[[[ broken. The export printed Mermaid's parse error, Parse error on line 4 with a caret under the bad token, then sat until Playwright's locator.waitFor: Timeout 30000ms exceeded, and exited 1 after 36.34 seconds with no file. The error message is good. The 30 second wait before it counts is not, and in a CI job it reads like a hang.

A missing image exports as a blank half slide. We pointed an image-right slide at /missing-photo.jpg. The export exited 0, printed nothing about it, and the right half of that page was white. You will only catch this by looking at the PDF.

An impossible highlight step is also silent. We changed the sequence to {1|9|2-3} on a three line block. Line 9 does not exist. Exit 0, no warning, and the step simply highlights nothing.

Gotcha: three of our four deliberate mistakes exited 0. Slidev will not tell you about a broken image, a nonexistent highlighted line, or (from Part 1) a misspelled layout. Page through the exported PDF before you send it, every time.

Common mistakes

  • Highlight sequences that do not end in all. Fine live, wrong in every PDF.
  • Your key sentence directly under a heading. The default theme fades it to 50%.
  • Markdown inside <v-click> with no blank lines around it. It renders as literal text instead of a list or paragraph.
  • Notes written as the first thing on a slide. Only the final comment counts.
  • Presenting from the PDF. It has no clicks, so every reveal you built is already revealed. Present from the dev server or a built site, and send the PDF afterwards.

Pro tip: run npx slidev export-notes the night before. It takes about two seconds, and it is the only backup that still works if the venue's second display does not.

What we did not test

We did not present this deck to a live audience or on a projector, so the claim about the 50% subtitle being hard to read is a judgement from the CSS and the exported page, not an observation in a room. We did not test Slidev's recording, camera, drawing or remote control features. We only tested one Mermaid diagram and one photo, and all exports ran on the same M4 Pro. Magic Move, Monaco live coding and custom Vue components are in the starter tour and rendered in its PDF export; we did not use them in this talk, and a PDF says nothing about whether live coding works on stage.

Next

We have a real talk and a PDF we trust. In Part 3 we ship it: a static website with slidev build, a PowerPoint for the colleague who insists, PNGs for a blog post, and hosting under a sub-path, which is where the leading slash on that image finally gets tested.

Share

Our Slidev talk exported to 550 KB in 6.21 seconds. The PDF also greyed out the one command readers needed, and three of four deliberate mistakes exited 0. #Slidev #OpenSource #Markdown #PublicSpeaking

Never miss a ship

The best stuff that shipped this week, delivered every Thursday. Free, no spam. We read all the boring stuff so you get the fun parts.

Keep reading