オフィス AI > パワポ AI > Next.js + pptxgenjsでマークダウンからPowerPointへの変換ツールを作成する完全ガイド

Next.js + pptxgenjsでマークダウンからPowerPointへの変換ツールを作成する完全ガイド

レベル:

2025年7月15日

8分で読めます

pptxgenjsライブラリを使用してNext.jsでマークダウンテキストをPowerPointプレゼンテーションに自動変換するWebツールの実装方法を詳しく解説します。ファイルアップロード、動的インポート、エラーハンドリングまで実践的なコード例付きで紹介。

PowerPoint
マークダウン
pptxgenjs
Next.js
ファイル変換
自動化
プレゼンテーション作成

マークダウンからPowerPointへの変換ツールが必要な理由

プレゼンテーション資料の作成は多くのビジネスパーソンにとって日常業務の一部ですが、PowerPointでのレイアウト調整や書式設定に時間を取られることが多々あります。一方で、マークダウンは軽量で書きやすく、多くの開発者や技術者に愛用されています。

この記事では、マークダウンテキストを自動でPowerPointプレゼンテーションに変換するWebツールをNext.js + pptxgenjsで作成する方法を詳しく解説します。

完成イメージ

以下のような機能を持つツールを作成します:

  • マークダウンテキストの直接入力
  • .mdファイルのアップロード機能
  • ワンクリックでPowerPoint形式(.pptx)にエクスポート
  • 見出しレベルに応じた自動スライド生成
  • リストや段落の適切な変換

必要な準備と環境構築

パッケージのインストール

まず、必要なライブラリをインストールします:

npm install pptxgenjs marked @types/marked

各パッケージの役割:

  • pptxgenjs: PowerPointファイル(.pptx)を生成するライブラリ
  • marked: マークダウンをパースするライブラリ
  • @types/marked: markedのTypeScript型定義

プロジェクト構造

今回は以下の構造でファイルを作成します:

src/app/office-ai/tools/markdown-to-pptx/
├── page.tsx                    # メタデータとページコンポーネント
└── MarkdownToPptxClient.tsx    # メインのクライアントコンポーネント

ページコンポーネントの実装

メタデータの設定

まず、SEO対策とページ情報を設定するためのpage.tsxを作成します:

import type { Metadata } from "next";
import MarkdownToPptxClient from "./MarkdownToPptxClient";

export const metadata: Metadata = {
  title: "マークダウン → PowerPoint 変換ツール|オフィス AI",
  description: "マークダウンファイルを美しいPowerPointプレゼンテーションに自動変換。テキスト入力またはファイルアップロードで手軽に使えるWebツール。",
  keywords: [
    "マークダウン PowerPoint 変換",
    "Markdown PPTX", 
    "プレゼンテーション 自動生成",
    "pptxgenjs",
  ],
};

export default function MarkdownToPptxPage() {
  return <MarkdownToPptxClient />;
}

メインコンポーネントの実装

基本的なState管理

クライアントコンポーネントでは、以下のstateを管理します:

"use client";

import { useState } from "react";

export default function MarkdownToPptxClient() {
  const [markdownText, setMarkdownText] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState<string | null>(null);

  // 実装続く...
}

ファイルアップロード機能

マークダウンファイル(.md)をアップロードして内容を読み取る機能を実装します:

const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
  const file = event.target.files?.[0];
  if (file) {
    if (file.type === "text/markdown" || file.name.endsWith(".md")) {
      const reader = new FileReader();
      reader.onload = (e) => {
        const content = e.target?.result as string;
        setMarkdownText(content);
        setSuccess("ファイルを読み込みました");
        setError(null);
      };
      reader.readAsText(file);
    } else {
      setError("マークダウンファイル(.md)を選択してください");
      setSuccess(null);
    }
  }
};

PowerPoint変換の核心ロジック

最も重要な変換処理を実装します。動的インポートを使用してクライアントサイドでのみライブラリを読み込みます:

