React

The #1 Fix for React Images: Local vs Deploy Path 2025

Tired of React images working locally but breaking on deployment? Discover the #1 fix for path issues in 2025. Learn the crucial difference between the public folder and importing assets.

A

Alex Miller

Senior Frontend Engineer specializing in React performance and build optimizations.

6 min read9 views

The Frustration: Why Do My Images Disappear After Deployment?

It’s a rite of passage for every React developer. You’ve spent hours meticulously crafting a beautiful user interface. The logos, icons, and banners look perfect on your local machine. You run `npm run build`, push your code, and deploy. You open the live site, proud of your work, only to be greeted by a sea of broken image icons. The console is screaming with `404 Not Found` errors. It worked on your machine, so what went wrong?

This frustrating disconnect between your local development environment and the final deployed application is one of the most common hurdles in the React ecosystem. The good news is that there's a definitive, modern solution. By the end of this guide, you'll understand exactly why this happens and master the #1 fix to ensure your images are always displayed correctly in 2025.

The Root Cause: Development Server vs. Production Build

The core of the problem lies in the fundamental difference between how your project is handled during development versus in production.

  • During Development (`npm start`): A development server (like the one in Create React App or Vite) is running. It's smart and flexible. It creates a virtual environment where file paths often map directly to your source code structure. When you use a path like `src/assets/logo.png`, the dev server knows exactly where to find it and serves it to the browser.
  • For Production (`npm run build`): Your code goes through a build process managed by a bundler like Webpack or Vite. This process is about optimization. It takes all your JavaScript, CSS, and yes, your images, and transforms them. Files are minified, bundled together, and most importantly for images, they are often renamed with a unique hash for cache-busting (e.g., `logo.a8f5d6c2.png`). Your original `src/assets/logo.png` path no longer exists in the final `build` or `dist` folder.

When you use a hardcoded path like `` in your component, it works in development but fails in production because that path is meaningless to the built application.

The Two Paths for React Images

React build tools provide two primary mechanisms for handling static assets like images. Understanding when and why to use each one is the key to solving this problem for good.

Method 1: The `public` Folder - The Straightforward but Brittle Way

Every standard React project (created with CRA, Vite, etc.) has a `public` folder. The rule for this folder is simple: anything inside it is NOT processed by the bundler. Instead, it's copied directly into the root of your build output folder.

When to Use It:

  • Assets that need a predictable, unchanging path (e.g., `robots.txt`, `manifest.json`).
  • The `favicon.ico` for your site.
  • Images or scripts that are referenced by external libraries that need a static URL.

How to Use It:

You place your image, say `my-logo.png`, inside `public/images/`. Then, in your code, you reference it with a path relative to the root. To do this reliably, you should use the `PUBLIC_URL` environment variable.

<img src={`${process.env.PUBLIC_URL}/images/my-logo.png`} alt="My Company Logo" />

Note: In Vite, you can often just use a root-relative path like `<img src="/images/my-logo.png" />` as Vite handles this correctly. However, using `PUBLIC_URL` is a safer, more portable pattern, especially if you ever migrate from Create React App.

The Downside: Since the bundler doesn't see this image, you get zero benefits. There's no cache-busting hash, and more importantly, if you misspell the filename or move the file, you won't get a build error. You'll only discover the problem in production—the very issue we're trying to solve!

Method 2: Importing Assets in `src` - The #1 Fix

This is the modern, recommended, and most robust approach for handling the vast majority of your application's images. By placing your images inside the `src` folder (e.g., `src/assets/`) and importing them directly into your components, you make them part of the build graph.

When to Use It:

  • Logos, icons, and any image that is part of your component's UI.
  • Background images in CSS-in-JS or CSS Modules.
  • Essentially, for 95% of your image needs.

How it Works:

When you `import` an image, you're not actually getting the image data. Instead, the bundler (Webpack/Vite) processes the image and gives you back a string: the final, public path to that asset in the build folder.

import React from 'react';
import companyLogo from '../assets/company-logo.svg'; // The path is relative to this file

function Header() {
  // The 'companyLogo' variable here will be a string like '/static/media/company-logo.a8f5d6c2.svg'
  return (
    <header>
      <img src={companyLogo} alt="Company logo" />
    </header>
  );
}

export default Header;

The Upside (Why This is the Fix):

  1. Error-Proofing: If you misspell the image path or the file `company-logo.svg` doesn't exist, your application will fail to compile during development. You find the error immediately, not after deployment.
  2. Performance: The bundler adds a unique content hash to the filename (`company-logo.a8f5d6c2.svg`). This means you can safely configure aggressive, long-term caching for your assets. When you update the image, the hash changes, and users automatically get the new version.
  3. Optimization: Advanced configurations can allow bundlers to automatically optimize images, such as converting them to modern formats like WebP or creating multiple sizes for responsive images.

Comparison Table: `public` Folder vs. `import` from `src`

Feature Comparison of Image Handling Methods
Feature `public` Folder `import` from `src`
Build Process Bypasses the bundler; files are copied as-is. Managed by the bundler; becomes part of the module graph.
Path Handling Static, predictable path. Referenced via root URL. Dynamic path generated by the bundler. Returned as a string from `import`.
Error Checking None. A typo results in a 404 error in production. Compile-time error. Build fails if the image is missing.
Performance No automatic cache-busting. Manual updates required. Automatic content hashing for efficient cache-busting.
Optimization No automatic optimization. Can be integrated with image optimization loaders/plugins.
Best Use Case `favicon.ico`, `robots.txt`, assets needing a fixed URL. All UI images: logos, icons, banners, component assets.

Bonus: Handling Dynamic Image Paths

What if the image you need to display is determined by data, like from an API response? You can't use a dynamic string in a standard `import` statement. Here's how to handle it.

Let's say you have a set of user avatars in `src/assets/avatars/` and you need to show one based on `props.userName`.

The Vite Way (`import.meta.glob`):

Vite offers a powerful feature for importing multiple modules. You can use it to eagerly or lazily load all matching images.

// Eagerly load all avatars
const avatarModules = import.meta.glob('../assets/avatars/*.png', { eager: true });

function UserProfile({ userName }) {
  const avatarPath = avatarModules[`../assets/avatars/${userName}.png`]?.default;
  return <img src={avatarPath} alt={userName} />;
}

The Webpack Way (`require.context`):

Webpack has a similar, though slightly different, API for this purpose.

function importAll(r) {
  let images = {};
  r.keys().map((item, index) => { images[item.replace('./', '')] = r(item); });
  return images;
}

const avatars = importAll(require.context('../assets/avatars', false, /\.png$/));

function UserProfile({ userName }) {
  const avatarPath = avatars[`${userName}.png`];
  return <img src={avatarPath} alt={userName} />;
}

Conclusion: Embrace the Build Process

The mystery of disappearing React images is not a bug, but a feature of modern web development. The build process is your friend, designed to make your application faster and more reliable. By letting it manage your assets, you gain compile-time safety and performance optimizations for free.

For 2025 and beyond, the rule is clear: for any image that is part of your application's UI, place it in the `src` directory and `import` it into your components. Reserve the `public` folder for specific exceptions that must escape the build process. Adopting this single practice is the #1 fix that will eliminate broken image paths from your workflow, letting you deploy with confidence every time.