Integrations

Stories in a site

A Récit site (this one, for instance) publishes stories with @recit/site-stories: each story gets its own page, embeds in any other page, and a per-slide search. One file is enough.

1. Declare the stories (recit.config.ts)

TypeScriptrecit.config.ts
import "@recit/site-stories/styles.css"; import { defineConfig } from "recit/config"; import * as components from "./components/index.js"; export default defineConfig({ // Site options site: { title: "Lorem Ipsum Docs" }, navigation: [{ type: "doc", file: "index" }], // Récit: the stories published by the site publish: { dir: "./stories", // default routePrefix: "/present", // default: /stories layout: "none", // "default": inside the documentation layout components, entries: [{ slug: "showcase", file: "../examples/deck-showcase/slides.md" }], }, });

defineConfig adds recitPlugin (routes, embeds, search) and recitVitePlugin (compilation) to the site: no separate site config and no vite.config.ts. Every .md or .mdx file of dir is compiled, plus the explicit entries. Each one uses the recit.config.* found next to it and is recompiled on change in development. A .md is sanitized unless the project sets security.allowHtml: true; an .mdx is never sanitized, because MDX is code, not content. See recit.config.

A site that declares recitPlugin in plugins by hand keeps it, and then adds recitVitePlugin from @recit/site-stories/vite to its own vite.config.ts.

Every story gets a route (/present/showcase), prerendered with the site. The styles are scoped to the .recit-wrapper element, so they never leak into the documentation.

2. Embed in pages

MDXmy-page.mdx
<StoryEmbed slug="showcase" slide={4} /> <StoryGallery decks={["starter", "pitch"]} />

Both components are provided by the plugin, no import needed. StoryGallery takes optional links and descriptions objects, keyed by slug.

This is the story of the examples/site-embed project, embedded with <StoryEmbed slug="lorem-ipsum" />:

Click the deck, then use ← → · 4 slides · 00:32Open fullscreen →

Languages

With an i18n section in recit.config.ts, every language of a story gets its own page. A story talk.md with a talk.fr.md variant is served at /present/talk and /fr/present/talk. With routing: "prefix-always", the default language gets a prefix too (/en/present/talk). StoryEmbed, StoryGallery and the plugin's links follow the same routes.

TypeScriptrecit.config.ts
export default defineConfig({ site: { title: "Lorem Ipsum" }, publish: { dir: "./stories", routePrefix: "/present" }, i18n: { locales: ["en", "fr"], defaultLocale: "en" }, });
  • Story pages carry <html lang dir> and hreflang / x-default links to their other languages.
  • Each prefixed language gets a home page (/fr) listing its stories.
  • A language switcher is added to the header. It keeps the current story when it exists in the other language, otherwise it goes to that language's home.
  • Pagefind builds one index per language.

The page of each story contains a hidden index: the site's Pagefind search returns the slide (/present/course#slide-7) and the deck opens right on it. Try searching for "dichotomia" (⌘K).