A React-based presentation framework that combines the component model of React with the presentation features of reveal.js. Designed with Quarto-compatible theming and a path toward text-forward authoring in Markdown and Org-mode.
- Hybrid Architecture: React renders content, reveal.js handles navigation
- Quarto-Compatible: SCSS variables and classes match Quarto conventions
- Positioning System: Components accept position props (
top,left,x,y) - Type-Safe: Full TypeScript support with comprehensive type definitions
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run buildThis framework supports authoring presentations in Quarto Markdown (QMD), which are then compiled into standalone React+Reveal.js HTML files.
Create a .qmd file in the examples/ directory (or anywhere):
---
title: My Presentation
author: Your Name
---
## First Slide
Content goes here.
## Second Slide
More content.Run the build script to compile your QMD file:
# Build to default output (dist/[filename]/)
npm run build:presentation -- examples/mypres.qmd
# Build to custom output location
npm run build:presentation -- examples/mypres.qmd -o dist/custom-nameThe output is a folder containing index.html and bundled assets. Open it in a
browser or deploy to any static host.
Level-2 headings (##) create new slides:
## Slide Title
Slide content here.
## Another Slide {.center}
Centered slide (use ~{.center}~ attribute).Use fenced divs with .columns and .column:
## Two Columns
:::: {.columns}
::: {.column width="50%"}
Left column content.
:::
::: {.column width="50%"}
Right column content.
:::
::::Wrap content in {.fragment} divs or apply to inline spans:
::: {.fragment}
This appears on click.
:::
::: {.fragment .fade-up}
This fades up on the next click.
:::Fragment styles: fade-in, fade-up, fade-down, fade-left, fade-right,
grow, shrink, highlight-red, highlight-green, highlight-blue
Use Quarto-style callout syntax:
::: {.callout-note}
This is a note callout.
:::
::: {.callout-tip title="Pro Tip"}
Callouts can have custom titles.
:::
::: {.callout-warning}
Warning message here.
:::
::: {.callout-caution}
Critical caution!
:::Position elements anywhere on the slide canvas:
::: {.absolute top="10%" left="5%"}
Positioned at top-left
:::
::: {.absolute bottom="10%" right="5%"}
Positioned at bottom-right
:::Standard fenced code blocks with syntax highlighting:
```typescript
const greeting = "Hello, World!";
console.log(greeting);
```Use Pandoc bracketed spans for inline styling:
[Badge Text]{.badge}
[Green Badge]{.badge style="background: #238636;"}
[Muted text]{.text-muted}Add background attributes to slide headings:
## Dark Slide {background-color="#1a1a2e"}
Content on dark background.
## Image Background {background-image="photo.jpg"}
Content over image.---
title: Sample Presentation
author: Jane Doe
---
## Welcome {.center}
[My Company]{.badge style="background: #2a76dd;"}
A brief overview of our work.
## Key Features
:::: {.columns}
::: {.column width="50%"}
::: {.callout-note title="Feature 1"}
Description of first feature.
:::
:::
::: {.column width="50%"}
::: {.callout-tip title="Feature 2"}
Description of second feature.
:::
:::
::::
## Roadmap
::: {.fragment}
Q1: Planning phase
:::
::: {.fragment}
Q2: Development
:::
::: {.fragment}
Q3: Launch
:::
## Questions? {.center background-color="#1a1a2e"}
[Contact us]{.badge} at hello@example.comBuild with:
npm run build:presentation -- examples/sample.qmd -o dist/sampleRoot presentation container. Initializes reveal.js and provides context.
<Deck
config={{
hash: true,
transition: 'slide',
controls: true,
}}
>
{/* Slides go here */}
</Deck>Individual slide. Maps to a reveal.js <section>.
<Slide
background={{ color: '#1a1a2e' }}
center
notes="Speaker notes here"
>
<Heading level={1}>Title</Heading>
</Slide>| Prop | Type | Description |
|---|---|---|
background | string or object | Background color/image/video |
center | boolean | Vertically center content |
transition | TransitionType | Slide transition style |
notes | string | Speaker notes |
autoAnimate~| ~boolean | Enable auto-animate |
Multi-column layout using flexbox.
<Columns gap="2rem" vcentered>
<Column width="half">Left content</Column>
<Column width="half">Right content</Column>
</Columns>Width presets: half, third, two-thirds, quarter, three-quarters
Position elements anywhere on the slide canvas.
{/* Using top/left */}
<Absolute top="10%" left="20%">
<Text>Positioned</Text>
</Absolute>
{/* Using x/y shorthand */}
<Absolute x={100} y={200} width={300}>
<Image src="photo.jpg" />
</Absolute>
{/* Centered */}
<Absolute x="50%" y="50%" style={{ transform: 'translate(-50%, -50%)' }}>
<Heading level={1}>Centered</Heading>
</Absolute>Overlay content for sequential reveals.
<Stack>
<Fragment><Text>First</Text></Fragment>
<Fragment><Text>Second</Text></Fragment>
<Fragment><Text>Third</Text></Fragment>
</Stack>Incremental content reveals.
<Fragment style="fade-up">Appears on click</Fragment>
<Fragment style="highlight-blue" index={0}>Same time as first</Fragment>Fragment styles: fade-in, fade-up, fade-down, grow, shrink,
highlight-red, highlight-green, highlight-blue, etc.
Typography with Quarto-compatible sizing.
<Heading level={1}>Main Title</Heading>
<Heading level={2} className="subtitle">Subtitle</Heading>Paragraphs with size, alignment, and styling props.
<Text size="lg" align="center">Large centered text</Text>
<Text muted>Secondary information</Text>
<Text weight="bold" primary>Emphasized text</Text>Syntax-highlighted code blocks.
<Code language="typescript" lineNumbers caption="Example">
{`const x = 42;
console.log(x);`}
</Code>Quarto-style callout boxes.
<Callout type="note">Informational note</Callout>
<Callout type="tip" title="Pro Tip">Helpful advice</Callout>
<Callout type="warning">Be careful!</Callout>
<Callout type="caution">Critical warning</Callout>Customize the theme by overriding Quarto-compatible SCSS variables:
// Typography
$presentation-font-size-root: 40px;
$presentation-heading-font-weight: 600;
$font-family-sans-serif: system-ui, sans-serif;
// Colors
$body-bg: #fff;
$body-color: #222;
$primary: #2a76dd;
$link-color: #2a76dd;
// Callouts
$callout-color-note: #0969da;
$callout-color-tip: #238636;
$callout-color-warning: #9a6700;
$callout-color-caution: #cf222e;
// Code
$code-block-bg: #f7f7f7;
$code-block-font-size: 0.55em;Layout and utility classes available:
.columns,.column,.is-half,.is-third.absolute- for positioned elements.r-stack- stacked overlays.text-sm,.text-lg,.text-xl- size utilities.text-muted,.text-primary- color utilities.font-bold,.font-semibold- weight utilities
Access the reveal.js API instance.
function NavigationButtons() {
const reveal = useReveal()
return (
<>
<button onClick={() => reveal?.prev()}>Previous</button>
<button onClick={() => reveal?.next()}>Next</button>
</>
)
}Get current presentation state.
function SlideIndicator() {
const state = useSlideState()
return <span>Slide {state.indexh + 1}</span>
}Subscribe to reveal.js events.
useSlideEvent('slidechanged', (event) => {
console.log('Now on slide', event.indexh)
})Org-mode files (.org) are also supported with similar capabilities.
npm run build:presentation -- examples/mypres.org -o dist/mypresLevel-1 headings (*) create slides:
* First Slide
Content here.
* Second Slide
:PROPERTIES:
:CENTER: t
:END:
Centered slide.
Use #+BEGIN_COLUMNS blocks:
#+BEGIN_COLUMNS
#+BEGIN_COLUMN 50%
Left column content.
#+END_COLUMN
#+BEGIN_COLUMN 50%
Right column content.
#+END_COLUMN
#+END_COLUMNS
Use #+ATTR_REVEAL: :frag or wrap in blocks:
#+BEGIN_FRAGMENT
This appears on click.
#+END_FRAGMENT
#+BEGIN_FRAGMENT fade-up
This fades up.
#+END_FRAGMENT
Use #+BEGIN_CALLOUT blocks:
#+BEGIN_CALLOUT note "Important Note"
This is a note callout with title.
#+END_CALLOUT
#+BEGIN_CALLOUT tip
A helpful tip.
#+END_CALLOUT
Use #+BEGIN_ABSOLUTE blocks:
#+BEGIN_ABSOLUTE top=10% left=5%
Positioned content.
#+END_ABSOLUTE
Use @@class:text@@ syntax for inline styling:
@@badge:Badge Text@@
@@text-muted:Muted text@@
Standard Org source blocks:
#+BEGIN_SRC typescript
const x = 42;
console.log(x);
#+END_SRC
#+TITLE: Sample Presentation
* Welcome
:PROPERTIES:
:CENTER: t
:END:
@@badge:My Company@@
A brief overview.
* Key Features
#+BEGIN_COLUMNS
#+BEGIN_COLUMN 50%
#+BEGIN_CALLOUT note "Feature 1"
First feature description.
#+END_CALLOUT
#+END_COLUMN
#+BEGIN_COLUMN 50%
#+BEGIN_CALLOUT tip "Feature 2"
Second feature description.
#+END_CALLOUT
#+END_COLUMN
#+END_COLUMNS
* Questions?
:PROPERTIES:
:CENTER: t
:BACKGROUND_COLOR: #1a1a2e
:END:
Contact us at hello@example.com
A custom ox-react.el backend for direct export from Emacs without the
intermediate parsing step.
src/ ├── components/ # React components │ ├── Deck.tsx # Root component │ ├── Slide.tsx # Slide container │ ├── Heading.tsx # Typography │ ├── Text.tsx # Text content │ ├── Absolute.tsx # Positioning │ ├── Columns.tsx # Layout │ ├── Fragment.tsx # Reveals │ ├── Code.tsx # Code blocks │ └── Callout.tsx # Callout boxes ├── hooks/ # React hooks ├── parser/ # Source file parsers │ ├── qmd/ # QMD (Quarto Markdown) parser │ ├── org/ # Org-mode parser │ └── renderer.tsx # AST to React renderer ├── styles/ # SCSS theming ├── types/ # TypeScript types └── demo/ # Demo presentation examples/ ├── demo.qmd # QMD demo presentation └── demo.org # Org-mode demo presentation scripts/ └── build-presentation.ts # Build script for presentations
MIT