BUSINESS REVIEW

Sales Performance Update

Q2 2026 · North America Region · Prepared by Revenue Ops

EXAMPLE DATA
Executive Snapshot

Replace placeholders with the current review period actuals.

Quarterly Sales vs. Plan

Example chart — bound to the same sampleData object that drives the slide.

Page 1
Download the demo PPT

How to wire data into a PowerPoint template

Two practical patterns — from the simplest “a designer hands me a finished .pptx, code just swaps the numbers” to a fully code-built deck. Each has a flow diagram so you can see where the data and the file move. The buttons at the top of the page are working demos of both.

I want to…UseEffort
Have a designer own the layout; pull numbers from a database, JSON, or form. Method 1{{token}} swap on a template .pptx. 1–2 hrs
Let users click a button on my web page / app and download a fresh deck built from data. Method 2 — build the slide in JavaScript (PptxGenJS). 2–3 hrs

1 Bind web data into a template (placeholder swap)

The template uses double-brace tokens like {{REVENUE}} and {{PIPELINE}}. The pattern is the same whether you do it in the browser (JSZip), Node.js (docxtemplater + pptx module), or Python (python-pptx): load the file, walk every text run, and replace the tokens with values from your data source. The Download from template button above is a working browser-only implementation — on load it looks for template/SalesReview-Template.pptx in this folder and uses it automatically; if it isn’t there, an in-memory built-in template is used instead. Here’s the same idea in Python:

Your data JSON / DB / API Template.pptx with {{TOKEN}} placeholders Token swap JSZip / python-pptx / docxtemplater Filled deck .pptx

Designer owns the layout; code owns the numbers. Moving shapes in the template doesn’t require a code change.

# Python example using python-pptx — fills {{TOKEN}} placeholders in an existing .pptx
# pip install python-pptx
from pptx import Presentation

# 1. The data you want to inject. Keys MUST match the {{TOKEN}} names in the template.
#    In production this dict would come from your DB / API / form submission.
data = {
    "REVENUE":         "$12.4M",
    "REVENUE_DELTA":   "+8.2% YoY",
    "PLAN_ATTAINMENT": "104%",
    "PIPELINE":        "$18.1M",
    "WIN_RATE":        "36%",
}

# 2. Open the designer-built template (the .pptx that contains the {{TOKEN}} strings).
prs = Presentation("SalesReview-Template.pptx")

# 3. Walk every slide → every shape → every paragraph → every text run, and
#    replace any {{TOKEN}} we recognize. Working at the "run" level preserves
#    the original font, size, color, and bold/italic formatting from the template.
for slide in prs.slides:
    for shape in slide.shapes:
        if not shape.has_text_frame: continue   # skip pictures, charts, lines, etc.
        for para in shape.text_frame.paragraphs:
            for run in para.runs:
                for k, v in data.items():
                    run.text = run.text.replace(f"{{{{{k}}}}}", v)

# 4. Save under a new name so the original template is never overwritten.
prs.save("SalesReview-Q2-2026.pptx")

2 Generate a .pptx straight from the browser (PptxGenJS)

For dashboards where users click a button and download a ready-made deck, you don’t need a template file at all — PptxGenJS builds a fully native PowerPoint file in JavaScript. The Generate a .pptx from this page button above runs exactly this code:

Your web page sampleData (JS object) PptxGenJS builds slides in-browser Download .pptx no server needed

Pure-client generation. Great when there’s no backend — or you don’t want one in the loop.

// JavaScript example using PptxGenJS — builds a .pptx in the browser from data.
// Loaded via <script src="...pptxgen.bundle.js"> — no build step / no server needed.

// 1. Create the deck and pick a slide size. LAYOUT_WIDE = 13.33" x 7.5" (16:9).
//    Other options: "LAYOUT_4x3" (10"x7.5"), "LAYOUT_16x9" (10"x5.625"), or custom inches.
const pptx = new PptxGenJS();
pptx.layout = "LAYOUT_WIDE";

