Reactで画像を表示する方法|publicとsrcフォルダの違い

Reactで画像を表示する方法をpublicフォルダとsrcフォルダの違いから解説。初心者向けに実装例とベストプラクティスを紹介します。

Learning Next 運営
23 分で読めます

みなさん、Reactで開発していてこんな困ったことはありませんか?

「画像を追加したのに表示されない」
「publicフォルダとsrcフォルダ、どっちに画像を置けばいいの?」
「画像のパスの書き方がよくわからない」

Reactでの画像表示は、初心者がつまずきやすいポイントの一つです。 でも安心してください!

この記事では、Reactで画像を確実に表示する方法をわかりやすく解説します。 publicフォルダとsrcフォルダの違いから、実際の使い分けまで詳しくご紹介しますね。

publicとsrcフォルダって何が違うの?

Reactで画像を扱う時、主に2つの置き場所があります。 それぞれの特徴を理解することが、正しく画像を表示するコツです。

publicフォルダの特徴

publicフォルダは、そのまま公開される場所です。

publicフォルダの特徴

  • ビルド時にそのままコピーされる
  • URLで直接アクセスできる
  • ファイル名が変更されない
  • 絶対パスで指定する
public/
├── images/
│   ├── logo.png
│   └── hero.jpg
├── favicon.ico
└── index.html

この構造だと、http://localhost:3000/images/logo.pngで直接アクセスできます。

srcフォルダの特徴

srcフォルダは、Webpackで処理される場所です。

srcフォルダの特徴

  • ビルド時に最適化される
  • import文で読み込む必要がある
  • ファイル名にハッシュが付く
  • 相対パスで指定する
src/
├── assets/
│   └── images/
│       ├── logo.png
│       └── icon.svg
├── components/
└── App.js

この場合、画像を使うにはimport文が必要になります。

どちらを選べばいい?

簡単な判断基準をご紹介します。

publicフォルダを使う場合

  • 外部から直接アクセスしたい画像
  • SEO用のOG画像
  • たくさんの画像を動的に表示する場合

srcフォルダを使う場合

  • コンポーネントで使う画像
  • アイコンや装飾用画像
  • 最適化が必要な画像

迷ったらsrcフォルダから始めるのがおすすめです。

publicフォルダで画像を表示してみよう

まずは、publicフォルダを使った画像表示方法を見てみましょう。 手順がわかりやすいので、初心者にもおすすめです。

ステップ1: 画像を配置する

publicフォルダ内に画像を配置します。

public/
├── images/        ← imagesフォルダを作成
│   ├── logo.png   ← 画像ファイルを配置
│   ├── hero.jpg
│   └── avatar.png
├── favicon.ico
└── index.html

imagesフォルダを作ると、ファイルが整理されて管理しやすくなります。

ステップ2: JSXで表示する

publicフォルダの画像は、絶対パスで指定します。

function App() {
  return (
    <div>
      <img src="/images/logo.png" alt="ロゴ" />
      <img src="/images/hero.jpg" alt="ヒーロー画像" />
      <img src="/images/avatar.png" alt="アバター" />
    </div>
  );
}

ポイントは、パスの最初に「/」を付けることです。 これで、publicフォルダを基準とした絶対パスになります。

ステップ3: 環境変数を使ってより安全に

デプロイ環境でも確実に動作させるために、環境変数を使う方法もあります。

function App() {
  return (
    <div>
      <img 
        src={process.env.PUBLIC_URL + "/images/logo.png"} 
        alt="ロゴ" 
      />
      <img 
        src={process.env.PUBLIC_URL + "/images/hero.jpg"} 
        alt="ヒーロー画像" 
      />
    </div>
  );
}

process.env.PUBLIC_URLを使うことで、デプロイ先のURLが変わっても正しく表示されます。

動的な画像表示の例

記事のサムネイルなど、動的に変わる画像を表示する場合です。

