원클릭으로
prez
// Create beautiful presentations from any codebase. Zero-opinion slide engine — you have full React/HTML/CSS freedom inside each slide.
// Create beautiful presentations from any codebase. Zero-opinion slide engine — you have full React/HTML/CSS freedom inside each slide.
Generate, search, and create images for prez presentations. AI generation via Pollinations, photo search via Unsplash/Pexels, and SVG-to-PNG rendering.
Visually validate prez slides by generating screenshots and diffing them against a baseline. Use after editing slides.tsx to confirm slides render correctly.
| name | prez |
| description | Create beautiful presentations from any codebase. Zero-opinion slide engine — you have full React/HTML/CSS freedom inside each slide. |
| metadata | {"author":"Enriquefft","version":"1.0.0","argument-hint":"describe the presentation you want"} |
Create presentations from any codebase. Users describe what they want, you build the slides.
bunx prez-* commands resolve @enriquefft/prez from the nearest package.json. If you don't have a deck yet, scaffold one with bunx @enriquefft/prez init — that adds the package as a dependency and installs all three prez skills via the skills CLI.
If you arrived here via bunx skills add Enriquefft/prez (skills-first flow), continue with bunx @enriquefft/prez init from the project root — re-running it is idempotent (skills CLI re-symlinks the same source).
When the user asks you to create a presentation, pitch deck, slide deck, or any visual slide-based content from their project.
If no deck/ folder exists in the project, scaffold one:
bunx @enriquefft/prez init
Then cd deck && bun install && bun run dev to start the dev server at localhost:5173.
Legacy install form (kept for historical links, prefer bunx above):
curl -fsSL https://raw.githubusercontent.com/Enriquefft/prez/main/setup.sh | sh
prez init <name> --yes — non-interactive scaffold, installs Claude skills into <name>/.claude/skills/prez init <name> --yes --no-skills — for CI or when the parent repo manages its own skillsSkills are deck-local: they land inside the scaffolded deck (e.g. deck/.claude/skills/), not in the cwd you ran prez init from. This keeps skills with the presentation, not leaking into unrelated parent projects.
prez-image — generate, search, and render images (see skills/prez-image/SKILL.md)prez-validate — screenshot every slide for visual validation (see skills/prez-validate/SKILL.md). Run it after any non-trivial change to src/slides.tsx to catch overflow, contrast, or broken-image issues TypeScript can't.Three components. That's it.
<Deck> — Root containerimport { Deck, Slide, Notes } from '@enriquefft/prez'
<Deck>
<Slide>...</Slide>
<Slide>...</Slide>
</Deck>
Props:
aspectRatio?: string — default "16/9"transition?: 'none' | 'fade' | 'slide' — default 'none'className?: stringstyle?: CSSPropertiesshowFullscreenButton?: boolean — floating fullscreen toggledownloadUrl?: string | { pdf?: string; pptx?: string } — floating download button; string for single format, object for a multi-format popover (see Export below)<Slide> — One slideFull creative freedom inside. Use any HTML, CSS, Tailwind, React components, animations.
<Slide>
<div className="flex items-center justify-center h-full bg-black text-white">
<h1 className="text-8xl font-bold">Anything goes</h1>
</div>
</Slide>
Props: className, style, children
<Notes> — Presenter notesRenders nothing in normal view. Shown in presenter mode (Alt+Shift+P).
<Slide>
<h1>Revenue</h1>
<Notes>Talk about Q3 growth. Mention 180% YoY.</Notes>
</Slide>
useDeck() hookAccess deck state from inside a slide:
const { currentSlide, totalSlides, next, prev, goTo } = useDeck()
deck/src/slides.tsx — this is the ONLY file you need to touch<Slide> children:
const slides = (
<>
<Slide>...</Slide>
{/* JSX comments work here */}
<Slide>...</Slide>
</>
)
export default slides
main.tsx, use {slides} (not <Slides />):
import slides from './slides'
<Deck>{slides}</Deck>
<Slide> is 1280x720 and can contain anything — full React/HTML/CSS freedom../../src/components/Chart)<Notes> for speaker talking pointsdeck/
├── src/
│ ├── main.tsx # Entry point (don't edit)
│ ├── slides.tsx # YOUR SLIDES — edit this file
│ └── styles.css # Tailwind + custom CSS
├── package.json
├── vite.config.ts
└── index.html
prez uses 1-based slide numbers on every external surface: CLI
arguments (--slide 3), JSON event fields ("slide": 3), node-API
parameters (validateScreenshots({ slide: 3 })), log lines, and diff
reports. Internal URL hashes are 0-based (#/0 renders slide 1),
and the ?screenshot=N render mode takes a 0-based N. The Node
API encodes this at the type level with branded
ExternalSlideNumber (1-based) and InternalSlideIndex (0-based)
types; use toInternal / toExternal / asExternal / asInternal
to cross the boundary. Agents should emit and parse 1-based numbers
everywhere the user-visible surface demands.
The Deck switches behavior based on URL query params — agents should not re-implement these; prez-validate and prez-export already drive them:
?print=true — all slides stacked with page-break CSS (drives PDF export)?presenter=true — presenter-mode UI (opened by Alt+Shift+P)?screenshot=N — renders only the slide at 0-based index N, used by prez-validate and the PPTX export. Agents that need a per-slide image should call prez-validate --slide N (1-based) instead of touching this URL directly.Every prez-* binary accepts:
-h, --help — print usage and exit 0-V, --version — print <name> <version> and exit 0These are emitted via a shared CLI kit (src/cli/_cli-kit.ts), so
agents can rely on identical behavior across prez init,
prez-image, prez-export, and prez-validate.
Single entry point — bunx prez-export — drives both PDF and PPTX. See skills/prez-validate/SKILL.md for the validate-then-export loop.
bunx prez-export # Export both PDF and PPTX to ./dist/
bunx prez-export pdf # PDF only
bunx prez-export pptx # PPTX only
bunx prez-export --build # Build first, then export
bunx prez-export --url http://localhost:5173 # Export from a running server
Flags:
--output <dir> / -o <dir> — output directory (default ./dist/)--base <path> — override Vite base when it isn't /--timeout <ms> — Chrome timeout, default 30000Because the default output is ./dist/, the built SPA and the exported artifacts live side-by-side. Wire them up with downloadUrl:
<Deck
downloadUrl={{ pdf: 'deck.pdf', pptx: 'deck.pptx' }}
>
{slides}
</Deck>
Then bunx prez-export --build produces a dist/ that serves the deck and its downloads from one directory. downloadUrl as a plain string emits a single direct-download button; as an object it emits a popover with one link per format.
For programmatic pipelines (custom validators, CI gates, batch screenshotters) import from @enriquefft/prez/node. The surface covers screenshot capture (ChromeBrowser, ChromeSession, screenshotSlides), diffing (diffPng), the full validate orchestrator (validateScreenshots), and the branded slide-index helpers. See skills/prez-validate/SKILL.md for the canonical usage pattern.
See skills/prez-image/SKILL.md for image generation (AI), royalty-free search, and SVG-to-PNG rendering.
main.tsx and slides.tsx. slides.tsx is the single source of truth; main.tsx just imports and renders it inside <Deck>.src/. Put them in deck/public/ so Vite serves them at the root, then reference via <img src="/hero.png"> or Tailwind bg-[url('/hero.png')].<Slides> component. slides.tsx exports a Fragment variable; render it with {slides}, not <Slides />.If bun run dev fails with ENOENT src/slides.tsx, the scaffold didn't complete — re-run bunx @enriquefft/prez init. If a CLI exits with "Error: dist/ not found", pass --build to have it build first.