Skip to content
Back to skills

Pptx Official

ASecurity

Generating PowerPoint presentations with pptxgenjs in Node.js. Use when creating automated presentations, slide decks, pitch decks, or reports in .pptx format from data.

  • 30 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 11, 2026
developmenttypescriptbashnodeapi

Works with

  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 23, 2026

npx -y skills add sabahattink/antigravity-fullstack-hq --skill pptx-official --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pptx Official?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Pptx Official
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/sabahattink-pptx-official/badge)](https://www.skillsdirectory.com/skills/sabahattink-pptx-official)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: pptx-official
description: Generating PowerPoint presentations with pptxgenjs in Node.js. Use when creating automated presentations, slide decks, pitch decks, or reports in .pptx format from data.
---

# PowerPoint Generation with pptxgenjs

## Installation

```bash
npm install pptxgenjs
npm install -D @types/node
```

## Basic Presentation

```typescript
import PptxGenJS from 'pptxgenjs'

async function generatePresentation(data: PresentationData): Promise<Buffer> {
  const pptx = new PptxGenJS()

  // Global settings
  pptx.author  = 'Full Stack HQ'
  pptx.company = 'Full Stack HQ'
  pptx.title   = data.title
  pptx.subject = data.subject
  pptx.layout  = 'LAYOUT_WIDE'  // 16:9

  // ─── Title Slide ─────────────────────────────────────────
  const titleSlide = pptx.addSlide()
  titleSlide.background = { color: '1E3A5F' }

  titleSlide.addText(data.title, {
    x:     '10%',
    y:     '35%',
    w:     '80%',
    h:     '20%',
    fontSize:  40,
    bold:      true,
    color:     'FFFFFF',
    align:     'center',
    fontFace:  'Calibri',
  })

  titleSlide.addText(data.subtitle, {
    x:        '15%',
    y:        '60%',
    w:        '70%',
    h:        '10%',
    fontSize: 20,
    color:    'B0C4DE',
    align:    'center',
  })

  titleSlide.addText(new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long' }), {
    x:        '0%',
    y:        '88%',
    w:        '100%',
    h:        '8%',
    fontSize: 12,
    color:    '7094B5',
    align:    'center',
  })

  // ─── Content Slides ───────────────────────────────────────
  for (const section of data.sections) {
    addContentSlide(pptx, section)
  }

  // ─── Chart Slide ──────────────────────────────────────────
  if (data.chartData) {
    addChartSlide(pptx, data.chartData)
  }

  // ─── Summary Slide ────────────────────────────────────────
  addSummarySlide(pptx, data.keyPoints)

  // Return as buffer
  const buffer = await pptx.write({ outputType: 'nodebuffer' }) as Buffer
  return buffer
}
```

## Content Slide with Bullets

```typescript
function addContentSlide(pptx: PptxGenJS, section: Section) {
  const slide = pptx.addSlide()

  // Slide header bar
  slide.addShape(pptx.ShapeType.rect, {
    x: 0, y: 0, w: '100%', h: 0.8,
    fill: { color: '2563EB' },
    line: { color: '2563EB' },
  })

  // Title
  slide.addText(section.title, {
    x:        0.3,
    y:        0.1,
    w:        '90%',
    h:        0.6,
    fontSize: 22,
    bold:     true,
    color:    'FFFFFF',
    fontFace: 'Calibri',
  })

  // Bullet points
  slide.addText(
    section.bullets.map(bullet => ({
      text:    bullet,
      options: {
        bullet: { type: 'bullet', characterCode: '25CF' }, // filled circle
        paraSpaceAfter: 8,
      },
    })),
    {
      x:        0.5,
      y:        1.1,
      w:        '90%',
      h:        '75%',
      fontSize: 16,
      color:    '374151',
      fontFace: 'Calibri',
      valign:   'top',
    }
  )

  // Slide number
  slide.addText(`${section.slideNumber}`, {
    x:        '93%',
    y:        '92%',
    w:        '5%',
    h:        '6%',
    fontSize: 10,
    color:    '9CA3AF',
    align:    'right',
  })
}
```

## Chart Slide

```typescript
function addChartSlide(pptx: PptxGenJS, chartData: ChartData) {
  const slide = pptx.addSlide()

  // Header
  slide.addShape(pptx.ShapeType.rect, {
    x: 0, y: 0, w: '100%', h: 0.8,
    fill: { color: '2563EB' },
    line: { color: '2563EB' },
  })
  slide.addText(chartData.title, {
    x: 0.3, y: 0.1, w: '90%', h: 0.6,
    fontSize: 22, bold: true, color: 'FFFFFF',
  })

  // Bar chart
  slide.addChart(pptx.ChartType.bar, [
    {
      name:   chartData.seriesName,
      labels: chartData.labels,
      values: chartData.values,
    },
  ], {
    x:     0.5,
    y:     1.0,
    w:     9.0,
    h:     5.0,
    chartColors:  ['2563EB', '10B981', 'F59E0B', 'EF4444'],
    showValue:    true,
    showLegend:   true,
    legendPos:    'b',
    dataLabelFontSize: 10,
    catAxisLabelFontSize: 11,
    valAxisLabelFontSize: 11,
    title:         chartData.title,
    showTitle:     false,
  })
}

// Line chart for trends
slide.addChart(pptx.ChartType.line, [
  { name: 'Revenue', labels: months, values: revenueData },
  { name: 'Costs',   labels: months, values: costData },
], {
  x: 0.5, y: 1.0, w: 9.0, h: 5.0,
  lineSize:    2,
  showLegend:  true,
  showMarker:  true,
  chartColors: ['2563EB', 'EF4444'],
})
```

## Table Slide

```typescript
function addTableSlide(pptx: PptxGenJS, tableData: TableData) {
  const slide = pptx.addSlide()

  const rows: PptxGenJS.TableRow[] = [
    // Header row
    tableData.headers.map(h => ({
      text: h,
      options: {
        bold:     true,
        color:    'FFFFFF',
        fill:     { color: '2563EB' },
        fontSize: 12,
        align:    'center' as const,
      },
    })),
    // Data rows
    ...tableData.rows.map((row, i) =>
      row.map(cell => ({
        text: cell,
        options: {
          fontSize: 11,
          color:    '374151',
          fill:     i % 2 === 0 ? { color: 'F8FAFC' } : { color: 'FFFFFF' },
        },
      }))
    ),
  ]

  slide.addTable(rows, {
    x:     0.3,
    y:     1.0,
    w:     9.4,
    border: { type: 'solid', color: 'E5E7EB', pt: 0.5 },
    colW:  tableData.columnWidths,
  })
}
```

## NestJS Integration

```typescript
// presentations/presentations.service.ts
@Injectable()
export class PresentationsService {
  async generateReport(reportId: string): Promise<Buffer> {
    const report = await this.reportsRepo.findOneOrFail(reportId)

    return generatePresentation({
      title:    report.title,
      subtitle: report.period,
      subject:  'Monthly Report',
      sections: report.sections.map(s => ({
        title:  s.heading,
        bullets: s.points,
        slideNumber: s.order,
      })),
      chartData: {
        title:      'Revenue Trend',
        seriesName: 'Revenue',
        labels:     report.chart.months,
        values:     report.chart.values,
      },
      keyPoints: report.summary,
    })
  }
}

// presentations/presentations.controller.ts
@Get(':id/pptx')
@UseGuards(JwtAuthGuard)
async downloadPptx(
  @Param('id') id: string,
  @Res() res: Response,
) {
  const buffer = await this.presentationsService.generateReport(id)

  res.set({
    'Content-Type':        'application/vnd.openxmlformats-officedocument.presentationml.presentation',
    'Content-Disposition': `attachment; filename="report-${id}.pptx"`,
    'Content-Length':      buffer.length,
  })

  res.end(buffer)
}
```

## Forbidden Patterns

- Never use absolute pixel values for positioning — use percentages or inch values for portability
- Never forget to `await pptx.write()` — it returns a Promise
- Never include user-provided text without escaping HTML entities in text fields
- Never create slides in a loop without checking memory — large decks need streaming or chunking

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…