Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React Server Componentで生のHTMLを扱う技術

Avatar for nakataki nakataki
February 26, 2025
400

React Server Componentで生のHTMLを扱う技術

はてなCMSはノーコードエディタが出力するHTML・CSSをReact Server Componentsでレンダリングしています。また、ユーザーが自由にHTML・CSSを記述する機能も備えています。
Next.js App Router上において、ノーコードで作ったコンテンツをServer-Side Renderingしてパフォーマンスを確保することと、ユーザがHTMLや`script`タグを自由度高く埋め込めることを両立するために、どのような工夫を行ったのか解説します。

Avatar for nakataki

nakataki

February 26, 2025
Tweet

Transcript

  1. 余談: App Routerの強み Pages Routerを使うと… getServerSideProps ではJSONにSerializeできるものしか返せない JSX Element(Reactコンポーネント)は渡せない つまり、Componentの生成を

    Page() 以下でやるしかない そして Page() の処理は、クライアントにコードが露出してしまう セキュリティ、パフォーマンスの両面で懸念 17
  2. 余談: App Routerの強み 2 App Routerだと… React Server Compoentを生成できる Component生成のロジックは隠蔽される

    つまり… React Componentを生成してSSRしたかったら、App Routerを使おう! 18
  3. カスタム<head> App Routerでは、もはや<head>を触らせてくれない。 <Next/Head> なんてない 各種メタデータを generateMetadata() 関数でエクスポート export function

    generateMetadata(): Metadata { return { title: "Hello, World!", description: "Hatena Engineer Seminar #32", }; } ユーザが入力した文字列から Metadata オブジェクトを組み立てるのは 非現実的… 20
  4. カスタム<head> 2 React 19の新機能を使おう React がこのコンポーネントをレンダーする際、 <title> , <link> ,

    <meta> タグを認識し、自動的にドキュメントの <head> セクションに移動させます。 React v19 – React Reactから直接ドキュメントにメタデータを追加できるようになっている! 21
  5. カスタム<head> 3 Easy (簡略化しています) document.querySelectorAll("script, meta, link").forEach((elem, index) => {

    if (elem.tagName.toLowerCase() === "style") { elem.setAttribute("precedence", "high"); elem.setAttribute("href", `user_style_${index}`); } if (elem.tagName.toLowerCase() === "link") { elem.setAttribute("precedence", "high"); } headContent += elem.outerHTML; }); 22
  6. 自由HTML埋め込み: 直す const isServerSide = typeof window === "undefined"; export

    function SSROnlyBlock({ html }: { html: string }) { return ( <div suppressHydrationWarning dangerouslySetInnerHTML={{ __html: isServerSide ? html : "" }} /> ); } Next.jsは dangerouslySetInnerHTML で指定したScriptをHydrationの 対象にしない suppressHydrationWarning でエラーを回避 33