Next.jsでrender blocking CSSを削除してCore Web Vitalsを改善する
Core Web Vitalsの高速化のために、Next.jsでrender blocking CSSを削除してください。
Next.jsにおけるrender blocking CSSの排除
CSSはrender blockingなリソースです。ブラウザは<head>内のすべてのスタイルシートをダウンロードし解析するまで、1ピクセルも描画しません。Next.jsアプリでは、デフォルトで2つの外部CSSファイル(グローバルなスタイルシートとページ固有のスタイルシート)を意味します。高速な3G回線の場合、ブラウザがレンダリングを開始する前に、これら2つのファイルをダウンロードするだけで600msかかります。この600msは、そのままFirst Contentful PaintとLargest Contentful Paintに加算されます。
最終確認: Arjen Karel (2026年3月)
Table of Contents!
平均して、すべてのクライアントとデバイスにおいて、render blocking CSSによる遅延は約200msに上ります。2025 Web Almanacによると、モバイルページの85%が依然としてrender blockingリソースの監査に不合格となっています。中央値のページは8つのCSSファイル、合計79 KBを読み込んでいます。低速なモバイル回線では、レンダリング遅延が全体のLCPの69%を占めることもあります。今すぐ修正しましょう。

App RouterとPages Routerの比較
解決策は、使用しているNext.jsのルーターによって異なります。Pages Router(_document.tsxを使用)とApp Router(app/layout.tsxを使用)では、CSSの配信方法が異なります。crittersによるCritical CSSのインライン化は、App Routerでは機能しません。ストリーミングが、crittersのHTML処理方法と互換性がないためです。App Routerを使用している場合は、Option 4に進んでください。
Next.js 13.4以降、App Routerがデフォルトです。しかし、多くの本番アプリは依然としてPages Routerで稼働しており、以下に示す最初の3つの方法はこれらのアプリに対して有効です。
Option 1: Critical CSSの生成(Pages Router)
最も速い方法は、Critical CSSを生成することです。Critical CSSは、ページの可視領域をレンダリングするために必要なCSSルールの集合です。これらのルールは<head>内にインラインで配置されます。並行して元のCSSファイルがダウンロードされ、ブラウザはレンダリングを続行します。元のCSSファイルのダウンロードが完了すると、ページに注入されます。
Critical CSSは、Next.jsでは実験的な機能として利用可能です。next.config.jsにexperimental: { optimizeCss: true }を追加します。Next.jsは内部でcrittersライブラリをバンドルしているため、個別にインストールする必要はありません。なお、元のcrittersパッケージは非推奨(beastiesフォークに置き換え)ですが、Next.jsはまだ切り替えを行っていません。
const nextConfig = {
reactStrictMode: true,
experimental: { optimizeCss: true }
}
この方法はPages Routerでのみ機能します。crittersは完全にレンダリングされたHTMLを必要とし、ストリーミングと互換性がないため、App Routerはサポートしていません。
Option 2: すべてのCSSをheadにインライン化する(Pages Router)
Next.jsアプリで実験的な機能を有効にしたくない場合は、手動でCSSをインライン化することを検討してください。カスタムheadセクションを作成し、_document.tsxで参照します。
デメリットは、クリティカルなルールだけでなくすべてをインライン化するため、最初の方法よりもインラインCSSが大きくなることです。しかし、スタイルシートをクリーンで軽量に保てば、Next.jsアプリのCore Web Vitalsは大幅に向上します。
import { Html, Head, Main, NextScript } from "next/document";
import { readFileSync } from "fs";
import { join } from "path";
class InlineStylesHead extends Head {
getCssLinks: Head["getCssLinks"] = ({ allFiles }) => {
const { assetPrefix } = this.context;
if (!allFiles || allFiles.length === 0) return null;
return allFiles
.filter((file: any) => /\.css$/.test(file))
.map((file: any) => (
<style
key={file}
nonce={this.props.nonce}
data-href={`${assetPrefix}/_next/${file}`}
dangerouslySetInnerHTML={{
__html: readFileSync(join(process.cwd(), ".next", file), "utf-8"),
}}
/>
));
};
}
export default function Document() {
return (
<Html>
<InlineStylesHead />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
Option 3: Styled Components(Pages Router)
Styled Componentsは、JavaScriptファイル内にCSSを記述できるCSS-in-JSツールです。クラス名をスコープし、未使用のCSSを自動的に削除し、コンポーネントと一緒にスタイルを管理できます。Core Web Vitalsの観点では、そのページで必要なスタイルのみが注入されることを意味します。
next.config.jsにcompiler: { styledComponents: true }を追加し、styled-componentsをインストール(yarn add styled-componentsおよびyarn add -D @types/styled-components)します。その後、_document.jsを更新して、styled componentsのサーバーサイドレンダリングをサポートします。
const nextConfig = {
reactStrictMode: true,
compiler: {
styledComponents: true,
}
}
import Document, { DocumentContext } from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
}
App Routerでstyled-componentsを使用する場合、異なる設定が必要です。useServerInsertedHTMLを使用してスタイルレジストリのクライアントコンポーネントを作成し、ルートレイアウトをラップします。App Routerでは、styled componentsはクライアントコンポーネントでのみ機能します。完全な実装パターンについては、Next.jsのCSS-in-JSガイドを参照してください。
Option 4: inlineCssによるCSSのインライン化(App Router、Next.js 15以降)
App Router向けに、Next.js 15ではexperimental.inlineCssが導入されました。これにより、すべての<link>スタイルシートタグがインラインの<style>タグに置き換わり、render blockingなウォーターフォールが完全に排除されます。
const nextConfig = {
experimental: {
inlineCss: true,
}
}
これはまだ実験段階であり、Next.jsチームはまだ本番環境での使用を推奨していません。クリティカルCSSだけでなくすべてのCSSをインライン化するため、HTMLサイズが増加します。Tailwind CSSを使用しているサイトでは、Tailwindが小さくアトミックなCSSバンドルを生成するため、このトレードオフは有利に働きます。大きなスタイルシートを持つサイトでは、HTMLの肥大化がTime to First Byteを悪化させる可能性があります。
CSS ModulesとTailwind CSSについての注意事項
現在、Next.jsで最も人気のあるスタイリング手法は、CSS ModulesとTailwind CSSの2つです。どちらもビルド時に標準のCSSファイルにコンパイルされ、<link>タグとして配信されます。つまり、デフォルトでrender blockingになります。
Tailwindには1つ利点があります。未使用のクラスを削除するため、出力は通常非常に小さくなります(多くの場合、gzip圧縮で10 KB未満)。10 KBのファイルによるrender blockingの影響は最小限です。CSS Modulesは、未使用のスタイルに気を配らないとファイルが大きくなる可能性があります。
どちらの場合も、上記のすべての選択肢が適用されます。render blocking CSSを完全に排除したい場合は、好みのスタイリング手法とoptimizeCss(Pages Router)またはinlineCss(App Router)を組み合わせてください。
どの方法を選ぶべきか?
使用しているルーターとCSSの戦略に依存します。
- App Router + Tailwind:
inlineCssを使用します。CSSバンドルが小さいため、インライン化が実用的です。 - App Router + 大きなCSS:
inlineCssが安定するのを待つか、カスタムのbeasties設定を使用します。 - Pages Router:
optimizeCss(Option 1)を使用します。最も簡単な解決策であり、クリティカルなものだけをインライン化します。 - Pages Router + CSS-in-JS: 上記で示したSSRパターンでstyled-componentsまたはEmotionを使用します。
CoreDashで監視しているNext.jsサイト全体で、インライン化されたクリティカルCSSを使用しているサイトは、デフォルトのCSS配信と比較して中央値で400msのFCP改善を示しています。これは、優れたFCPスコアと平凡なスコアの差です。
どの方法を選ぶにしても、Real User Monitoringで結果を検証してください。Labスコアは起こり得る結果を示し、field dataは実際に起こった結果を示します。
CSSとJavaScriptがレンダリングパイプラインとどのように相互作用するかについての詳細は、render blockingリソースの修正に関する完全ガイドを参照してください。Next.jsアプリを最適化している場合は、Next.jsにおけるサードパーティスクリプトの修正とCore Web Vitalsの測定に関するガイドも確認してください。読み込み戦略をより広く理解するには、ブラウザが何を最初にフェッチするかを決定する仕組みを解説したリソースの優先順位付けガイドをご覧ください。