// 2. (Optional) Define a brand theme in one place so every shape reuses it.
//    Colors are 6-digit hex strings WITHOUT the leading "#". Sizes are in points.
const theme = {
  accent:    "005EB8",   // brand blue — swap to your own (e.g. "D71920" for red)
  deep:      "003B71",   // deep navy for table headers
  text:      "1B1F24",   // near-black body text
  muted:     "5C6670",   // secondary / label gray
  green:     "2E7D32",   // positive delta
  fontTitle: "Aptos Display", // big headings (any installed font name works)
  fontBody:  "Aptos"          // body text — try "Calibri", "Segoe UI", "Arial", etc.
};

// 3. Add a slide. You can set a solid background or a hex color.
const slide = pptx.addSlide();
slide.background = { color: "FFFFFF" };   // e.g. "F5F8FC" for soft blue page

// 4. addText() options control color, size, font, alignment, etc.
//    Most-used keys: x, y, w, h (inches), fontSize (pt), fontFace, color (hex),
//    bold, italic, underline, align ("left"|"center"|"right"), valign, charSpacing.
slide.addText("BUSINESS REVIEW", {
  x:0.5, y:0.4, w:5, h:0.3,
  fontSize:10, bold:true,
  color: theme.accent, fontFace: theme.fontBody,
  charSpacing:2                // 0.1pt letter spacing — try 0 to disable
});
slide.addText(sampleData.title, {
  x:0.5, y:0.7, w:9, h:0.6,
  fontSize:28, bold:true,         // bump to 36 for a bigger headline
  color: theme.text, fontFace: theme.fontTitle
});

// 5. KPI tiles — a single addText() call can hold multiple text runs,
//    each with its own font / color / size. Great for label-over-value tiles.
sampleData.kpis.forEach((k, i) => {
  const col = i % 2, row = Math.floor(i/2);
  slide.addText(
    [
      { text:k.label+"\n", options:{ fontSize:9,  bold:true, color: theme.muted } },
      { text:k.value+"\n", options:{ fontSize:22, bold:true, color: theme.text  } }, // raise to 28 for emphasis
      { text:k.delta,        options:{ fontSize:10, bold:true, color: theme.green } }  // red "B23A48" if negative
    ],
    {
      x: 0.5 + col*1.85, y: 2.6 + row*1.15, w:1.75, h:1,
      fill: { color:"FFFFFF" },                              // tile background
      line: { color:"C8D3E0", width:0.5 },                  // border color + thickness (pt)
      fontFace: theme.fontBody, valign:"middle"
    }
  );
});

// 6. addShape() draws a native PowerPoint shape — perfect for accents / dividers.
slide.addShape(pptx.ShapeType.rect, {
  x:0.5, y:1.6, w:0.4, h:0.05,
  fill:{ color: theme.accent }, line:{ color: theme.accent }
});

// 7. Native PowerPoint chart from the same sample data.
//    chartColors[] sets the series colors in order. showLegend / legendPos control the legend.
slide.addChart(pptx.ChartType.bar, [
  { name:"Plan",   labels:sampleData.chart.labels, values:sampleData.chart.plan   },
  { name:"Actual", labels:sampleData.chart.labels, values:sampleData.chart.actual }
], {
  x:5, y:2.2, w:7.5, h:3,
  chartColors:["C8D3E0", theme.accent],   // one color per data series
  showLegend:true, legendPos:"b",           // "b"=bottom, "t"=top, "l"=left, "r"=right
  legendFontSize:9, legendFontFace: theme.fontBody,
  catAxisLabelFontSize:9, valAxisLabelFontSize:9,
  catAxisLabelColor: theme.muted, valAxisLabelColor: theme.muted
});

// 8. Trigger the browser download. Use pptx.write({outputType:"blob"}) instead
//    if you want to email / upload the file rather than download it.
pptx.writeFile({ fileName:"SalesReview.pptx" });

What this demo proves

Use ChatGPT / Claude to start your own version click to expand

Two copy-paste prompts — one per method on this page. Replace every [bracketed] placeholder with details from your own deck / data and paste the result into ChatGPT, Claude, or any code-capable LLM.

Prompt 1 — Token-swap an existing .pptx (Method 1)

