Skip to main content

Step 1: Set Things Up

Let’s start by getting Presenton up and running locally.

Clone the Repository

Open your terminal and run:
git clone https://github.com/presenton/presenton.git
cd presenton

Start Presenton

You can run Presenton in two ways: Option A: Docker (recommended for quick setup)
docker compose up development --build
Option B: Electron (for development or desktop use)
cd electron
npm run setup:env   # First time only
npm run dev
Once it’s running, open your browser and visit: http://localhost:5000 If you see the Presenton interface, you’re good to go!

Step 2: Understand How Templates Work

Presenton organizes slide designs into template groups. Each template group contains one or more slide layouts and a settings.json file that describes the group’s behavior.

Where Templates Live

All templates are located in:
app/presentation-templates
The exact path depends on how you run Presenton:
  • Docker: servers/nextjs/app/presentation-templates
  • Electron: electron/servers/nextjs/app/presentation-templates
Navigate there by running:
# For Docker setup
cd servers/nextjs/app/presentation-templates

# For Electron setup
cd electron/servers/nextjs/app/presentation-templates
Inside this folder, you’ll see multiple subdirectories — each one is a template group (e.g., general, neo-general, modern, standard, swift).

Explore a Template Group

Pick any template folder (e.g., general) and open it. Here’s what you’ll find:
  • settings.json: Contains metadata about the template group: description, whether slides are ordered, and whether it’s the default template.
  • One or more .tsx files: Each file defines a single slide layout, including its Zod schema (used by the AI to generate content) and the React component that renders the slide.
Each .tsx file must export:
  • Schema — Zod schema defining the slide data
  • layoutId — Unique identifier for the layout
  • layoutName — Display name in the UI
  • layoutDescription — Description for the AI to match content
  • Default export — The React component that renders the slide

Preview Templates in the UI

To see all available presentation templates and their slide layouts in your browser, go to: http://localhost:5000/template-preview This preview page gives you a visual overview of each template group and lets you inspect how each slide layout looks when used. When generating a presentation, you’ll be able to pick from these templates in the Outlines and Templates step of the process.

Step 3: Build Your Own Template

Now it’s time to create a custom template of your own.

Create a New Template Directory

In the presentation-templates folder, create a new directory. This will be your template group.
mkdir my-presentation-template
cd my-presentation-template
💡 The folder name (e.g., my-presentation-template) becomes the template ID used in the app.

Add a settings.json File

Create a file named settings.json with:
{
  "description": "This is my presentation template",
  "ordered": false,
  "default": false
}
  • description: Short description of this template group.
  • ordered: If true, slides are used in a fixed order (see How Slides Are Selected below). Keep false for AI-driven layout selection.
  • default: If true, this template is used by default when generating a presentation.
⚠️ Use boolean values (true/false), not strings ("true"/"false").

Step 4: Add Your First Slide Layout

Create a Layout File

Create a new .tsx file for your first slide:
touch TitleDescriptionSlide.tsx
You can use ExampleSlideLayoutTemplate.tsx in the presentation-templates folder as a minimal starting point, or copy from an existing layout like general/BasicInfoSlideLayout.tsx.

Define Schema and Component

Each slide layout needs:
  1. A Zod schema — tells the AI what data this slide expects.
  2. A React component — renders the slide visually.
Here’s a minimal example with title and description:
import * as z from "zod";

export const layoutId = "title-description-slide";
export const layoutName = "Title & Description";
export const layoutDescription =
  "A simple slide with a title and description text.";

export const Schema = z.object({
  title: z.string().min(3).max(50).default("Slide Title").meta({
    description: "Main title of the slide",
  }),
  description: z
    .string()
    .min(10)
    .max(180)
    .default("Slide description content.")
    .meta({
      description: "Content description of the slide",
    }),
});

type SchemaType = z.infer<typeof Schema>;

