Reactアプリケーションをさまざまな画面サイズに対応させるには、CSSメディアクエリやカスタムフックを組み合わせたレスポンシブデザインの実装が必要です。ここでは、Reactでレスポンシブなレイアウトを構築する方法を解説します。
基本的な使い方
sample.js
import React, { useState, useEffect } from 'react';
// メディアクエリを監視するカスタムフック
function useMediaQuery(query) {
const [matches, setMatches] = useState(false);
useEffect(() => {
const mediaQuery = window.matchMedia(query);
setMatches(mediaQuery.matches);
const handler = (event) => setMatches(event.matches);
mediaQuery.addEventListener('change', handler);
return () => mediaQuery.removeEventListener('change', handler);
}, [query]);
return matches;
}
function ResponsiveLayout() {
// レスポンシブなブレイクポイント
const isMobile = useMediaQuery('(max-width: 767px)');
const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1023px)');
// モバイルメニューの状態
const [menuOpen, setMenuOpen] = useState(false);
// 画面サイズに基づいたスタイル
const styles = {
container: {
maxWidth: '1200px',
margin: '0 auto',
padding: isMobile ? '10px' : isTablet ? '20px' : '30px',
fontFamily: 'Arial, sans-serif'
},
navbar: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#333',
color: 'white',
padding: '15px',
borderRadius: '5px',
marginBottom: '20px'
},
logo: {
fontSize: isMobile ? '20px' : '24px',
fontWeight: 'bold'
},
menuButton: {
display: isMobile ? 'block' : 'none',
background: 'none',
border: 'none',
color: 'white',
fontSize: '16px',
cursor: 'pointer'
},
desktopMenu: {
display: isMobile ? 'none' : 'flex',
listStyle: 'none',
margin: 0,
padding: 0
},
mobileMenu: {
display: menuOpen && isMobile ? 'block' : 'none',
position: 'absolute',
top: '65px',
left: 0,
right: 0,
backgroundColor: '#333',
zIndex: 100
},
menuItem: {
margin: isMobile ? '0' : '0 15px',
padding: isMobile ? '15px' : '0',
borderBottom: isMobile ? '1px solid #444' : 'none',
textAlign: isMobile ? 'center' : 'left'
},
menuLink: {
color: 'white',
textDecoration: 'none'
},
hero: {
backgroundColor: '#f0f0f0',
padding: isMobile ? '20px' : isTablet ? '30px' : '40px',
borderRadius: '5px',
marginBottom: '20px',
textAlign: 'center'
},
heroTitle: {
fontSize: isMobile ? '24px' : isTablet ? '32px' : '42px',
color: '#333',
marginBottom: '10px'
},
heroText: {
fontSize: isMobile ? '16px' : '18px',
color: '#666',
maxWidth: '700px',
margin: '0 auto'
},
cardGrid: {
display: 'grid',
gridTemplateColumns: isMobile ? '1fr' : isTablet ? '1fr 1fr' : '1fr 1fr 1fr',
gap: isMobile ? '15px' : '25px',
marginBottom: '30px'
},
card: {
backgroundColor: 'white',
borderRadius: '5px',
padding: '20px',
boxShadow: '0 2px 5px rgba(0,0,0,0.1)'
},
cardTitle: {
fontSize: '20px',
color: '#333',
marginTop: 0
},
cardContent: {
color: '#666'
},
footer: {
textAlign: 'center',
padding: '20px',
borderTop: '1px solid #eee',
marginTop: '30px',
color: '#666'
}
};
return (
<div style={styles.container}>
{/* ナビゲーションバー */}
<nav style={styles.navbar}>
<div style={styles.logo}>ReactSite</div>
{/* モバイルメニューボタン */}
<button
style={styles.menuButton}
onClick={() => setMenuOpen(!menuOpen)}
>
{menuOpen ? '閉じる ×' : 'メニュー ☰'}
</button>
{/* デスクトップメニュー */}
<ul style={styles.desktopMenu}>
<li style={styles.menuItem}><a style={styles.menuLink} href="#home">ホーム</a></li>
<li style={styles.menuItem}><a style={styles.menuLink} href="#about">会社情報</a></li>
<li style={styles.menuItem}><a style={styles.menuLink} href="#services">サービス</a></li>
<li style={styles.menuItem}><a style={styles.menuLink} href="#contact">お問い合わせ</a></li>
</ul>
{/* モバイルメニュー */}
<ul style={styles.mobileMenu}>
<li style={styles.menuItem}><a style={styles.menuLink} href="#home">ホーム</a></li>
<li style={styles.menuItem}><a style={styles.menuLink} href="#about">会社情報</a></li>
<li style={styles.menuItem}><a style={styles.menuLink} href="#services">サービス</a></li>
<li style={styles.menuItem}><a style={styles.menuLink} href="#contact">お問い合わせ</a></li>
</ul>
</nav>
{/* ヒーローセクション */}
<div style={styles.hero}>
<h1 style={styles.heroTitle}>レスポンシブなReactサイト</h1>
<p style={styles.heroText}>
このサンプルは、メディアクエリとインラインスタイルを使用して
レスポンシブなレイアウトを実現しています。画面サイズを変更して、
レイアウトがどのように変化するか確認してください。
</p>
</div>
{/* 現在の表示モード */}
<div style={{ backgroundColor: '#e0f7fa', padding: '10px', borderRadius: '5px', marginBottom: '20px', textAlign: 'center' }}>
<p>現在の表示モード: <strong>{isMobile ? 'モバイル' : isTablet ? 'タブレット' : 'デスクトップ'}</strong></p>
</div>
{/* カードグリッド */}
<div style={styles.cardGrid}>
<div style={styles.card}>
<h3 style={styles.cardTitle}>レスポンシブデザイン</h3>
<p style={styles.cardContent}>
レスポンシブデザインは、異なる画面サイズに適応するためのアプローチです。
CSSメディアクエリやFlexbox、Gridなどを使って実現します。
</p>
</div>
<div style={styles.card}>
<h3 style={styles.cardTitle}>Reactコンポーネント</h3>
<p style={styles.cardContent}>
Reactコンポーネントを使うことで、レスポンシブな要素を簡単に再利用できます。
カスタムフックを作成して、メディアクエリを簡単に扱うこともできます。
</p>
</div>
<div style={styles.card}>
<h3 style={styles.cardTitle}>モバイルファースト</h3>
<p style={styles.cardContent}>
モバイルファーストのアプローチでは、まずモバイル向けのデザインを作成し、
大きな画面サイズに対応するスタイルを後から追加していきます。
</p>
</div>
</div>
{/* フッター */}
<footer style={styles.footer}>
<p>ReactレスポンシブデザインのサンプルSPAサイト © 2025</p>
</footer>
</div>
);
}
export default ResponsiveLayout;
説明
Step 1レスポンシブデザインの基本
レスポンシブデザインとは、様々な画面サイズやデバイスに対応できるウェブデザインのアプローチです。Reactでは、CSSの機能を活用してレスポンシブなコンポーネントを作成できます。
レスポンシブデザインの基本的な技術:
- メディアクエリ:画面サイズに応じてスタイルを変更
- フレキシブルグリッド:相対的な単位(%、rem、emなど)を使用
- フレキシブルな画像:画像がコンテナに合わせて調整される
- Flexbox/Grid:モダンなレイアウト技術
Step 2Reactでのレスポンシブスタイルの適用方法
Reactでレスポンシブデザインを実現する主な方法は3つあります:
- 通常のCSSファイル:メディアクエリを含む従来のCSS
- CSS-in-JS:styled-componentsやEmotionなどのライブラリを使用
- CSSフレームワーク:Bootstrap、Material-UI、Tailwindなどを活用
通常のCSSファイルとメディアクエリの例:
/* styles.css */ .responsive-container { padding: 20px; background-color: #f0f0f0; } /* モバイルファースト: デフォルトはモバイル向けスタイル */ .card-grid { display: grid; grid-template-columns: 1fr; /* 1カラム */ gap: 15px; } /* タブレット用 */ @media (min-width: 768px) { .card-grid { grid-template-columns: 1fr 1fr; /* 2カラム */ } } /* デスクトップ用 */ @media (min-width: 1024px) { .card-grid { grid-template-columns: 1fr 1fr 1fr; /* 3カラム */ } .responsive-container { padding: 40px; } }
// ResponsiveComponent.js import React from 'react'; import './styles.css'; function ResponsiveComponent() { return ( <div className="responsive-container"> <h1>レスポンシブグリッド</h1> <div className="card-grid"> <div className="card">カード 1</div> <div className="card">カード 2</div> <div className="card">カード 3</div> <div className="card">カード 4</div> </div> </div> ); }
Step 3CSS-in-JSとレスポンシブデザイン
styled-componentsを使用したレスポンシブデザインの例:
// まず、styled-componentsをインストール // npm install styled-components import React from 'react'; import styled from 'styled-components'; // レスポンシブなブレイクポイントを定義 const breakpoints = { mobile: '480px', tablet: '768px', desktop: '1024px' }; // メディアクエリのユーティリティ関数 const media = { mobile: `@media (min-width: ${breakpoints.mobile})`, tablet: `@media (min-width: ${breakpoints.tablet})`, desktop: `@media (min-width: ${breakpoints.desktop})` }; // スタイルコンポーネントの定義 const Container = styled.div` padding: 15px; max-width: 1200px; margin: 0 auto; ${media.tablet} { padding: 25px; } ${media.desktop} { padding: 40px; } `; const Grid = styled.div` display: grid; grid-template-columns: 1fr; gap: 15px; ${media.tablet} { grid-template-columns: 1fr 1fr; } ${media.desktop} { grid-template-columns: 1fr 1fr 1fr; gap: 25px; } `; const Card = styled.div` background-color: #ffffff; border-radius: 8px; padding: 20px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); // カードのコンテンツも必要に応じてレスポンシブに h3 { font-size: 18px; ${media.tablet} { font-size: 20px; } } `; function StyledResponsiveComponent() { return ( <Container> <h1>Styled Components レスポンシブデザイン</h1> <Grid> <Card> <h3>カード 1</h3> <p>コンテンツの例です。</p> </Card> <Card> <h3>カード 2</h3> <p>コンテンツの例です。</p> </Card> <Card> <h3>カード 3</h3> <p>コンテンツの例です。</p> </Card> </Grid> </Container> ); }
Step 4useMediaQueryフックの作成と使用
JavaScriptを使って画面サイズに基づいてコンポーネントのロジックを変更したい場合、カスタムフックを作成すると便利です:
// useMediaQuery.js import { useState, useEffect } from 'react'; function useMediaQuery(query) { // メディアクエリに一致するかどうかの状態 const [matches, setMatches] = useState(false); useEffect(() => { // メディアクエリを作成 const mediaQuery = window.matchMedia(query); // 現在の状態を設定 setMatches(mediaQuery.matches); // メディアクエリの変更を監視するリスナーを追加 const handleChange = (event) => { setMatches(event.matches); }; mediaQuery.addEventListener('change', handleChange); // クリーンアップ関数 return () => { mediaQuery.removeEventListener('change', handleChange); }; }, [query]); return matches; } export default useMediaQuery;
// このフックを使用するコンポーネント import React from 'react'; import useMediaQuery from './useMediaQuery'; function ResponsiveLayout() { const isMobile = useMediaQuery('(max-width: 767px)'); const isTablet = useMediaQuery('(min-width: 768px) and (max-width: 1023px)'); const isDesktop = useMediaQuery('(min-width: 1024px)'); return ( <div> <h1>レスポンシブレイアウト</h1> {isMobile && ( <div> <h2>モバイルビュー</h2> <!-- モバイル向けコンテンツ --> </div> )} {isTablet && ( <div> <h2>タブレットビュー</h2> <!-- タブレット向けコンテンツ --> </div> )} {isDesktop && ( <div> <h2>デスクトップビュー</h2> <!-- デスクトップ向けコンテンツ --> </div> )} <!-- 共通コンテンツ --> <p>画面サイズ: {isMobile ? 'モバイル' : isTablet ? 'タブレット' : 'デスクトップ'}</p> </div> ); }
Step 5レスポンシブなナビゲーションの実装
一般的なレスポンシブナビゲーションパターンの実装例:
import React, { useState } from 'react'; import useMediaQuery from './useMediaQuery'; import './Navbar.css'; function Navbar() { const [menuOpen, setMenuOpen] = useState(false); const isMobile = useMediaQuery('(max-width: 767px)'); const toggleMenu = () => { setMenuOpen(!menuOpen); }; return ( <nav className="navbar"> <div className="logo"> <a href="/">ロゴ</a> </div> {isMobile ? ( <> <button className="menu-toggle" onClick={toggleMenu}> {menuOpen ? 'メニューを閉じる' : 'メニューを開く'} </button> {menuOpen && ( <ul className="mobile-menu"> <li><a href="/">ホーム</a></li> <li><a href="/about">会社情報</a></li> <li><a href="/services">サービス</a></li> <li><a href="/contact">お問い合わせ</a></li> </ul> )} </> ) : ( <ul className="desktop-menu"> <li><a href="/">ホーム</a></li> <li><a href="/about">会社情報</a></li> <li><a href="/services">サービス</a></li> <li><a href="/contact">お問い合わせ</a></li> </ul> )} </nav> ); }
/* Navbar.css */ .navbar { display: flex; justify-content: space-between; align-items: center; padding: 15px 20px; background-color: #333; color: white; } .logo a { color: white; font-size: 24px; text-decoration: none; } .desktop-menu { display: flex; list-style: none; margin: 0; padding: 0; } .desktop-menu li { margin: 0 15px; } .desktop-menu a { color: white; text-decoration: none; } .menu-toggle { background: none; border: none; color: white; cursor: pointer; font-size: 16px; } .mobile-menu { position: absolute; top: 60px; left: 0; right: 0; background-color: #333; padding: 10px 0; list-style: none; margin: 0; } .mobile-menu li { padding: 12px 20px; border-bottom: 1px solid #444; } .mobile-menu a { color: white; text-decoration: none; display: block; }
レスポンシブデザインのベストプラクティス:
- モバイルファーストのアプローチを採用する
- パフォーマンスを考慮し、適切な画像サイズを使用する
- タッチデバイス向けに十分なタップ領域を確保する
- ビューポートメタタグを設定する:
<meta name="viewport" content="width=device-width, initial-scale=1" /> - 機能検出(feature detection)を使用してデバイス機能に応じた体験を提供する
- アクセシビリティを忘れずに考慮する
まとめ
- CSSメディアクエリを使えば、画面幅に応じてスタイルを切り替えられる
useMediaQueryカスタムフックで、JavaScript側から画面サイズを検知できる- モバイルファーストで設計し、小さい画面から順にスタイルを拡張するのが基本
- CSS-in-JSやstyled-componentsでもレスポンシブ対応が可能
- フレックスボックスやCSSグリッドを活用すると柔軟なレイアウトが実現できる
- 画像やフォントサイズも画面サイズに合わせて調整する