【React】スタイリング方法まとめ
Reactでhtmlをスタイリングする方法をまとめました。
Reactでhtmlをスタイリングする方法
大きく以下の4つの方法があります。
- CSSファイルを読み込む
- インラインスタイル
- CSS Modules
- CSS in JS
それぞれの違いと特徴をみてみましょう。
CSSファイルを外部ファイルとして読み込む
外部ファイルとしてcssを読み込む方法は、とてもよく使う方法の一つです。
基本的には、jsファイル、つまりコンポーネントと同じ名前にしてcssファイルを作成します。
Sample.jsなら、Sample.cssとします。
Sample.css
.btn {
width: 200px;
height: 60px;
display: block;
font-size: 18px;
line-height: 1.75;
letter-spacing: 0.05em;
cursor: pointer;
color: black;
border: none;
}
.btn.isSelected {
background-color: black;
color: white;
border: 2px solid orange;
}
このような内容を記述したとします。
普通のcssです。
Sample.js
import { useState } from "react"
import './Sample.css';
const Sample = () => {
const [isSelected, setIsSelected] = useState(false);
const clickHandler = () => setIsSelected(prev => !prev);
return (
<>
<button className={isSelected ? "btn isSelected" : "btn"} onClick={clickHandler}>Button</button>
<button className={`btn ${isSelected ? "isSelected" : ""}`} onClick={clickHandler}>Button2</button>
</>
)
};
export default Sample;
外部ファイルの場合、class属性は、ReactではclassNameとします。
テンプレートリテラル“を使う場合は、その中身は通常のjsを記述できるので、テンプレートリテラルの記法にのっとって${}で記述します。
コンポーネントごとに適用されないので、注意が必要です。
コンポーネントを意識した設計が必要になります。
画面全体に適用したいcssや共通のcssの場合には最適ですが、コンポーネントごとにcssを当てたいときは、CSS ModulesやCSS in JSを使います。
CSSファイルを外部ファイルとして読み込むメリット・デメリット
グローバルスコープになるので、CSSの詳細度は要注意です。
ルートファイルなど、共通のスタイルを適用させたい場所には最適です。
インラインスタイル
import { useState } from "react"
const Sample = () => {
const [isSelected, setIsSelected] = useState(false);
const clickHandler = () => setIsSelected(prev => !prev);
const style = {
width: 200,
height: 60,
display: "block",
fontSize: 18,
lineHeight: 1.75,
"letter-spacing": "0.05em",
cursor: "pointer",
backgroundColor: isSelected ? "black" : "",
color: isSelected ? "white" : "black",
border: isSelected ? "2px solid orange": "none"
}
return (
<>
<button style={style} onClick={clickHandler}>Button</button>
<button style={{backgroundColor: "red", color: "white"}}>Button2</button>
</>
)
};
export default Sample;
インラインスタイルでスタイルをあてるには、上記の2つの方法があります。
基本的にはJavaScriptの記法に沿う必要があるため、ケバブケース(background-color)といったcssの書き方ではなく、キャメルケース(backgroundColor)という書き方をします。
ケバブケースで書く場合は、""で囲むことで可能になります。
三項演算子で""を指定した場合、そのプロパティは設定していないことになります。
ただの数値を入れた場合pxになるので、pxは省略することができます。
どちらもインラインスタイル特有の問題がでてきます。
同じスタイルを当てたい場合、同じものを属性で指定しないといけません。
また、beforeのような疑似要素やactiveのような疑似セレクターはこの方法では記述できません。
あとは、@mediaなどのメディアクエリも使えません。
よって現実的にはメインでは使えません。
また、読み込むたびにスタイルが計算されるため、パフォーマンスもよくありません。
よって、基本的には、本番環境で使うことはほとんどありませんが、ちょっと当てて確認したいといった
テスト環境ではたまに使う方法となります。
インラインスタイルのメリット・デメリット
再利用性が低く、疑似要素やメディアクエリは使用できず、
レンダリングのたびに計算されてパフォーマンスも低下されるため、基本的にあまり使わないです。
ただし、動的に頻繁に計算が必要なスタイルのときや、テスト環境でちょっと試したいといったケースで使えます。
CSS Modules(近い将来廃止されるかも?)
Sample.module.css
.btn {
width: 200px;
height: 60px;
display: block;
font-size: 18px;
line-height: 1.75;
letter-spacing: 0.05em;
cursor: pointer;
color: black;
border: none;
}
.btn:hover {
background-color: blue;
}
.isSelected {
background-color: black;
color: white;
border: 2px solid orange;
}
まず、CSS Modulesを使う場合、cssファイルは、module.cssとします。
あとは、メディアクエリも擬似要素なども普通のCSSと同じように記述可能です。
htmlタグを指定したりすると、共通のcssとしてスタイルが適用されてしまう点に注意が必要です。
divタグなどhtml要素に指定せず、cssクラスを使います。
Sample.js
import { useState } from "react"
import styles from './Sample.module.css';
const Sample = () => {
const [isSelected, setIsSelected] = useState(false);
console.log(styles);
const clickHandler = () => setIsSelected(prev => !prev);
return (
<>
<button className={isSelected ? styles.btn+" "+ styles.isSelected : styles.btn} onClick={clickHandler}>Button</button>
<button className={`${styles.btn} ${isSelected ? styles.isSelected : ""}`} onClick={clickHandler}>Button2</button>
</>
)
};
export default Sample;
基本的に問題はないのですが、将来的に廃止される可能性の高い記法になります。
よって、今から使う場合は極力CSS Modulesの使用はされて、下のCSS in JSなどを使うのがいいです。
CSS Modulesのメリット・デメリット
外部ファイルの読み込みと違い、クラス名の衝突は起きないのでCSSの詳細度などは気にせず記述できます。
CSSとJSが分かれる必要があります。これは好みの問題となります。
大きなデメリットとしては、React開発側が廃止したい旨をつぶやいているので、
将来的に廃止、もしくは非推奨になる可能性が高い点です。
CSS in JS(styled-components)
CSS in JSは、基本的には、ライブラリを使用します。
有名なライブラリとして、styled-componentsや、emotionsがあります。
ライブラリ:styled-componentsをインストール
npm i styled-components
上のようにしてインストールします。
VS Codeを使っている場合は、拡張機能(styled-components.vscode-styled-components)を入れておくとハイライトされて便利です。
Sample.js
import { useState } from "react";
// import styles from './Sample.module.css';
import styled from "styled-components";
// const StyledBtn = styled.button``;// タグ付きテンプレート、関数の実行と同じ、最初は大文字で書く
const StyledBtn = styled.button`
/* この中はcssと全く同じ記法 */
width: 200px;
height: 60px;
display: block;
font-size: 18px;
line-height: 1.75;
letter-spacing: 0.05em;
cursor: pointer;
color: black;
border: none;
/* propsを渡すときは関数にする */
background-color: ${(props) => (props.isSelected ? "black" : "white")};
/* 分割代入でも記述可能 */
color: ${({ isSelected }) => (isSelected ? "white" : "black")};
`;
const Sample = () => {
const [isSelected, setIsSelected] = useState(false);
const clickHandler = () => setIsSelected((prev) => !prev);
return (
<>
<StyledBtn isSelected={isSelected} onClick={clickHandler}>
Button
</StyledBtn>
</>
);
};
export default Sample;
上のように記述します。
StyledBtnというコンポーネントとして使用することができます。
ポイントとしては、
const StyledBtn = styled.button``;// タグ付きテンプレート、関数の実行と同じ、最初は大文字で書く
タグ付きテンプレートという記法を使います。
テンプレートリテラル“部分を実行するという関数になります。
このテンプレートリテラル部分に普通のCSSを記述していきます。
テンプレートリテラルなので、基本的には文字列になります。JSを使いたい部分だけ、テンプレートリテラルの記法にのっとり、${}で記述します。
コンポーネントと同じため、propsを渡すことが可能です。
/* propsを渡すときは関数にする */
background-color: ${(props) => (props.isSelected ? "black" : "white")};
/* 分割代入でも記述可能 */
color: ${({ isSelected }) => (isSelected ? "white" : "black")};
継承を使って同じようなスタイルのボタンを作成する
import { useState } from "react";
// import styles from './Sample.module.css';
import styled from "styled-components";
// const StyledBtn = styled.button``;// タグ付きテンプレート、関数の実行と同じ、最初は大文字で書く
const StyledBtn = styled.button`
/* この中はcssと全く同じ記法 */
width: 200px;
height: 60px;
display: block;
font-size: 18px;
line-height: 1.75;
letter-spacing: 0.05em;
cursor: pointer;
color: red;
border: none;
/* propsを渡すときは関数にする */
background-color: ${(props) => (props.isSelected ? "black" : "white")};
/* 分割代入でも記述可能 */
color: ${({ isSelected }) => (isSelected ? "white" : "black")};
`;
const RedBtn = styled(StyledBtn)`
background-color: ${(props) => (props.isSelected ? "gray" : "red")};
:hover {
opacity: 0.8;
}
span {
color: blue;
font-weight: bold;
}
`;
const Sample = () => {
const [isSelected, setIsSelected] = useState(false);
const clickHandler = () => setIsSelected((prev) => !prev);
return (
<>
<StyledBtn isSelected={isSelected} onClick={clickHandler}>
Button
</StyledBtn>
<RedBtn isSelected={isSelected} onClick={clickHandler}>
Button<span>スパン</span>
</RedBtn>
</>
);
};
export default Sample;
上のように、styled-componentを継承することが可能です。
const RedBtn = styled(StyledBtn)``;
具体的には上のように、継承したいstyled-componentをstyledの引数にわたします。
あとは、テンプレートリテラル部分に、変更したい内容を記述します。
ネストが可能
上のように、ネストして書くことが可能です。
そのため、基本的にコンポーネントごとに記述することができます。
あとは、メディアクエリも擬似要素も疑似セレクターもCSSはすべて記述可能です。
ランダムな文字列が付与されるようになるため、CSSの詳細度はもはや気にしなくてよくなります。
CSS in JSのメリット・デメリット
クラス名の衝突は起きないので詳細度は気にしなくていいです。
CSSとJSが一つにまとまります。これは好みの問題です。
propsを参照して動的にスタイルを当てることができます。
疑似要素やメディアクエリなどCSSの機能なすべて使用可能です。
ライブラリは入れる必要があります。
とりわけデメリットがあるわけではありませんが、記述の仕方に慣れが必要となります。
現在主流の書き方となります。