const SlideComponent = ({ data }: { data: Partial<SchemaType> }) => {
  return (
    <div className="w-full max-w-[1280px] max-h-[720px] aspect-video bg-white flex flex-col justify-center items-center p-10 mx-auto">
      <h1 className="text-4xl font-bold">{data.title}</h1>
      <p className="mt-6">{data.description}</p>
    </div>
  );
};

export default SlideComponent;
Tips:
  • Use .default() on each schema field so the preview renders correctly.
  • Use .meta({ description: "..." }) to help the AI generate better content.
  • Keep a consistent 16:9 aspect ratio and max-width of 1280px — layouts are exported as PDF and PPTX.

Step 5: Register Your Template in the App

Templates are not auto-discovered. You must register them in presentation-templates/index.tsx. The file is organized into five steps — follow them in order:

Step 5.1: Import Your Layouts

Find the // TODO: Step 1: Import All templates Layouts Here section and add your layout import:
// Import your layout (add with other imports)
import TitleDescriptionSlideLayout, {
  Schema as TitleDescriptionSlideSchema,
  layoutId as TitleDescriptionSlideId,
  layoutName as TitleDescriptionSlideName,
  layoutDescription as TitleDescriptionSlideDesc,
} from "./my-presentation-template/TitleDescriptionSlide";

Step 5.2: Import Your Settings

Find the // TODO: Step 2: Import template settings Here section and add:
import myTemplateSettings from "./my-presentation-template/settings.json";

Step 5.3: Create Template Entries

Find the // TODO: Step 3: Create template entries for each template section and add your template array:
export const myTemplateLayouts: TemplateWithData[] = [
  createTemplateEntry(
    TitleDescriptionSlideLayout,
    TitleDescriptionSlideSchema,
    TitleDescriptionSlideId,
    TitleDescriptionSlideName,
    TitleDescriptionSlideDesc,
    "my-presentation-template", // template group ID (folder name)
    "TitleDescriptionSlide", // file name without .tsx
  ),
];

Step 5.4: Add to allLayouts

Find the // TODO: Step 4: Combine all templates into a single array For UseCases section and add your layouts to allLayouts:
export const allLayouts: TemplateWithData[] = [
  ...neoGeneralTemplates,
  // ... other templates
  ...myTemplateLayouts,
];

Step 5.5: Add to templates

Find the // TODO: Step 5: Combine all templates into a single array For UseCases section and add your template group to the templates array:
export const templates: TemplateLayoutsWithSettings[] = [
  // ... existing template groups
  {
    id: "my-presentation-template",
    name: "My Presentation Template",
    description: myTemplateSettings.description,
    settings: myTemplateSettings as TemplateGroupSettings,
    layouts: myTemplateLayouts,
  },
];

Step 6: Preview Your Template

Visit: http://localhost:5000/template-preview You should see your template group listed. Click on it to preview your slide layout. Your template is now ready to use. Select it in the Outlines and Templates step when generating a presentation.

Adding More Slides to Your Template

Add another slide layout with title, description, image, and icon.

Create a New Layout File

touch TitleDescriptionImageIconSlide.tsx

Define the Schema with Image and Icon

Use ImageSchema and IconSchema from the default schemes for AI-generated images and icons:
import * as z from "zod";
import {
  ImageSchema,
  IconSchema,
} from "@/app/presentation-templates/defaultSchemes";

export const layoutId = "title-description-image-icon-slide";
export const layoutName = "Title, Description, Image & Icon";
export const layoutDescription =
  "A slide with title, description, image, and icon.";

export const Schema = z.object({
  title: z.string().min(3).max(50).default("Slide Title").meta({
    description: "Main title of the slide",
  }),
  description: z.string().min(10).max(180).default("Slide description.").meta({
    description: "Content description of the slide",
  }),
  image: ImageSchema.default({
    __image_url__:
      "https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823_1280.jpg",
    __image_prompt__: "A beautiful road in the mountains",
  }).meta({
    description: "Image to display in the slide",
  }),
  icon: IconSchema.default({
    __icon_url__: "/static/icons/placeholder.png",
    __icon_query__: "mountain icon",
  }).meta({
    description: "Icon to display in the slide",
  }),
});

