Material UI を使った TODO リストの UI 拡張チュートリアル
1. 概要
前回の記事で予告したとおり、今回は Material UI を使ってアプリの見た目と操作性を向上させていきます
適用前 | 適用後 |
---|---|
![]() |
![]() |
Material UI は、Google が提唱する Material Design ガイドラインに基づいて設計されたコンポーネントライブラリです
多彩な、デザイン済みコンポーネントが提供されており、それらを組み合わせるだけで統一感のある美しいインターフェースを簡単に作成できます
目標
- Material UI の導入と基本的な使い方を学ぶ
- TODO リストの UI を Material UI で拡張する
- Material UI の公式ドキュメントからコンポーネントの探し方を学ぶ
2. 前提条件
このチュートリアルを始める前に、以下の環境を準備してください
必要に応じて、Reactの基本チュートリアル(前回の記事)はこちらで環境構築の詳細を確認してください
このチュートリアルは、React の基本(コンポーネント、状態管理、ルーティング)を習得した方を対象としています
必要な前提条件
-
リポジトリ管理: GitHub 上のサンプルプロジェクト react-todo-tutorial を使用します
-
作業開始ブランチ:
lesson-materialui
- 各チュートリアルごとに新しいブランチを作成して管理します
-
作業開始ブランチ:
-
動作環境:
- Visual Studio Code
- Devcontainer もしくはローカル環境(Node.js 20 以上)
手順: Material UI のインストール
-
リポジトリのクローン
ターミナルで以下のコマンドを実行して、リポジトリをクローンしてくださいgit clone https://github.com/weworku/react-todo-tutorial.git cd react-todo-tutorial git checkout lesson-materialui
-
Devcontainer を使用する場合
- Visual Studio Code でプロジェクトフォルダを開きます
- 左下の「><」アイコンから Reopen in Container を選択して開発コンテナを起動します
- 開発コンテナが起動したら、ターミナルで以下のコマンドを実行して開発サーバーを開始します
npm install npm run dev -- --host 0.0.0.0
サーバーが起動すると、通常は http://localhost:5173 にアクセスできるようになります
ブラウザでこのURLを開き、アプリケーションが正しく表示されることを確認してください
備考
- 環境構築に不安がある場合は、前回の記事で Devcontainer を使用した詳細なセットアップ手順を確認してください
3. 環境構築
Material UI のインストール
プロジェクトのルートディレクトリで以下のコマンドを実行し、Material UI をインストールします
npm install @mui/material @emotion/react @emotion/styled
-
@mui/material
: Material UI のコアコンポーネント -
@emotion/react
/@emotion/styled
: スタイリングツール
詳しい手順については、公式のインストールガイドをご参照ください:
Material UI - Installation
4. Material UI を使った UI の改善
このセクションでは、main.tsx
、App.tsx
、TodoForm.tsx
、TodoItem.tsx
、TodoDetails.tsx
、Home.tsx
の UI を Material UI のコンポーネントで改善し、アプリ全体をスタイル適用済みのコンポーネントで整えていきます
4.1 main.tsx: アプリ全体へのテーマ適用
Material UI では、ThemeProvider
を使用してアプリ全体にテーマを適用することができます
テーマは、色やフォントスタイルなどのデザイン要素を統一的に管理するための設定です
CssBaseline
を利用すると、ブラウザのデフォルトスタイルがリセットされ、Material UI のスタイルを適用することができます
主なコンセプト
-
ThemeProvider:
- 作成したテーマを全ての Material UI コンポーネントに適用するためのラッパー
- アプリ全体のデザインを統一する役割を持つ
-
CssBaseline:
- 各ブラウザが持つデフォルトスタイルをリセットし、Material UI のデザインを正しく反映するためのコンポーネント
- 基本的なスタイル(ボックスモデルの初期化やフォントのリセットなど)も適用されます
main.tsx の修正
以下のように ThemeProvider
と CssBaseline
を追加して、Material UI のテーマを適用します
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { ThemeProvider, createTheme, CssBaseline } from '@mui/material' // Add: Material UI のテーマ関連をインポート
import App from './App.tsx'
// Add: カスタムテーマの作成
const theme = createTheme({
palette: {
primary: {
main: '#1976d2', // Add: プライマリカラー(青系)
},
secondary: {
main: '#dc004e', // Add: セカンダリカラー(赤系)
},
},
typography: {
fontFamily: 'Roboto, Arial, sans-serif', // Add: フォントファミリーの設定
},
})
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ThemeProvider theme={theme}> {/* Add: Material UI のテーマを適用 */}
<CssBaseline /> {/* Add: デフォルトスタイルをリセット */}
<App />
</ThemeProvider>
</StrictMode>
)
解説
-
テーマのカスタマイズ:
createTheme
を使用することで、色やフォントなどを自由にカスタマイズ可能です
例えば、palette
プロパティを使ってプライマリカラーやセカンダリカラーを指定できます -
ブラウザスタイルのリセット:
CssBaseline
を使うことで、ブラウザごとの余白やデフォルトスタイルの違いを統一し、Material UI の見た目を正しく表示できます -
ThemeProvider の使い方:
アプリ全体をThemeProvider
でラップすることで、すべての Material UI コンポーネントが指定したテーマに従います
もしこのタイミングで動作確認する場合は、ブラウザの開発者ツールなどでフォントが変わっていることを確認してみてください
テーマ適用前 | テーマ適用後 |
---|---|
![]() |
![]() |
4.2 App.tsx: レイアウトとルーティングの統合
アプリ全体のレイアウトを改善するために、Material UI の Container
を使用して中央寄せレイアウトにし、Typography
を使ってタイトル部分をスタイリッシュに装飾します
既存の React Router によるルーティング機能はそのまま活用しつつ、UI のベース構造を Material UI に置き換えます
import { useState } from 'react'
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import { Container, Typography } from '@mui/material' // Add: Material UI のレイアウト用コンポーネントをインポート
import Home from './pages/Home'
import TodoDetails from './pages/TodoDetails'
function App() {
const [todos, setTodos] = useState([
"開発準備",
"TODOアイテムのコンポーネントを作成する",
"AppコンポーネントでTODOリストを表示する"
])
const addTodo = (todo: string) => {
setTodos([...todos, todo])
}
return (
<Container maxWidth="sm"> {/* Add: アプリ全体を中央寄せレイアウトにする */}
<Typography variant="h2" gutterBottom>TODO リスト</Typography> {/* Add: タイトル表示をスタイリッシュに */}
<Router>
<Routes>
<Route path="/" element={<Home todos={todos} addTodo={addTodo} />} />
<Route path="/todo/:id" element={<TodoDetails todos={todos} />} />
</Routes>
</Router>
</Container>
)
}
export default App
解説
-
TextField
入力フィールドにラベルやスタイルが自動で付与される Material UI のコンポーネントです
fullWidth
を指定することで、横幅いっぱいに広がり、使いやすくなります -
Button
variant="contained"
を指定することで、立体的で視認性の高いボタンになります
色やサイズも簡単にカスタマイズできます -
Box
div
の代わりに使える軽量なレイアウトコンポーネントで、Material UI のsx
プロパティを使って、CSS をシンプルに適用できますここでは
display: 'flex'
とgap: 1
で、フォームの横並びと間隔を簡単に整えています
4.3 TodoForm.tsx: 入力フォームの改善
以前作成した TodoForm.tsx
は、プレーンな HTML フォームとボタンを使用しており、少し味気ない UI になっていました
ここでは、Material UI の TextField
と Button
を使用して、見た目と操作性の両面を改善します
加えて、Box
を使うことでレイアウトも簡潔に整えます
import { useState } from 'react'
import { TextField, Button, Box } from '@mui/material' // Add: Material UI のコンポーネントをインポート
function TodoForm({ addTodo }: { addTodo: (todo: string) => void }) {
const [inputValue, setInputValue] = useState('') // 入力状態を管理
const handleSubmit = (e: React.FormEvent) => { // Add: フォーム送信イベントを処理
e.preventDefault() // Add: ページリロードを防止
if (inputValue.trim()) {
addTodo(inputValue)
setInputValue('') // Add: TODO追加後に入力欄をリセット
}
}
return (
<Box
component="form" // Add: form要素としての振る舞いを持たせる
onSubmit={handleSubmit} // Add: Submit時の処理を紐付け
sx={{ display: 'flex', gap: 1 }} // Add: 横並びと間隔調整のスタイル
>
<TextField
label="新しいTODO" // Add: フィールド上部のラベル
variant="outlined" // Add: 枠付きデザインのスタイル
fullWidth // Add: 横幅いっぱいに広げる
value={inputValue}
onChange={(e) => setInputValue(e.target.value)} // 入力更新
/>
<Button
type="submit" // Add: フォーム送信ボタンとして動作
variant="contained" // Add: 塗りつぶしタイプのデザイン
>
追加
</Button>
</Box>
)
}
export default TodoForm
4.4 TodoItem.tsx: TODO リストアイテムの改善
これまでの TodoItem.tsx
では、TODO を単なるテキストリンクとして表示していましたが、UI の視認性や操作性の観点からは少し味気ない印象です
このセクションでは、Material UI の Card
コンポーネントを使って TODO アイテムをカード形式で表示し、ひと目で見やすく、クリックしやすい UI に改善します
さらに、Typography
を活用して文字のスタイルも整え、全体のデザインに統一感をもたせていきます
// src/components/TodoItem.tsx
import React from 'react'
import { Link } from 'react-router-dom' // React Router の Link を使用
import { Card, CardContent, Typography } from '@mui/material' // Add: Material UI の表示用コンポーネントをインポート
function TodoItem({ index, content }: { index: number, content: string }) {
return (
<Card
variant="outlined" // Add: 枠線付きのカードデザイン
sx={{ marginBottom: 1 }} // Add: 下方向の余白を追加
>
<CardContent>
{/* Add: テキストを Material UI の Typography で表示 */}
<Typography variant="body1">
<Link to={`/todo/${index}`} style={{ textDecoration: 'none', color: 'inherit' }}>
{/* Add: クリック可能なリンク(装飾はTypographyに準拠) */}
{content}
</Link>
</Typography>
</CardContent>
</Card>
)
}
export default TodoItem
解説
-
Card / CardContent
- TODO アイテム全体を
Card
で囲むことで、枠線や余白が自動で適用され、情報の区切りが明確になります -
variant="outlined"
により、フラットながら視認性の高い枠付きカードになります -
sx={{ marginBottom: 1 }}
によってリスト同士の間隔も簡単に調整できます
- TODO アイテム全体を
-
Typography
- テキストのサイズや行間を整えて、読みやすいデザインに
- 今後、フォントスタイルをテーマに合わせて変更したい場合も、
Typography
を使っておくと柔軟に対応できます
-
Link
- React Router の
Link
を使って、各 TODO の詳細ページに遷移できるようにします -
style={{ textDecoration: 'none', color: 'inherit' }}
を指定することで、リンクっぽい青い下線を消しつつ、親要素のスタイル(Typography)を継承して自然な見た目に保ちます
- React Router の
4.5 Home.tsx: ホームページのレイアウト改善
TodoItemがCardコンポーネントを使用するようになったため、Home.tsxのレイアウトも調整する必要があります
従来の<ul>
リストから、Material UIのBox
コンポーネントを使用したレイアウトに変更します
// src/pages/Home.tsx
import { Box } from '@mui/material' // add: Material UI の Box コンポーネントをインポート
import TodoItem from '../components/TodoItem'
import TodoForm from '../components/TodoForm'
function Home(props: { todos: string[]; addTodo: (todo: string) => void }) {
return (
<Box> {/* edit: <div> を Box に置き換えて Material UI のスタイル適用を可能に */}
<TodoForm addTodo={props.addTodo} />
<Box sx={{ mt: 2 }}> {/* add: TODO リスト部分にマージンを追加し、縦方向の余白を確保 */}
{props.todos.map((todo, index) => (
<TodoItem key={index} index={index} content={todo} />
))}
</Box>
</Box>
)
}
export default Home
解説
-
Box コンポーネント
-
<div>
の代わりに使用できる汎用的なコンテナコンポーネントです -
sx
プロパティを使用して、簡単にスタイリングを適用できます -
mt: 2
は「margin-top: 2」を意味し、上部に余白を追加します
2
は Material UI の spacing ユニット(通常は 8px × 値)
-
-
リストの変更
- 従来の
<ul>
と<li>
タグを使用したHTMLリストから、Material UIのコンポーネントを使用したレイアウトに変更しています - これにより、TodoItemのCardコンポーネントがより自然に表示されます
- 従来の
4.6 TodoDetails.tsx: 詳細ページのレイアウト改善
最後に、TODO詳細ページのUIも改善します
従来のHTMLタグを使用した実装から、Material UIのコンポーネントを使用した実装に変更します
// src/pages/TodoDetails.tsx
import { useParams, Link } from 'react-router-dom'
import { Box, Typography, Button, Card, CardContent } from '@mui/material' // add: Material UI の各種 UI コンポーネントをインポート
function TodoDetails(props: { todos: string[] }) {
const { id } = useParams<{ id: string }>()
const todo = props.todos[Number(id)]
if (!todo) {
return (
<Box> {/* edit: <div> を Box に変更し、MUI スタイリングを適用可能に */}
<Typography variant="h6" color="error">TODO not found</Typography> {/* add: Typography を使ってエラーメッセージを赤色でスタイリング */}
<Button component={Link} to="/" variant="contained" sx={{ mt: 2 }}> {/* add: 戻るリンクを Button に変更し、操作性を向上 */}
Back to TODO List
</Button>
</Box>
)
}
return (
<Box> {/* edit: <div> を Box に変更し、全体のレイアウトを整える */}
<Typography variant="h4" gutterBottom>TODO Details</Typography> {/* add: タイトル見出しを Typography に変更してデザインを統一 */}
<Card variant="outlined" sx={{ mb: 3 }}> {/* add: TODO 詳細を Card で囲み、視覚的に区切りを追加 */}
<CardContent>
<Typography variant="body1">{todo}</Typography> {/* add: 本文テキストも Typography に変更し、整った表示に */}
</CardContent>
</Card>
<Button component={Link} to="/" variant="contained"> {/* add: ボタン型のナビゲーションリンクを追加 */}
Back to TODO List
</Button>
</Box>
)
}
export default TodoDetails
解説
-
エラー表示の改善
- TODOが見つからない場合のエラーメッセージを
Typography
コンポーネントで表示し、color="error"
を指定して赤色で警告表示しています - 戻るボタンも
Button
コンポーネントを使用して視認性を高めています
- TODOが見つからない場合のエラーメッセージを
-
詳細表示の改善
- TODOの詳細を
Card
コンポーネントで囲み、視覚的に区切りを明確にしています -
Typography
コンポーネントを使用してテキストのスタイルを整えています
- TODOの詳細を
-
ナビゲーションの改善
- 「Back to TODO List」リンクを
Button
コンポーネントに変更し、クリック領域を広げて視認性と操作性の両面で向上します -
component={Link}
を指定することで、Material UIのButton
コンポーネントとReact RouterのLink
コンポーネントを組み合わせています
- 「Back to TODO List」リンクを
5. 動作確認
-
以下のコマンドでアプリを起動します
npm run dev -- --host 0.0.0.0
※ Material UI のインストール(ステップ3)が完了している必要があります
-
確認ポイント:
- フォームが Material UI の
TextField
とButton
に変わっていること - 入力すると新しい TODO が追加されること
- 各 TODO が Material UI の
Card
で表示されること - テーマ(フォントやカラー)が適用されており、ブラウザのデフォルトスタイルと異なること
- リストが画面中央に表示されており、
Container
の効果があること - TODO をクリックすると URL が
/todo/0
のように変わること(ルーティングが正しく機能していること)
必要に応じて、開発者ツールの「要素」タブや「ネットワーク」タブで各コンポーネントの描画や遷移動作を確認すると、より深く理解できます
6. まとめ
このチュートリアルでは、Material UI を使用して以下のことを学びました
- Material UI の基本的な導入方法
-
TextField
,Button
,Card
などのコンポーネントの利用 - フォームとリストアイテムのデザイン向上
7. 次のステップ
次回の記事では、Material UI のレイアウトコンポーネント(Grid
や Box
)を使用して、TODO リストを複数のペインに分割した UI を作成します
さらに、Material UI のドキュメントを活用して目的のコンポーネントを探す方法についても紹介します
この記事で得た知識を活用して、独自の UI カスタマイズにも挑戦してみてほしいです!
Discussion