/docs/authoring
Author an MDX deck
Use the Markdown, MDX, and deck-specific syntax supported by the compiler.
Keep each deck in its own directory
Use decks/<slug>/deck.mdx. The directory name becomes the URL slug, so decks/launch/deck.mdx appears at /decks/launch.
The opening --- block contains deck metadata. Later separators start new slides, so a bare --- cannot be used as a thematic break inside a slide.
---
title: Hono at the edge
transition: fade
---
# Hono at the edge
---
title: Runtime boundary
layout: statement
---
Node for I/O. Hono for routes.Write slide content with GFM and MDX
Use CommonMark plus GFM tables, task lists, strikethrough, and autolinks. MDX adds JSX elements and expressions.
# Runtime boundary
Use **bold**, *emphasis*, `inline code`, and [links](https://hono.dev/).
- Node for build-time I/O
- Hono for runtime routes
- [x] Compile the deck
- [ ] Deploy the Worker
| Layer | Responsibility |
| :-- | :-- |
| Node | Build-time I/O |
| Hono | Runtime routes |
~~Filesystem access at runtime~~
> Keep the Worker bundle filesystem-free.
```ts
const runtime = "Hono"
```- Fenced code
- Add a language such as
ts,tsx, orcssafter the opening fence to enable syntax highlighting in the slide. - MDX
- Use JSX components, props, and expressions. Keep
importandexportstatements at the top of the file.
Separate deck and slide metadata
Opening frontmatter applies to the deck. Frontmatter immediately after a slide separator applies only to that slide. Unknown keys remain in meta but produce a warning.
- Deck
title,description,author,tags,date,theme,transition,transitionDuration,transitionEasing,draft,assets,presenter- Slide
title,layout,class,notes,background,transition,transitionDuration,transitionEasing- Transitions
none,fade,fade-out,slide-left,slide-right,slide-up,slide-down,view-transition- Layouts
default, centeredcover, and centeredstatementare built in. A custom name becomes alayout-<name>class that can be styled intheme.css.
Reveal content one step at a time with fire
Add fire to a component to reveal it in source order. Use :::fire for Markdown and :::fire{each="item"} for list items. Use <Fire> to group multiple JSX elements into one step. The compiler removes fire before rendering the component.
<Card fire />
<Chart fire="scale" at="2" />
:::fire{at="+2"}
Markdown block
:::
:::fire{each="item" depth="2" every="2"}
- Parent one
- Child one
- Parent two
- Child two
:::
<Fire effect="fade-up">
<Heading />
<Description />
</Fire>The staged-reveal model and the at, depth, and every options draw in part on Slidev's v-click and v-clicks. The syntax and implementation are specific to hono-decks; compatibility with Slidev is not guaranteed. at="2" sets an absolute position, while at="+2" advances two steps from the current position. For lists, depth selects the nested levels and every sets how many items appear per step. Both default to 1.
Choose an effect
Use fire="scale" on a component and effect="scale" with :::fire. Built-ins are none, fade, fade-up, and scale. Define custom effects in theme.css.
<Chart fire="blur-in" />
:::fire{effect="blur-in"}
Markdown block
:::[data-fire-effect="blur-in"] {
--fire-transform: scale(.98);
--fire-filter: blur(12px);
--fire-duration: .32s;
--fire-easing: ease-out;
}Add notes for the presenter
Text under notes: | and MDX comments are combined into presenter notes and omitted from the slide. Every MDX comment is treated as a note, even if you intended it only as a source-code comment.
---
notes: |
Explain why generation runs before deployment.
---
# Build once, serve anywhere
{/* Mention the generated module boundary. */}Start with server components
Named exports from components/index.tsx are available as tags in that deck's MDX. Registries are deck-local, so the same component name can exist in another deck.
// decks/welcome/components/index.tsx
export function Metric(props: { value: string; label: string }) {
return <figure>
<strong>{props.value}</strong>
<figcaption>{props.label}</figcaption>
</figure>
}<Metric value="18 ms" label="Response time" />Use an island only for browser interaction
Use components/client/index.tsx only for components that need clicks or local state. The compiler generates and serves the client entry, so ordinary applications do not add a separate asset route.
// decks/welcome/components/client/index.tsx
import { useState } from "hono/jsx/dom"
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
}Reference assets and embeds explicitly
Reference images under decks/launch/assets/ with paths relative to deck.mdx. Explicit directives are available for youtube, x, card, embed, and iframe.

@[youtube](https://www.youtube.com/watch?v=VIDEO_ID)
@[x](https://x.com/honojs/status/POST_ID)
@[card](https://hono.dev/docs/)
@[embed](https://example.com/demo)
@[iframe](https://example.com/demo)A bare URL on its own line becomes a normal link. Configure CSP and allowed origins before publishing external iframes. To serve assets from R2, use withR2Assets() without changing MDX paths.
Add a deck-local theme
Place theme.css beside deck.mdx to style only that deck. Start with the provided CSS variables, then add selectors for specific content.
/* decks/welcome/theme.css */
:root {
--hono-decks-accent-color: #ff5b1a;
}
.slide h1 {
text-wrap: balance;
}Preview changes with the dev command
npm run devWhile the Vite or Wrangler dev command is running, saved MDX is recompiled automatically and the browser updates after a successful compile. Use npm run decks:compile in CI or when checking the compiler by itself. Compile errors identify the source file and slide number.
After the deck renders, continue to routes and UI for public surfaces or configuration for bindings and R2.