function ArticleList({ articles }) {
  return (
    <div className="article-list">
      {articles.map(article => (
        <div key={article.id} className="article-card">
          <img 
            src={`/images/articles/${article.thumbnail}`} 
            alt={article.title}
            className="thumbnail"
          />
          <h3>{article.title}</h3>
          <p>{article.description}</p>
        </div>
      ))}
    </div>
  );
}

テンプレートリテラルを使って、動的にパスを生成できます。 このような場合は、publicフォルダが便利です。

srcフォルダで画像を表示してみよう

次に、srcフォルダを使った画像表示方法を見てみましょう。 少し手順が増えますが、最適化の恩恵を受けられます。

ステップ1: 画像を配置する

srcフォルダ内にassetsフォルダを作って画像を配置します。

src/
├── assets/
│   ├── images/
│   │   ├── logo.png    ← ロゴ画像
│   │   ├── hero.jpg    ← ヒーロー画像
│   │   └── avatar.png  ← アバター画像
│   └── icons/
│       ├── search.svg  ← アイコン
│       └── menu.svg
├── components/
└── App.js

assetsフォルダの中でimagesとiconsに分けると、さらに整理しやすくなります。

ステップ2: import文で読み込む

srcフォルダの画像は、必ずimport文で読み込みます。

import logo from './assets/images/logo.png';
import heroImage from './assets/images/hero.jpg';
import avatarImage from './assets/images/avatar.png';

function App() {
  return (
    <div>
      <img src={logo} alt="ロゴ" />
      <img src={heroImage} alt="ヒーロー画像" />
      <img src={avatarImage} alt="アバター" />
    </div>
  );
}

import文を使うことで、画像がモジュールとして扱われます。 ビルド時に最適化されて、ファイル名にハッシュが付きます。

ステップ3: コンポーネントごとに整理

各コンポーネントで使う画像は、そのコンポーネントの近くに配置することもできます。

src/
├── components/
│   ├── Header/
│   │   ├── Header.jsx
│   │   ├── Header.css
│   │   └── logo.png     ← Headerで使う画像
│   └── Hero/
│       ├── Hero.jsx
│       ├── Hero.css
│       └── hero-bg.jpg  ← Heroで使う画像
└── App.js

このように配置すると、どのコンポーネントがどの画像を使っているかが一目でわかります。

// Header/Header.jsx
import React from 'react';
import logo from './logo.png';  // 同じフォルダから読み込み

function Header() {
  return (
    <header>
      <img src={logo} alt="サイトロゴ" className="logo" />
      <nav>
        <ul>
          <li><a href="#home">ホーム</a></li>
          <li><a href="#about">About</a></li>
        </ul>
      </nav>
    </header>
  );
}

export default Header;

動的読み込みの方法

画像名が動的に変わる場合は、require文を使います。

function UserAvatar({ username }) {
  // ユーザー名に基づいて画像を動的に読み込み
  const avatarSrc = require(`./assets/images/avatars/${username}.png`);
  
  return (
    <img 
      src={avatarSrc} 
      alt={`${username}のアバター`}
      className="user-avatar"
    />
  );
}

ただし、この方法は画像のファイル名が事前に決まっている場合にのみ使えます。

実際のプロジェクトでの使い分け

では、実際のプロジェクトではどのように使い分けるのでしょうか? 具体例を見てみましょう。

ヘッダーコンポーネントの例

サイトのロゴなど、固定の画像はsrcフォルダに配置します。

import React from 'react';
import logo from '../assets/images/logo.png';
import './Header.css';

function Header() {
  return (
    <header className="header">
      <div className="header-content">
        <img src={logo} alt="サイトロゴ" className="logo" />
        <nav className="navigation">
          <ul>
            <li><a href="#home">ホーム</a></li>
            <li><a href="#services">サービス</a></li>
            <li><a href="#contact">お問い合わせ</a></li>
          </ul>
        </nav>
      </div>
    </header>
  );
}

export default Header;

ロゴのような重要な画像は、srcフォルダに配置してimportで読み込みます。 これにより、ビルド時に最適化され、確実に画像が含まれます。

ブログ記事一覧の例