const handleConvert = async () => {
  if (!markdownText.trim()) {
    setError("マークダウンテキストを入力してください");
    return;
  }

  setIsLoading(true);
  setError(null);
  setSuccess(null);

  try {
    // 動的インポート(クライアントサイドでのみ実行)
    const PptxGenJS = (await import("pptxgenjs")).default;
    const { marked } = await import("marked");

    // PowerPointプレゼンテーションを作成
    const pptx = new PptxGenJS();

    // マークダウンをパース
    const tokens = marked.lexer(markdownText);
    
    let currentSlide = pptx.addSlide();
    let slideContent: Array<{ text: string; options: any }> = [];
    let hasTitle = false;

    for (const token of tokens) {
      if (token.type === "heading") {
        // 見出しレベル1は新しいスライドのタイトル
        if (token.depth === 1) {
          if (hasTitle) {
            // 前のスライドにコンテンツを追加
            if (slideContent.length > 0) {
              currentSlide.addText(slideContent, {
                x: 0.5, y: 1.5, w: 9, h: 5,
                fontSize: 18, color: "363636"
              });
            }
            // 新しいスライドを作成
            currentSlide = pptx.addSlide();
            slideContent = [];
          }
          
          // タイトルを追加
          currentSlide.addText(token.text, {
            x: 0.5, y: 0.3, w: 9, h: 1,
            fontSize: 32, bold: true, color: "1976d2"
          });
          hasTitle = true;
        } else {
          // その他の見出しはコンテンツとして追加
          slideContent.push({
            text: token.text,
            options: { 
              fontSize: token.depth === 2 ? 24 : 20,
              bold: true, color: "1976d2", bullet: false
            }
          });
        }
      } else if (token.type === "paragraph") {
        slideContent.push({
          text: token.text,
          options: { 
            fontSize: 18, color: "363636", bullet: false
          }
        });
      } else if (token.type === "list") {
        token.items.forEach((item: any) => {
          slideContent.push({
            text: item.text,
            options: { 
              fontSize: 18, color: "363636", bullet: true
            }
          });
        });
      }
    }

    // 最後のスライドのコンテンツを追加
    if (slideContent.length > 0) {
      currentSlide.addText(slideContent, {
        x: 0.5, y: hasTitle ? 1.5 : 0.5,
        w: 9, h: hasTitle ? 5 : 6.5,
        fontSize: 18, color: "363636"
      });
    }

    // ファイルを保存
    const fileName = `markdown-presentation-${new Date().toISOString().slice(0, 10)}.pptx`;
    await pptx.writeFile({ fileName });

    setSuccess("PowerPointファイルが作成されました!");
  } catch (err: any) {
    console.error("変換エラー:", err);
    setError("変換中にエラーが発生しました: " + (err.message || "不明なエラー"));
  } finally {
    setIsLoading(false);
  }
};

UI/UXの実装ポイント

Material-UIを使ったモダンなデザイン

ユーザーフレンドリーなインターフェースを作成するため、Material-UIコンポーネントを活用します:

// ファイルアップロードボタン
<input
  accept=".md,.markdown"
  style={{ display: "none" }}
  id="markdown-file-upload"
  type="file"
  onChange={handleFileUpload}
/>
<label htmlFor="markdown-file-upload">
  <Button
    variant="outlined"
    component="span"
    startIcon={<Upload />}
    sx={{
      borderColor: "#1976d2",
      color: "#1976d2",
      "&:hover": {
        borderColor: "#1565c0",
        backgroundColor: "#e3f2fd",
      },
    }}
  >
    .mdファイルをアップロード
  </Button>
</label>

// テキスト入力エリア
<TextField
  fullWidth
  multiline
  rows={12}
  value={markdownText}
  onChange={(e) => setMarkdownText(e.target.value)}
  placeholder="マークダウンテキストを貼り付けてください..."
  variant="outlined"
  sx={{
    "& .MuiOutlinedInput-root": {
      fontFamily: "monospace",
      fontSize: "14px",
    },
  }}
/>

エラーハンドリングとユーザーフィードバック

適切なフィードバックを提供するため、Alertコンポーネントでエラーと成功メッセージを表示します:

{error && (
  <Alert severity="error" sx={{ mb: 3 }}>
    {error}
  </Alert>
)}
{success && (
  <Alert severity="success" sx={{ mb: 3 }}>
    {success}
  </Alert>
)}