type SchemaType = z.infer<typeof Schema>;

const SlideComponent = ({ data }: { data: Partial<SchemaType> }) => {
  return (
    <div className="w-full max-w-[1280px] max-h-[720px] aspect-video bg-white flex justify-center items-center gap-4 p-10 mx-auto">
      <div className="basis-1/2 rounded-lg overflow-hidden">
        <img
          className="w-full"
          src={data.image?.__image_url__ || ""}
          alt={data.image?.__image_prompt__ || ""}
        />
      </div>
      <div className="basis-1/2 flex flex-col justify-center items-center">
        <img
          className="w-16 h-16"
          src={data.icon?.__icon_url__ || ""}
          alt={data.icon?.__icon_query__ || ""}
        />
        <h1 className="mt-4 text-4xl font-bold">{data.title}</h1>
        <p className="mt-6">{data.description}</p>
      </div>
    </div>
  );
};

export default SlideComponent;
🖼️ ImageSchema and IconSchema are required for AI-generated images and icons. They ensure compatibility with PDF and PPTX export.

Register the New Layout

In index.tsx, add the import in Step 1 and the createTemplateEntry call in Step 3 (myTemplateLayouts array):
// Step 1: Add import
import TitleDescriptionImageIconSlideLayout, {
  Schema as TitleDescriptionImageIconSlideSchema,
  layoutId as TitleDescriptionImageIconSlideId,
  layoutName as TitleDescriptionImageIconSlideName,
  layoutDescription as TitleDescriptionImageIconSlideDesc,
} from "./my-presentation-template/TitleDescriptionImageIconSlide";

// Step 3: Add to myTemplateLayouts array
createTemplateEntry(
  TitleDescriptionImageIconSlideLayout,
  TitleDescriptionImageIconSlideSchema,
  TitleDescriptionImageIconSlideId,
  TitleDescriptionImageIconSlideName,
  TitleDescriptionImageIconSlideDesc,
  "my-presentation-template",
  "TitleDescriptionImageIconSlide"
),

Preview

Visit http://localhost:5000/template-preview — both slides should appear under your template.

Naming Layouts for Better AI Matching

Use descriptive filenames so the AI can choose the right layout for each slide. For example:
  • TitleDescriptionSlide.tsx
  • TitleDescriptionImageIconSlide.tsx
Instead of generic names like SlideLayout1.tsx or SlideLayout2.tsx.

How Slides Are Selected in a Template

When generating a presentation, Presenton selects a slide layout for each slide from the layouts in your template group.

AI-Driven Selection (ordered: false)

With ordered: false (default), the AI picks the best layout for each slide based on content. Descriptive layoutName and layoutDescription help improve matching.

Fixed Order (ordered: true)

To force a specific layout order, set ordered: true in settings.json:
{
  "description": "My template with fixed slide order",
  "ordered": true,
  "default": false
}
Then order your layouts in the myTemplateLayouts array in index.tsx — the first layout is slide 1, the second is slide 2, and so on. You can also use numeric prefixes in filenames (e.g., 1-TitleSlide.tsx, 2-ContentSlide.tsx) to keep the order clear.

Layout Best Practices

  • Aspect ratio: Use aspect-video (16:9) and max-w-[1280px] max-h-[720px] for consistent export.
  • Defaults: Every schema field should have a .default() for preview and fallbacks.
  • Meta descriptions: Add .meta({ description: "..." }) to help the AI.
  • Validation: Use .min() and .max() on strings and arrays.
  • Theme variables: Use CSS variables like var(--background-color), var(--primary-color), var(--heading-font-family) for theme support.
  • Optional branding: Support __companyName__ and _logo_url__ in data for company branding when provided.
For more examples, see ExampleSlideLayout.tsx and existing layouts in general/, neo-general/, and other template folders.