How to Reference a Local Image in React: Complete Guide to Static Assets

Use ES6 import statements for images processed through your build pipeline, store files in the public folder for static URLs, or leverage require() for dynamic image paths in React applications.

Referencing local images in React requires understanding how static assets interact with the build system. Whether you are bundling with webpack, Vite, or Parcel, React projects support multiple patterns for handling local image files depending on whether you need processing, optimization, or runtime path resolution.

Import Images Using ES6 Modules

The standard approach for referencing local images uses ES6 module imports. This method processes files through your build system, enabling optimizations like hashing, compression, and inclusion in the final bundle.

Place your image in the src directory alongside your components, then import it directly:

import React from 'react';
import logoImage from './assets/logo.png';

function Header() {
  return (
    <header>
      <img src={logoImage} alt="Company Logo" />
    </header>
  );
}

export default Header;

When you import an image this way, your bundler handles path resolution and may add cache-busting content hashes to the filename during production builds. This ensures browsers load updated assets when your application redeploys. Do not wrap the imported variable in quotes when assigning it to the src attribute, as import returns a processed URL string or data URI.

Reference Static Files in the Public Folder

For images that do not need processing or that must maintain specific URLs (such as robots.txt or favicon.ico), place files in the public folder at your project root. Standard React build configurations automatically expose these files at the root path.

Move your image to public/images/photo.jpg, then reference it with an absolute path:

function Gallery() {
  return (
    <div>
      <img src="/images/photo.jpg" alt="Gallery Item" />
    </div>
  );
}

Files in the public folder bypass the build pipeline entirely. They are copied as-is to the output directory, making this approach ideal for external CSS files that cannot resolve webpack module paths or when you need guaranteed static URLs for SEO and social media meta tags.

Use require() for Dynamic Image Paths

When image paths must be determined at runtime based on props or state, use CommonJS require() syntax:

function ProductCard({ productId }) {
  // Construct path dynamically based on props
  const imagePath = `./assets/products/${productId}.jpg`;
  
  return (
    <img src={require(imagePath)} alt="Product" />
  );
}

Important limitation: require() needs webpack or your bundler to understand the dependency graph at build time. Completely dynamic paths (where the directory or filename is generated entirely at runtime) may cause module resolution errors. For truly dynamic references where the exact filename is unknown until execution, store images in the public folder and construct the URL string directly instead of using require().

Handle Background Images in CSS

When referencing local images within CSS files, use relative paths from the stylesheet location:

/* In a CSS module or global stylesheet */
.hero-banner {
  background-image: url('../assets/background.jpg');
  background-size: cover;
  background-position: center;
}

For CSS-in-JS solutions like styled-components, import the asset first to ensure proper processing:

import styled from 'styled-components';
import backgroundImg from './background.jpg';

const HeroSection = styled.div`
  background-image: url(${backgroundImg});
  background-size: cover;
  height: 100vh;
`;

Summary

  • ES6 imports (import image from './path.png') process images through the build pipeline with optimization, hashing, and compression for files in the src directory.
  • Public folder storage (/images/file.png) serves files statically without processing, accessible via absolute root-relative paths that remain constant across builds.
  • require() syntax enables dynamic image loading when paths depend on runtime variables, though it requires partial literal paths for bundler compatibility.
  • CSS references use standard relative paths in stylesheets or imported variables in CSS-in-JS to ensure correct asset resolution.

Frequently Asked Questions

Why does my local image show in development but return 404 in production?

This typically occurs when using string literals like src="./image.png" instead of imported variables. During production builds, filenames receive content hashes for cache busting, breaking hardcoded paths. Always use import image from './image.png' and reference the image variable, or move the file to the public folder and use absolute paths starting with /.

Can I use template literals or variables inside ES6 import statements?

No, ES6 imports require static string literals that can be resolved at parse time. You cannot use import image from `${variable}` or similar dynamic expressions. For runtime-dependent images, use the public folder and construct the path string, or use require() with webpack's require.context for batch processing multiple assets.

Should I use import or the public folder for my React images?

Use import when you want the image processed (compressed, hashed for caching, inline data URIs for small files) and when the image is referenced from JavaScript or CSS. Use the public folder only when you need the original filename preserved, when referencing from external CSS files outside the module system, or for files like manifest.json and robots.txt that require specific public URLs.

How do I handle user-uploaded or external images in React?

For user-uploaded content or external URLs, store the full URL string in state or props and pass it directly to the src attribute: <img src={user.imageUrl} alt="User upload" />. Never attempt to import or require() these paths, as they resolve at runtime rather than build time. Ensure you sanitize external URLs to prevent XSS vulnerabilities when rendering user-provided content.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s https://instagit.com/install.md

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client