記事のサムネイルなど、動的に変わる画像はpublicフォルダを使います。

import React from 'react';
import './BlogList.css';

function BlogList({ posts }) {
  return (
    <div className="blog-list">
      <h2>最新記事</h2>
      <div className="posts-grid">
        {posts.map(post => (
          <article key={post.id} className="post-card">
            <img 
              src={`/images/blog/${post.thumbnail}`} 
              alt={post.title}
              className="post-thumbnail"
            />
            <div className="post-content">
              <h3>{post.title}</h3>
              <p>{post.excerpt}</p>
              <time>{post.publishedAt}</time>
            </div>
          </article>
        ))}
      </div>
    </div>
  );
}

export default BlogList;

記事のサムネイルは、CMSやAPIから動的に取得することが多いので、publicフォルダが適しています。

プロフィール画像の例

ユーザーのプロフィール画像など、外部から取得する画像の表示例です。

import React, { useState } from 'react';
import defaultAvatar from '../assets/images/default-avatar.png';

function UserProfile({ user }) {
  const [imageError, setImageError] = useState(false);

  const handleImageError = () => {
    setImageError(true);
  };

  return (
    <div className="user-profile">
      <img 
        src={imageError ? defaultAvatar : user.profileImage} 
        alt={`${user.name}のプロフィール画像`}
        onError={handleImageError}
        className="profile-image"
      />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
    </div>
  );
}

export default UserProfile;

この例では、以下のことを行っています。

  • デフォルト画像: srcフォルダに配置してimport
  • ユーザー画像: 外部URLまたはpublicフォルダの画像
  • エラーハンドリング: 画像が読み込めない場合はデフォルト画像を表示

画像を最適化しよう

画像表示がうまくいったら、次はパフォーマンスを向上させましょう。 簡単にできる最適化方法をご紹介します。

ファイル形式を使い分ける

用途に応じて適切なファイル形式を選びましょう。

JPEG: 写真や複雑な画像に適している

function PhotoGallery({ photos }) {
  return (
    <div className="photo-gallery">
      {photos.map(photo => (
        <img 
          key={photo.id}
          src={`/images/gallery/${photo.filename}.jpg`}  // JPEG形式
          alt={photo.description}
          className="gallery-image"
        />
      ))}
    </div>
  );
}

PNG: 透明度が必要な画像やロゴに適している

import logo from '../assets/images/logo.png';  // PNG形式(透明背景)

function Header() {
  return (
    <header>
      <img src={logo} alt="ロゴ" className="logo" />
    </header>
  );
}

SVG: アイコンやシンプルな図形に適している

import searchIcon from '../assets/icons/search.svg';  // SVG形式

function SearchButton() {
  return (
    <button className="search-button">
      <img src={searchIcon} alt="検索" className="search-icon" />
      検索
    </button>
  );
}

レスポンシブ画像を作る

画面サイズに応じて画像が適切に表示されるようにしましょう。

function ResponsiveImage({ src, alt, className }) {
  return (
    <img 
      src={src}
      alt={alt}
      className={className}
      style={{ 
        maxWidth: '100%', 
        height: 'auto' 
      }}
    />
  );
}

// 使用例
function ProductCard({ product }) {
  return (
    <div className="product-card">
      <ResponsiveImage 
        src={`/images/products/${product.image}`}
        alt={product.name}
        className="product-image"
      />
      <h3>{product.name}</h3>
      <p>{product.price}円</p>
    </div>
  );
}

遅延読み込みで高速化

画面に表示される時に画像を読み込む、遅延読み込みを実装しましょう。

function LazyImage({ src, alt, className }) {
  return (
    <img 
      src={src}
      alt={alt}
      className={className}
      loading="lazy"  // 遅延読み込みを有効化
    />
  );
}

// 長いリスト表示での使用例
function ImageList({ images }) {
  return (
    <div className="image-list">
      {images.map(image => (
        <LazyImage 
          key={image.id}
          src={`/images/thumbnails/${image.filename}`}
          alt={image.description}
          className="thumbnail"
        />
      ))}
    </div>
  );
}