変換ルールの設計思想

見出しレベルによるスライド分割

マークダウンの見出しレベルに基づいて、以下のルールでスライドを生成します:

  • # 見出し1: 新しいスライドのタイトルとして配置
  • ## 見出し2以下: 同一スライド内のサブタイトルとして配置
  • 段落: 通常のテキストコンテンツとして配置
  • リスト: 箇条書きとして配置

レイアウト設計

PowerPointスライドの座標系を理解した適切なレイアウトを設計:

// タイトルの配置
currentSlide.addText(token.text, {
  x: 0.5,    // 左からの位置(インチ)
  y: 0.3,    // 上からの位置(インチ)
  w: 9,      // 幅(インチ)
  h: 1,      // 高さ(インチ)
  fontSize: 32,
  bold: true,
  color: "1976d2"
});

応用カスタマイズのアイデア

テーマのカスタマイズ

企業ブランドに合わせたテーマを設定できます:

// 企業カラーの設定
const brandColors = {
  primary: "#1976d2",
  secondary: "#666666",
  accent: "#ff9800"
};

// テンプレートスライドの追加
pptx.defineSlideMaster({
  title: "MASTER_SLIDE",
  background: { color: "F1F1F1" },
  objects: [
    {
      placeholder: {
        options: { name: "title", type: "title", x: 0.6, y: 0.2, w: 8.5, h: 1.5 },
        text: "[(企業名)]"
      }
    }
  ]
});

コードブロックの対応

技術資料でよく使われるコードブロックにも対応可能:

else if (token.type === "code") {
  slideContent.push({
    text: token.text,
    options: { 
      fontSize: 14,
      fontFace: "Courier New",
      color: "333333",
      fill: "F5F5F5",
      bullet: false
    }
  });
}

トラブルシューティング

よくある問題と解決法

1. 動的インポートが失敗する場合

// 型エラーを回避する方法
const PptxGenJS = (await import("pptxgenjs")) as any;
const pptx = new PptxGenJS.default();

2. ファイルダウンロードが機能しない場合
ブラウザのセキュリティ設定を確認し、必要に応じてhttpsでアクセスしてください。

3. 日本語フォントが正しく表示されない場合

currentSlide.addText(text, {
  fontFace: "メイリオ", // 日本語対応フォントを指定
  // その他のオプション...
});

まとめ

この記事では、Next.js + pptxgenjsを使用してマークダウンからPowerPointへの変換ツールを作成する方法を詳しく解説しました。

主なポイント

  1. 動的インポートでクライアントサイドでのみライブラリを読み込み
  2. marked.jsでマークダウンを構造化データに変換
  3. pptxgenjsでPowerPointファイルを生成
  4. Material-UIでユーザーフレンドリーなUIを構築
  5. 適切なエラーハンドリングでユーザビリティを向上

活用シーン

  • 技術文書のプレゼンテーション化
  • ブログ記事の発表資料作成
  • マークダウンで書かれた仕様書の資料化
  • 定期レポートの自動生成

このツールを基盤として、さらなるカスタマイズや機能追加を行い、業務効率化を実現してください。プレゼンテーション作成の時間を大幅に短縮し、コンテンツの質向上に集中できるようになるでしょう。

この記事が役に立ちましたか?

パワポ AI×AIの導入や業務効率化についてのご相談は、お気軽にビットライトまでお問い合わせください。

関連記事
    著者
    守高 成悟
    守高 成悟

    代表取締役 CEO

    大阪大学基礎工学部情報科学科卒業。大学時代はAIについて研究。10年以上のWebアプリ開発・データ分析経験を持つ。株式会社ビットライト代表として、生成AI・RPA・Excelなどを活用した業務効率化の専門コンサルティングを提供。

    関連記事
    著者
    守高 成悟
    守高 成悟

    代表取締役 CEO

    大阪大学基礎工学部情報科学科卒業。大学時代はAIについて研究。10年以上のWebアプリ開発・データ分析経験を持つ。株式会社ビットライト代表として、生成AI・RPA・Excelなどを活用した業務効率化の専門コンサルティングを提供。