TSX elements and templates

@office-kit/pptx-dsl turns typed TSX into native PPTX objects through @office-kit/pptx. It has its own JSX runtime; it does not use React, Vue, the DOM, or CSS layout. To install and preview a project, follow

Create slides with AI first. To try TSX without installing anything, open the REPL's TSX tab.

A complete deck

Replace the generated deck.tsx with the following, then save:

import { Presentation, Slide, Text, Chart } from '@office-kit/pptx-dsl';

export default (
  <Presentation>
    <Slide background="#15171C">
      <Text x={1} y={2.5} width={11} height={1} size={44} bold color="#FFFFFF">
        Quarterly business review
      </Text>
    </Slide>
    <Slide background="#FFFFFF">
      <Text x={0.9} y={0.5} width={11.5} height={0.8} size={30} bold>
        Revenue by quarter
      </Text>
      <Chart
        x={0.9} y={1.7} width={11.5} height={5}
        spec={{
          kind: 'column',
          categories: ['Q1', 'Q2', 'Q3', 'Q4'],
          series: [{ name: 'Revenue', values: [120, 180, 240, 300], color: '#E5481F' }],
        }}
      />
    </Slide>
  </Presentation>
);

Export a Presentation as the module’s default export. The default canvas is 16:9, approximately 13.333 × 7.5 inches. Positions and dimensions (x, y, width, height) are in inches; font sizes and stroke widths are in points. Child order is drawing order. Geometry is explicit; there is no automatic layout.

Native elements

ElementMain inputs
PresentationOptional source PPTX bytes, size, theme, mode.
SlideOptional background, notes, layout, source from, or edit target.
TextRequired bounds; literal children or rich paragraphs; text formatting such as size, bold, color; bullets and paragraphSpacing; a bullet and level per entry of paragraphs.
ShapeRequired bounds and preset; fill, stroke, effects, optional text with align and anchor.
LineEnd points x1, y1, x2, y2; color and width.
GroupTwo or more visual elements that move and resize as one object; optional name.
ImageRequired bounds and byte data; optional format and fit.
MediaRequired bounds; kind video / audio with byte data, or online with a url; optional poster.
ChartRequired bounds and core ChartSpec in spec.
TableRequired bounds and rows of strings or cell objects (text or rich paragraphs, colSpan, rowSpan); column widths, row heights, cell/header styles, stripe fill, and a per-cell styleCell callback. Cell styles cover fill, text format, anchor, alignment and borders.
FillExisting shape target, replacement text children, optional format and autoFit.
RemoveExisting shape target.
RawDeferred callback for public core APIs; optional explicit scope.

Use editor completion on each element to see its typed props. The

core API reference documents types such as `ChartSpec` and `ParagraphSpec`; it is the function-level API, not a list of JSX components.

Reuse elements with ordinary TypeScript functions, arrays, fragments, conditions and .map(). A separate high-level component library is not included. Images can be loaded in the Node authoring workflow with readFile(new URL('./image.png', import.meta.url)); pass the resulting bytes to Image through data.

Edit or compose a template

First run npx --no-install office-pptx inspect template.pptx in an initialized project. It prints slide indices, part names, layouts, shape names and IDs. Missing or ambiguous references are errors.

ReferenceSupported selectors
SlideZero-based index, exact title, or part.
LayoutExact name, type, or part.
ShapeExact name, id, or placeholder with optional type and/or idx.

With Presentation source={bytes}, the default is mode="edit". A Slide target={{ index: 0 }} edits the original first slide while unmentioned slides remain. Fill replaces only the selected shape’s text; formatting changes are explicit. Existing template masters and layouts remain in the package.

To deliberately replace the slide sequence, use compose mode:

import { readFile } from 'node:fs/promises';
import { Presentation, Slide, Fill } from '@office-kit/pptx-dsl';

const source = await readFile(new URL('./template.pptx', import.meta.url));

export default (
  <Presentation source={source} mode="compose">
    <Slide from={{ index: 0 }}>
      <Fill target={{ name: 'Title 1' }}>Opening</Fill>
    </Slide>
    <Slide from={{ index: 0 }}>
      <Fill target={{ name: 'Title 1' }}>Closing</Fill>
    </Slide>
  </Presentation>
);

This produces two copies of the original first slide and removes the original sequence. Replace Title 1 with a real target. Source references always refer to the original deck, and every rebuild loads the source fresh. Source decks keep their original dimensions; size applies only to new presentations.

Preserving existing OOXML is different from exposing every feature as a typed DSL element. Unknown content should remain in the package even if the preview cannot render it. Cross-presentation imports are not offered as a lossless path.

Raw escape hatch

Use Raw when the core library supports a capability that has no declarative prop. It runs after normal construction, in declaration order, before compose mode removes original slides. Callbacks can be async.

import { setSlideTransition } from '@office-kit/pptx';
import { Presentation, Slide, Text, Raw } from '@office-kit/pptx-dsl';

export default (
  <Presentation>
    <Slide>
      <Text x={1} y={1} width={10} height={1} size={32}>Welcome</Text>
      <Raw
        scope="slide"
        apply={({ slide }) => {
          setSlideTransition(slide, { effect: 'fade', speed: 'med' });
        }}
      />
    </Slide>
  </Presentation>
);

scope="presentation", scope="slide", and scope="shape" require the corresponding context and check nesting. Without scope, slide/shape handles are optional. Raw executes trusted local code, as do other imports in the TSX file. The static SVG preview does not play the transition in this example.

Embedding compile() in your own build

An office-pptx init project comes with a build that records source locations and a check script that runs tsc --noEmit. A host that calls compile() from its own build has to arrange both.

  • Build with the dev JSX transform (TypeScript "jsx": "react-jsxdev", esbuild jsx: 'automatic', jsxDev: true). Only then does a compile() error start with file:line:col <Element>: for each enclosing element.
  • Run tsc in strict mode on authored modules. The runtime ignores a prop it does not know, so type checking is the only thing that rejects one.
  • compile(root) takes the root Presentation element and resolves to the core presentation; savePresentation from @office-kit/pptx gives the .pptx bytes. Template bytes go in through <Presentation source={bytes}>.

Package responsibilities and current limits

PackageResponsibility
@office-kit/pptxRead, change, validate and serialize the PPTX/OOXML package.
@office-kit/pptx-dslTyped TSX elements and compilation to the core model.
@office-kit/pptx-previewRendering; separate from authoring and preservation.
@office-kit/pptx-devNode CLI: project creation, watch, viewer, inspection and export.

The DSL is initial coverage, not complete PPTX coverage. Typed master/layout creation, animations, transitions, connectors attached to shapes (Line is a free-standing straight line), media playback, and merged or rich-text table cells need additional elements or core work. Raw can use available core functions, but does not count as typed declarative coverage.