October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
Frontend Development

How to Add Images in React JS: src, public, Remote URLs, and Fixes

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use React’s built-in <img> element: provide a src URL and meaningful alt text. For a file in your project, either import it from src or place it in Vite’s public directory and reference a root-absolute URL. For data supplied at runtime, put the JavaScript value in braces, such as src={user.imageUrl}. The sections below show each approach, when to choose it, and how to diagnose images that do not appear.

The basic React image

React renders the browser’s normal image element. A minimal component is:

export default function ParkPhoto() {
  return (
    <img
      src="/images/park.jpg"
      alt="A person walking through a park"
    />
  );
}

src is the address the browser requests. alt describes an informative image to people using screen readers and appears as a text fallback when the image cannot load. If an image is purely decorative, use alt="" rather than inventing a description.

When the dimensions are known, include width and height. The browser can reserve the correct space before the file arrives, reducing layout movement. For a below-the-fold, noncritical image, loading="lazy" can defer loading; fetchPriority="low" can also lower its loading priority. Keep important hero imagery eager unless your rendering stack says otherwise.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose where the file lives

Approach Use it when URL behavior Main trade-off
Import from src The image belongs to the application and is known when you build it The bundler returns the final public URL Stable source paths, but the production filename can be fingerprinted
Vite public directory The file should be served directly or keep a stable name Reference it with a root-absolute path such as /images/logo.png No Vite processing; a wrong path becomes a runtime 404
Prop or fetched URL The image is discovered from user data or an API Evaluate the value with braces, for example src={user.imageUrl} The browser must be able to reach the remote address

Vite is one common build-tool route for new React projects. Create React App is deprecated, so a new application should use a recommended framework or another supported setup rather than starting a new CRA project.

Method 1: import an image from src

Put the file under the component’s source tree, then import it with a static path:

import profilePhoto from './assets/profile-photo.jpg';

export default function Profile() {
  return (
    <img
      src={profilePhoto}
      alt="Profile portrait"
      width={160}
      height={160}
    />
  );
}

Vite resolves that import to a public URL. During development it may look different from the production URL, because a production build can emit a content-hashed filename. Create React App’s documented import pattern works the same way: the imported value is passed to src.

Keep the import discoverable

Use a literal, static import path so the bundler can find the file:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import hero from './assets/hero.jpg';

Do not try to construct an arbitrary import path from a variable and expect the bundler to discover every possible file. If the filename is only known after an API response, use a URL value from that response instead.

A complete Vite-style component

import productPhoto from './assets/product-photo.webp';

export default function ProductCard() {
  return (
    <article>
      <img
        src={productPhoto}
        alt="Blue ceramic travel mug"
        width={320}
        height={240}
        loading="lazy"
      />
      <h2>Travel mug</h2>
    </article>
  );
}

Method 2: serve a file from Vite’s public directory

Create this project layout:

your-app/
├─ public/
│  └─ images/
│     └─ logo.png
└─ src/
   └─ App.jsx

Reference the file from the site root. Do not include public in the URL:

export default function Logo() {
  return (
    <img
      src="/images/logo.png"
      alt="Company logo"
      width={220}
      height={48}
    />
  );
}

Vite serves public files directly, without processing them. This is useful when a filename must remain stable or direct public serving is intentional. In the legacy Create React App workflow, public files are likewise not post-processed or content-hashed; a typo is therefore a runtime 404, which is why imports remain the normal recommendation for application assets.

Routes and leading slashes

A root-absolute path such as /images/logo.png starts at the site origin. A path such as images/logo.png is relative to the current document URL and can break when the component is rendered on a nested route. If you intentionally need a route-relative URL, verify the resulting address in the browser’s Network panel; otherwise prefer the leading slash for a Vite public asset.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Method 3: render a remote or data-backed image

When a URL comes from props or fetched data, put the JavaScript expression in braces. Quotes create a literal string; braces evaluate a value:

function Avatar({ user }) {
  return (
    <img
      src={user.imageUrl}
      alt={user.name}
      width={96}
      height={96}
    />
  );
}

A parent can pass the data like this:

export default function Account() {
  const user = {
    name: 'Mina Patel',
    imageUrl: 'https://images.example.com/mina.jpg'
  };

  return <Avatar user={user} />;
}

