A template for combining Astro and Reveal.js to build portable, composable presentation websites.
- Markdown presentations: Write slides in Markdown with full Reveal.js power
- Self-contained: Each presentation lives in its own folder with its assets
- Composable modules: Combine reusable slide modules to build talks
- Template variables: Use
{{variable}}syntax for dynamic content in modules - Automatic image rebasing: Relative paths work everywhere - images get rewritten to absolute paths
- Astro-powered: Fast builds, component-based architecture, static site deployment
- Clone this repository
- Install dependencies:
npm install - Add your presentations to
/public/slides(see below) - Run locally:
npm run dev
Create a folder for each presentation under /public/slides with:
- A markdown file (e.g.,
my-talk.md) - Any images or assets you want to include
Example structure:
/public/slides/
├── my-talk/
│ ├── my-talk.md
│ └── diagram.png
└── my-workshop/
├── workshop.md
├── screenshot1.png
└── screenshot2.png
In your markdown, use relative links to reference images: . This makes presentations portable - you can work on them locally, preview them in VS Code, etc. Presentations are loaded as an Astro collection and rendered as a reveal.js presentation at /slides/[folder-name].
You can build presentations by combining other presentations or modules using the includes field. This is great for:
- Reusing standard slides (title, acknowledgments, contact info)
- Mixing different talks or demos together
- Creating talk variants without duplicating content
Add an includes field to your frontmatter:
---
title: "My Research Talk"
author: "Your Name"
includes: ["title", "my-talk", "demo-project", "acknowledgments"]
---Any slides written in the markdown body will be appended after the included modules.
Important syntax notes:
- Module IDs are just folder names: Use
"title"not"_modules/title" - Use inline JSON arrays:
["item1", "item2"]not YAML multi-line format
The system automatically:
- Combines all listed modules in order
- Adds proper slide separators (
---) between modules - Rewrites image paths so they work correctly
- Substitutes template variables (see below)
See /public/slides/example-combined/ and /public/slides/snacks-combined/ for working examples.
Modules are slideshow fragments (one or more slides) that aren't meant to be viewed on their own--they only make sense when included in a presentation. They live in a special folder, /public/slides/_modules/.
Modules work like standalone presentations, but have two unique features:
- They don't show up as presentations in the presentation list.
- They allow you to use template variables, unlike a regular presentation. Use
{{variable}}placeholders that get filled in by the including presentation's frontmatter
Use {{variableName}} syntax in a module. When included, values are pulled from the presentation's frontmatter:
Module (_modules/title/title.md):
# {{title}}
{{author}} · {{date}}Presentation (using the module):
---
title: "My Amazing Talk"
author: "Jane Doe"
date: "2025-01-15"
includes: ["title", "my-demo", "acknowledgments"]
---The title module will render as "My Amazing Talk", "Jane Doe · 2025-01-15".
| Location | Use in includes |
|---|---|
/public/slides/_modules/title/title.md |
"title" |
/public/slides/_modules/acknowledgments/acknowledgments.md |
"acknowledgments" |
/public/slides/my-demo/my-demo.md |
"my-demo" |
Both modules and regular presentations use just the folder name--the system finds them automatically.
Set the theme in your frontmatter:
---
title: "My Talk"
revealTheme: "white" # Options: black, white, league, beige, sky, night, serif, simple, solarized
centered: true # Vertically center slides (default: false)
transition: "slide" # Options: none, fade, slide, convex, concave, zoom
---See /public/slides/theme-demo/ for examples of all available themes.
All commands are run from the root of the project, from a terminal:
| Command | Action |
|---|---|
npm install |
Installs dependencies |
npm run dev |
Starts local dev server at localhost:4321 |
npm run build |
Build your production site to ./dist/ |
npm run preview |
Preview your build locally, before deploying |
- Each presentation and module is loaded as a content collection entry
- Module IDs are generated from folder names (NOT full paths)
- When rendering a presentation with
includes:- Each module is fetched by ID from
slidesorslideModulescollection - Image paths are rewritten to absolute paths
- All content is combined with
---separators - Template variables
{{variable}}are substituted with frontmatter values
- Each module is fetched by ID from
- The combined content is rendered with Reveal.js
You must use inline JSON array syntax in frontmatter:
✅ Correct:
includes: ["title", "content", "acknowledgments"]
authors: ["Jane Doe", "John Smith"]❌ Incorrect (won't work):
includes:
- title
- content
- acknowledgmentsThis is due to how Astro's content collection glob loader parses frontmatter from /public/. The code handles both formats by detecting strings and parsing them as JSON, but multi-line YAML arrays don't parse correctly.
/public/slides/theme-demo/- Demonstrates all Reveal.js themes/public/slides/snacks-combined/- Simple example combining noodles and popcorn talks/public/slides/example-combined/- Demonstrates mixing modules and regular slides/public/slides/cooking-tips/- Standalone presentation example
- CLAUDE.md - Developer documentation and implementation details
- image_path_rebasing.md - How automatic image path rewriting works
MIT