I want a working script that fills {{TOKEN}} placeholders in a PowerPoint
template with data from my application.

Language / runtime:  [Python with python-pptx  |  Node.js  |  Browser JS with JSZip]
Template file path:  [path/to/Template.pptx]
Output file path:    [path/to/Output-{{DATE}}.pptx]

Data source (describe the shape, or paste a small sample):
[ e.g. JSON object, SQL query result, REST API response, CSV row ]

Tokens I need to replace (token name -> what it should be filled with):
  {{TITLE}}          -> [report title, e.g. data.title]
  {{SUBTITLE}}       -> [period / scope, e.g. "Q2 2026 · NA Region"]
  {{REVENUE}}        -> [formatted currency, e.g. f"${data.revenue:,.1f}M"]
  {{REVENUE_DELTA}}  -> [signed % vs prior period]
  {{ROW1_METRIC}}    -> [table row 1 label]
  {{ROW1_CURRENT}}   -> [table row 1 current value]
  [ ...add as many tokens as your template uses... ]

Requirements:
  1. Walk every slide AND every notes slide so no tokens are missed.
  2. Replace tokens at the text-run level so the template's fonts, sizes,
     colors, and bold/italic styling are preserved.
  3. Leave unknown {{TOKENS}} alone (don't crash) so the author can spot
     them in the output.
  4. Never overwrite the original template — always write to a new file.
  5. Add short comments above each step so I can extend it later.

Optional extras:
  [ ] also fill {{TOKENS}} inside grouped shapes
  [ ] log how many tokens were swapped per slide
  [ ] expose CLI flags for input/output paths

Prompt 2 — Generate a .pptx from code with PptxGenJS (Method 2)

Build me a PptxGenJS function that generates a .pptx in the browser
(no server, no template file) from JavaScript data. I want to drop it
into a button click handler.

Output: one self-contained function `generateDeck(data)` that ends with
`pptx.writeFile({ fileName: ... })`.

Slide size:  [LAYOUT_WIDE 13.33"x7.5"  |  LAYOUT_4x3 10"x7.5"  |  LAYOUT_16x9 10"x5.625"]

Brand theme (6-digit hex, no leading "#"):
  accent:    [005EB8]
  deep:      [003B71]
  text:      [1B1F24]
  muted:     [5C6670]
  positive:  [2E7D32]
  negative:  [B23A48]
Fonts:
  title:     [Aptos Display]
  body:      [Aptos]

Data shape I'll pass in:
{
  title:    "[Report title]",
  subtitle: "[Period / scope]",
  kpis: [
    { label: "[KPI label]", value: "[value]", delta: "[+/-x%]" }
    // ...as many as needed
  ],
  chart: {
    labels: ["Q1", "Q2", "Q3", "Q4"],
    series: [
      { name: "[Plan]",   values: [/* numbers */] },
      { name: "[Actual]", values: [/* numbers */] }
    ]
  },
  table: [
    { metric: "[name]", current: "[v]", prior: "[v]", variance: "[v]", notes: "[v]" }
    // ...
  ]
}

Slide layout (left to right, top to bottom):
  1. Tiny uppercase kicker top-left ("[BUSINESS REVIEW]")
  2. Large bold title bound to data.title
  3. Smaller muted subtitle bound to data.subtitle
  4. 2x2 KPI tile grid on the left (label / value / colored delta —
     positive color for "+" deltas, negative color for "-" deltas)
  5. Native PowerPoint bar chart on the right using chart.series,
     chartColors=[muted, accent], legend at bottom
  6. Optional data table along the bottom with a deep-navy header row
  7. Small muted page number bottom-right

Requirements:
  1. Put colors / fonts in a single `theme` object so they're easy to swap.
  2. Add a short comment above each major step (kicker, title, KPIs, chart,
     table, page number) so I can find and edit each block quickly.
  3. Pure PptxGenJS — no external CSS, no HTML — works from any page.
  4. End with pptx.writeFile to trigger the browser download.

Optional extras:
  [ ] addShape() accent bar under the title
  [ ] second slide that's notes-only
  [ ] return the Blob instead of downloading (for upload / email flows)