loading="lazy"を追加するだけで、簡単に遅延読み込みが実装できます。

画像が表示されない時の対処法

「画像を追加したのに表示されない」という時のチェックポイントをご紹介します。

よくある間違いとその解決法

1. パスの間違い

// ❌ 間違った例
<img src="images/logo.png" alt="ロゴ" />        // 相対パス(動かない)
<img src="./images/logo.png" alt="ロゴ" />      // 相対パス(動かない)

// ✅ 正しい例(publicフォルダの場合)
<img src="/images/logo.png" alt="ロゴ" />       // 絶対パス

2. ファイル名の大文字・小文字

// ❌ ファイル名が「Logo.PNG」の場合
<img src="/images/logo.png" alt="ロゴ" />

// ✅ 正しい例
<img src="/images/Logo.PNG" alt="ロゴ" />

3. import文の忘れ

// ❌ srcフォルダの画像をimportせずに使用
<img src="./assets/images/logo.png" alt="ロゴ" />

// ✅ 正しい例
import logo from './assets/images/logo.png';
<img src={logo} alt="ロゴ" />

デバッグのコツ

画像が表示されない時は、以下の手順でチェックしてみましょう。

1. ブラウザの開発者ツールで確認

F12キーを押して開発者ツールを開き、Consoleタブでエラーを確認します。 404エラーが出ている場合は、パスが間違っています。

2. ファイルの存在確認

ファイルが実際に存在するか、ファイル名が正確かを確認します。

3. パスの確認

  • publicフォルダの画像: /images/filename.png(絶対パス)
  • srcフォルダの画像: import文で読み込み

4. 再起動

開発サーバーを一度停止して、再起動してみます。

# 開発サーバーを停止(Ctrl+C)
# 再起動
npm start

便利なデバッグ用コンポーネント

画像の読み込み状況を確認できるコンポーネントを作ってみましょう。

import React, { useState } from 'react';

function DebugImage({ src, alt, ...props }) {
  const [loaded, setLoaded] = useState(false);
  const [error, setError] = useState(false);

  const handleLoad = () => {
    setLoaded(true);
    console.log(`画像が正常に読み込まれました: ${src}`);
  };

  const handleError = () => {
    setError(true);
    console.error(`画像の読み込みに失敗しました: ${src}`);
  };

  return (
    <div>
      <img 
        src={src}
        alt={alt}
        onLoad={handleLoad}
        onError={handleError}
        {...props}
      />
      {!loaded && !error && <p>読み込み中...</p>}
      {error && <p style={{color: 'red'}}>画像の読み込みに失敗しました</p>}
    </div>
  );
}

// 使用例
function App() {
  return (
    <div>
      <DebugImage src="/images/logo.png" alt="ロゴ" />
    </div>
  );
}

このコンポーネントを使うと、画像の読み込み状況がわかりやすくなります。

まとめ:Reactで画像を使いこなそう!

Reactでの画像表示について、基本から実践的な使い方まで詳しく解説しました。

重要なポイント

  1. publicフォルダ: 静的な画像、外部アクセスが必要な画像
  2. srcフォルダ: コンポーネントで使う画像、最適化が必要な画像
  3. パスの指定: publicは絶対パス、srcはimport文
  4. 最適化: ファイル形式、レスポンシブ、遅延読み込み

使い分けの判断基準

  • 動的に変わる画像 → publicフォルダ
  • 固定の画像 → srcフォルダ
  • 外部からアクセスする画像 → publicフォルダ
  • 最適化したい画像 → srcフォルダ

トラブル回避のコツ

  1. パスを正確に指定する
  2. ファイル名の大文字・小文字に注意
  3. srcフォルダの画像は必ずimport
  4. 開発者ツールでエラーを確認

最初は迷うかもしれませんが、基本的なルールを覚えてしまえば簡単です。 ぜひ実際のプロジェクトで試してみてください。

きっと、思い通りの画像表示ができるようになりますよ!

関連記事