Guides

Slides: decks, slide layouts and exports

The add-on @skaile/forge-editor-slides: the Block types deck and slide, the pure HTML render contract, the render targets reveal-html, pptx and marp-md, the transfer transforms that turn blocks into slides, and the slide preview in the inspector.

The add-on @skaile/forge-editor-slides adds presentations to the editor. A deck is a Typed block of the Block type deck. Its body is slides, Typed blocks of the Block type slide. A slide has a title, a description (the subtitle of a title slide), a body, and three Block properties: layout, notes and status.

PieceWhat it doesWhere
slideBlockTypes(locale)the Block types deck (a container that accepts slide) and slideeditor and server
renderSlide, renderDeckpure HTML and CSS of a slide or a deckanywhere
slidesTargets()the render targets reveal-html, pptx, marp-mdserver
slidesTransforms()the transfer transforms slides-from-blocks and slides-from-blocks-aieditor
slidesFeature()the block actions „Folien-Layout“ and „Folie anhängen“, the slide preview in the inspectoreditor

The add-on imports the library through its public entries only. The entry @skaile/forge-editor-slides/server reaches no editor code: use it in a server program.

Install and wire

import { defaultBlockTypes } from '@skaile/forge-editor/composites'
import { copyTransform } from '@skaile/forge-editor/transfer'
import { bulletsTransform, summaryTransform } from '@skaile/forge-editor/integrations/composite-ai'
import { slideBlockTypes, slidesTransforms } from '@skaile/forge-editor-slides'
import { slidesFeature } from '@skaile/forge-editor-slides/vue' // or '/react'

const editor = composeEditor({
  content: 'rich',
  interaction: [...INTERACTION.full, 'transfer'],
  // The Block types of the editor: the built-in ones and the two of this add-on.
  blockTypes: [...defaultBlockTypes(t), ...slideBlockTypes('de')],
  add: [slidesFeature()],
  // A `transfer.transforms` list replaces the default list, so name the built-in transforms too.
  transfer: { transforms: [copyTransform, summaryTransform, bulletsTransform, ...slidesTransforms({ locale: 'de' })] },
})
import { createRenderer, markdownTarget } from '@skaile/forge-editor/adapters/render'
import { slideBlockTypes, slidesTargets } from '@skaile/forge-editor-slides/server'

// installEditorServer({ ports: { render }, blockTypes })
export const render = createRenderer([markdownTarget(), ...slidesTargets()])
export const blockTypes = [...serverBlockTypes, ...slideBlockTypes('de')]

The Block types go to the server too: the AI chat then knows what a deck is, and the server builds the schema from the same list (Block types).

The slide model

Part of a slideSource
titlethe title row of the Typed block (hidden on a blank slide)
subtitlethe description row (shown on a title slide)
bodythe blocks after the chrome rows: paragraphs, headings, lists, quotes, code, images, rules
layoutBlock property, a select: title, bullets (default), two-column, image, blank
notesBlock property, a textarea: speaker notes
statusBlock property, a status: draft (todo), ready (doing), final (done)

Layouts decide the arrangement only. two-column splits the body at the first horizontal rule, or after the first half of its blocks. image shows the first image above the other blocks. A deck ignores body blocks that are not slides.

The render contract

renderSlide(slide, layout?) returns one slide in a 16:9 frame. renderDeck(deck, { css? }) returns all slides, one frame each, in front of a <style> element with SLIDE_CSS. Both are pure: the same JSON gives the same string, and every text is escaped (links keep only http, https, mailto and relative targets; an image source must be web, relative or a data:image/ address).

import { renderDeck } from '@skaile/forge-editor-slides/server'

const html = renderDeck(deckJson, { css: false })
// <div class="sk-deck" data-title="Quartalsbericht">
// <div class="sk-frame"><section class="sk-slide sk-slide--title" data-layout="title"><h1>Willkommen</h1>…

Nothing in the contract knows reveal.js, PowerPoint or Marp. Every target reads the same slide blocks (blocksOf) and builds its own format.

Render targets

slidesTargets(options) gives three targets for a Block of the type deck. A render of any other block fails with a message the client shows.

TargetFileWhat it holds
reveal-html.htmla reveal.js 6 page: one <section> per slide, the speaker notes as <aside class="notes"> (speaker view with S). reveal.js loads from REVEAL_CDN (jsDelivr); options.reveal.cdn points it at your own copy, options.reveal.theme picks the theme
pptx.pptxPowerPoint through PptxGenJS: the title as one text box, every block of the body as one text box, two stacks for two-column, pictures for data: images, speaker notes on the notes page
marp-md.mdMarp Markdown: front matter, --- between slides, the layout as a Marp class, speaker notes as HTML comments

A pptx text box has a height from an estimate of the wrapped lines; PowerPoint shrinks text that does not fit when a person edits it. An image with a source other than a data: address shows its alt text, because the render cannot fetch it.

Make slides from blocks

Drag a block into a deck and pick a transform, or drop it in the document and pick Als Folien einfügen. Both transforms insert a new deck Typed block after the drop position, titled „Folien: “, with one slide per block of the source:

  • A Typed block with nested Typed blocks gives one slide per nested block, depth first. Its own content comes first as a slide titled after it.
  • Any other block is split at its headings: a heading starts a slide.
  • A block without headings is one slide.
TransformDerivation methodNeeds
slides-from-blockslink: the structure is copiednothing
slides-from-blocks-aiai:slides: a model writes the slides from the block's Markdownthe ai capability

The new deck records a Derivation that names the source block. inputHash is the hash of the source's body Markdown at that moment (the snapshot) and outputHash is the hash of the deck's body, so the Derivation badge shows the deck as stale when the source changes and as edited when the deck body changes. The AI transform also records activity (a request id and the model). A drag of a block that has no block id inserts the deck without a Derivation.

The AI transform asks for Markdown: every slide starts with ## Title, bullets follow, optional lines Layout: … and Notizen: … set the layout and the speaker notes. The answer is parsed by slideDraftsFromMarkdown; an answer without a slide shows a notice and inserts nothing.

In the editor

slidesFeature() of /vue or /react adds:

  • the block action Folien-Layout on a slide (a menu of the layouts; layouts in the feature options narrows it) and Folie anhängen on a deck,
  • the section Folienvorschau in the Typed block inspector and details card: a slide shows its 16:9 preview, layout buttons and notes; a deck shows all slides. The component is SlideView.

The framework-free slidesFeature() of the package entry has the block actions without the preview.

Strings

The add-on keeps its own small catalog in German and English (SLIDES_MESSAGES, createSlidesTranslator(locale, overrides)), because the library's message keys are closed over the library's own strings. slideBlockTypes(locale) and slidesTransforms({ locale }) take the language; the feature and the preview read it from the editor context.

Copyright © 2026