ヘッドレスCMSのHTMLに含まれるimgタグをnext/imageで最適化する

2023/12/01に公開

初めに

この度、AmbitionsというWebメディアの開発にインターンとして携わりました。
https://ambitions-web.com/
Next.js+ ヘッドレスCMS(microCMS)の構成で開発しました。

ヘッドレスCMSの記事データをレンダリングするには、APIから帰ってくる記事のHTMLをそのままdivタグにdangerouslySetInnerHTMLで渡していました。

この場合、HTMLのimgタグもそのままレンダリングされるので、ヘッドレスCMSにアップロードした画像ファイルがそのまま表示されてしまい、ファイルサイズが大きい画像の場合は読み込みに時間がかかってしまいます。
読み込みのパフォーマンスを向上させるためには、画像のファイルサイズを抑えたりする必要があります。Next.js では next/image を用いることで画像が最適化され、ファイルサイズを大幅に減少させることができます。
ヘッドレスCMSの記事データに含まれる画像にnext/image を適用するにはHTMLの中のimgタグをnext/imageの<Image />コンポーネントにリプレイスする必要があります。
html-react-parserを使うと、リプレイスを簡単に行うことができました。

html-react-parserとは

html-react-parser はReactでHTMLをReactElementにパースするためのライブラリです。replace 関数を使うことで、以下のようにHTMLに含まれる特定の要素をReactのコンポーネントに変換できます。

この場合、<p id="replace">text</p>が、<Text>replaced</Text> に置き換えられます。

parse('<div><p id="replace">text</p></div>', {
  replace: domNode => {
   if (domNode.attribs && domNode.attribs.id === 'replace') {
   return <Text>replaced</Text>;
  }
 }
});

imgタグの最適化手順

インストール

html-react-parserをインストールします。

npm i html-react-perser

HTMLをパースしてReact Elementに変換する

const RichEditorField: FunctionComponent<RichEditorFieldProps> = ({
  content,
}) => {
 if (!content) return null;
 return <div>{parse(content, { replace })}</div>;
};

optionのreplace関数

上部で replace 関数の説明は既に行なっているので、「replace 関数を用いてimgタグを<Image />コンポーネントへ置き換えます。

import { StyleInnerHTML } from "@ambitions-web/ui";
import { FunctionComponent } from "react";
import Image from "next/image";
import parse, { Element, HTMLReactParserOptions } from "html-react-parser";

export type RichEditorFieldProps = {
  content: string | null;
};

type Replace = NonNullable<HTMLReactParserOptions["replace"]>;

export const RichEditorField: FunctionComponent<RichEditorFieldProps> = ({
  content,
}) => {
  return (
  if (!content) return null;

  return <div>{parse(content, { replace })}</div>;
};

const replace: Replace = (domNode) => {
  if (!(domNode instanceof Element)) return;

  if (domNode.name === "img") {
    const { attribs } = domNode;

    const { width, height } =; // 画像を縮小させたいサイズまでリサイズする(今回は処理内容は省略します。)

    return (
      <Image
        src={attribs.src}
        width={width}
        height={height}
        alt={attribs.alt ? attribs.alt : "Image"}
      />
    );
  }
};

}

これで、記事投稿に挿入されたすべての画像で Image コンポーネントが使用されます。

実際にDeveloper Toolsで確認してみると、CMSにアップロードされた解像度の高い3.3MBのjpeg画像が240KBのwebpとして描画され、ファイルサイズを93%削減することができました!

まとめ

この記事ではヘッドレスCMSのコンテンツをパースする選択肢を紹介させていただきました。

ヘッドレスCMSをNext.jsで使用している方の参考になれば幸いです。

Discussion