GatsbyJSでCSS Modulesのstyleタグを外部CSS化する方法

gatsby

GatsbyJSはReactを利用するのでCSS Modulesはスタイルを反映するための1つの手段です。

難しい設定は必要なくすぐに利用できるようになっているのでとても便利ですが気になることがあります。
それはCSS Modulesで指定したスタイルがインラインでHTMLに出力されてしまうということです。

CSSは外部ファイル化されるようにしたい、ということでGatsbyを使って生成されるHTMLに手を加えて実現できる方法を紹介します。

GatsbyJSでCSS Modulesを使う

まずGatsbyJSでCSS Modulesを使うには任意のスタイルシートのファイル名にmodule.cssをつけてimportすることで利用できます。 たとえば下記のようなuser.module.cssを用意します。

.user {
background: #ccc;
}

○○.module.cssのようなファイル名にしないとJSから読み込んでもスタイルが反映されないので注意が必要です。 スタイルシートを用意するとあとはJSから読み込みます。

import styles from './user.module.css'
const User = () => {
return <div className={styles.user}>User</div>
}

これでclassNameにはuser.module.cssで指定したクラスが付与され、CSSが出力されるようになります。

HTMLに出力されるCSSを外部読み込みにする

先に少し触れたように基本的な使い方をするとCSS ModulesのスタイルはHTMLに吐き出されてしまいます。 イメージ的には下記のようなコードです。

<html>
<head>
<style data-href="CSSのパス">
.CSS Modulesで動的に生成されるクラス名 { background: #ccc; }
</style>
</head>
<body>
<div class="CSS Modulesで動的に生成されるクラス名">User</div>
</body>
</html>

CSSが増えれば増えるほどstyleタグで出力されるコードが増えていきます。 動作には問題ないですがと時としてHTMLにCSSのコードを出力しないようにしたい場合には手を加えます。

GatsbyでレンダリングされるHTMLを変更する

HTMLに出力されるCSSを外部ファイル化するにはgatsby-ssr.jsに下記のコードを追加します。

exports.onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
if (process.env.NODE_ENV !== "production") return;
let hc = getHeadComponents();
hc.forEach(el => {
if (el.type === "style") {
el.type = "link";
el.props["href"] = el.props["data-href"];
el.props["rel"] = "stylesheet";
el.props["type"] = "text/css";
delete el.props["data-href"];
delete el.props["dangerouslySetInnerHTML"];
}
});
};

data-href属性がついたCSSはhrefで読み込むようになります。 このコードはGatsbyのissueで提示されています。

https://github.com/gatsbyjs/gatsby/issues/1526#issuecomment-433347822

Gatsbyではビルドして静的なHTMLを作るときに利用されることが多いことを考えると生成されるHTMLにはこだわりたくなります。

今回紹介した方法でHTMLにべた書きされていたスタイルを外部ファイル化することができるので、HTMLをとてもすっきりした状態にすることができます。