For a remote image, check that the address is reachable by the browser and that it is the image you intend to expose. Framework-specific image components may add optimization or preload behavior and can use different defaults; follow that framework’s documentation when you replace the native element.

Handle a missing URL explicitly

Do not send an undefined value to src. Render a fallback or omit the image until data exists:

function UserAvatar({ user }) {
  if (!user?.imageUrl) {
    return <span aria-label="No profile image">—</span>;
  }

  return (
    <img
      src={user.imageUrl}
      alt={`${user.name} profile portrait`}
      width={96}
      height={96}
    />
  );
}

Accessibility and loading decisions

  • Describe purpose, not decoration: write concise alternative text that conveys what an informative image contributes. Use alt="" for a purely decorative image.
  • Reserve space: provide numeric width and height when known.
  • Lazy-load selectively: use loading="lazy" for below-the-fold, noncritical images. Do not automatically lazy-load the primary hero image.
  • Lower noncritical priority when appropriate: fetchPriority="low" tells the browser the request is less urgent.
  • Use the correct attribute name: it is src, never scr.

These choices affect both users who rely on alternative text and users on slower connections. They do not change where the image file is stored; choose storage and loading behavior independently.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why an image is not showing

The page shows alt text or a broken-image icon

  • Open the browser’s Network panel and inspect the image request. A 404 usually means the URL does not match the file’s served path.
  • For a Vite public file, remove public/ from the URL: /images/logo.png, not /public/images/logo.png.
  • For an imported file, verify the import is relative to the component file and that capitalization matches the filename.
  • For a remote value, copy the resolved URL from the element and open it directly in a new tab. Confirm that the data is not empty or undefined.

The image works on the home page but not another route

A relative URL changes meaning when the document path changes. Use a root-absolute public URL or import the asset from src. The Network panel shows the exact address the browser requested, which makes this error straightforward to distinguish from a missing file.

The imported file works in development but the URL looks different in production

That is expected: the production build can emit a hashed filename. Use the imported variable rather than hard-coding the development URL, so the bundler can substitute the correct production address.

The dynamic image is blank

Log or inspect the value supplied to src. Make sure you use braces—src={user.imageUrl}—rather than the literal text src="user.imageUrl". Also verify that the browser can reach the returned URL and that your fallback handles a missing value.

The layout jumps while images load

Add the known intrinsic width and height. Without dimensions, the browser cannot reserve the image’s space before the response arrives.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A new project starts with an outdated setup

Create React App is deprecated. Start with a recommended React framework or a supported tool such as Vite, then apply the import or public-directory patterns above.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance and reliability checklist

  1. Decide whether the asset is build-time application content, a stable public file, or runtime data.
  2. Use an import for bundled application assets; use public only when direct serving or a stable name is intentional.
  3. Set width and height whenever the dimensions are known.
  4. Mark below-the-fold, noncritical images with loading="lazy"; keep critical imagery eager.
  5. Use a concise, useful alt value, or an empty value for decoration.
  6. For data-backed images, validate the URL before rendering and provide a visible fallback.
  7. When something fails, inspect the final request URL and status in the browser’s Network panel before changing code.

Capture a rendered React page without setting up a browser

If your goal is to create a screenshot of a deployed React route for documentation, QA, or a report, ScreenshotNeo is a website screenshot API and MCP server for developers. It accepts a URL and returns a PNG, JPEG, WebP, or PDF. Its cleanup step accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers.

Or skip the browser setup

Make one request (the complete option list and authentication details are in the ScreenshotNeo API documentation):

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

For a React page, replace the example URL with the route you want to capture. ScreenshotNeo supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus arbitrary viewports, retina scale, PDF paper size and page ranges, custom CSS and JavaScript, clicks before capture, selector waits, delays or network-idle waits, request and resource blocking, custom headers, cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed public-image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work to ease migration. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Plan Included shots Price
Free 1,000 per month No card required
Starter 3,000 $5
Growth 15,000 $15
Pro 60,000 $39
Scale 250,000 $99
Business 1,000,000 $249

Every feature is included on every plan, and yearly billing provides two months free. Start with 1,000 free screenshots a month with no card; paid plans start at $5 for 3,000.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.