How Image and Asset Loading Works in Create React App
Create React App uses webpack to automatically process imported assets from the src/ directory into optimized, cache-friendly URLs while providing a public/ folder escape hatch for static files that bypass the build system entirely.
Image and asset loading in Create React App (CRA) is handled by an integrated webpack configuration that transforms imported files into URLs your application can reference at runtime. According to the facebook/create-react-app source code, the build system distinguishes between two distinct asset pathways: files imported from JavaScript or CSS in the src/ folder, which are processed through webpack loaders, and files placed in the public/ folder, which are copied verbatim to the output directory.
The Two Asset Pathways in Create React App
CRA implements a dual-path architecture for handling static assets:
src/ imports (webpack-processed): When you import an image, font, or video from within your JavaScript or CSS, webpack applies asset module rules defined in packages/react-scripts/config/webpack.config.js. These files receive content hashing, potential inlining for small assets, and are emitted to static/media/.
public/ folder (static copy): Files placed in the public/ directory bypass webpack entirely. They are copied unchanged to the build output and accessed via the %PUBLIC_URL% placeholder or process.env.PUBLIC_URL. This approach is useful for files that need to retain exact names, such as manifest.json or robots.txt.
Importing Assets from the src Directory
Webpack Asset Module Configuration
In packages/react-scripts/config/webpack.config.js, CRA configures webpack's asset modules to handle standard image formats. The configuration uses type: 'asset' for files matching /\.(bmp|gif|jpe?g|png)$/i:
// From webpack.config.js (simplified)
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: imageInlineSizeLimit,
},
},
}
This rule instructs webpack to automatically determine whether to inline the asset as a base64 data URL or emit it as a separate file based on the imageInlineSizeLimit threshold.
Inline vs Emitted Files: The 10KB Threshold
CRA sets a default imageInlineSizeLimit of 10,000 bytes (10 KB) in packages/react-scripts/config/webpack.config.js. This threshold determines asset handling:
Files ≤ 10 KB: Webpack inlines the asset as a base64-encoded data URL directly into your JavaScript bundle. This eliminates an HTTP request but increases bundle size.
Files > 10 KB: Webpack treats the asset as asset/resource, emitting it to the build output with a content hash for cache busting. The import resolves to a URL like static/media/logo.3d5f1c.png.
The output filename pattern is configured via output.assetModuleFilename:
// webpack.config.js
output: {
assetModuleFilename: 'static/media/[name].[hash][ext]',
}
SVG Special Handling with SVGR
SVG files receive unique treatment in CRA. The webpack configuration in packages/react-scripts/config/webpack.config.js processes SVGs through two loaders:
@svgr/webpack: Converts the SVG into a React component that you can import as a named exportfile-loader: Emits the SVG file tostatic/media/and provides the URL as the default export
This dual export pattern allows flexible usage:
import { ReactComponent as Logo } from './logo.svg';
import logoUrl from './logo.svg';
// Use as React component
<Logo width={120} height={30} title="My Logo" />
// Use as image URL
<img src={logoUrl} alt="Logo" />
Using the Public Folder for Static Assets
The public/ folder serves as an escape hatch for files that must retain exact filenames or exist outside the module system. According to docusaurus/docs/using-the-public-folder.md, files in this directory are copied verbatim to the build output without processing.
Referencing Public Assets in HTML
Use the %PUBLIC_URL% placeholder in index.html or other HTML files:
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
During the build, CRA replaces %PUBLIC_URL% with the appropriate absolute or relative path based on your homepage configuration.
Referencing Public Assets in JavaScript
Access the public folder path at runtime using process.env.PUBLIC_URL:
function Footer() {
return (
<img
src={process.env.PUBLIC_URL + '/images/footer-logo.png'}
alt="Footer logo"
/>
);
}
Important limitation: Files in public/ do not receive content hashing. You must manually implement cache busting (e.g., versioned filenames like logo-v2.png) when updating these assets.
Asset Loading in CSS and SCSS Files
When you reference assets from CSS, Sass, or Less files, the css-loader resolves url() statements as module dependencies. This means the same webpack asset rules apply to images referenced in stylesheets.
In packages/react-scripts/config/webpack.config.js, the style loader chain includes css-loader configured to handle asset URLs:
/* src/App.css */
.hero {
/* This resolves through webpack and emits to static/media/ */
background-image: url('./assets/hero-bg.jpg');
}
The asset is processed according to the 10 KB threshold: small images inline as data URLs, larger ones emit to static/media/ with content hashes.
Key Configuration Files and Implementation Details
The asset handling system in CRA is defined across several configuration files in packages/react-scripts/config/:
| File | Purpose | Asset-Related Configuration |
|---|---|---|
webpack.config.js |
Core webpack configuration | Asset module rules (lines 80-102), imageInlineSizeLimit (lines 64-66), output.assetModuleFilename (lines 226-229), SVGR loader configuration |
paths.js |
Filesystem path resolution | Defines appPublic, appBuild, and publicUrlOrPath used for output locations |
env.js |
Environment variable injection | Handles PUBLIC_URL and process.env.PUBLIC_URL resolution |
babel-plugin-named-asset-import |
Babel plugin | Enables the import img from './file.png' syntax by transforming imports |
The babel-plugin-named-asset-import package specifically enables the ergonomic import syntax by ensuring that default imports from asset files resolve to their URLs or components.
Summary
- Webpack-powered imports: Files imported from
src/are processed through asset modules inpackages/react-scripts/config/webpack.config.js, with automatic inlining for assets under 10 KB and hashed filenames for larger files instatic/media/. - Dual-path SVG support: SVG files are processed by both
@svgr/webpack(for React component imports) andfile-loader(for URL imports), enabling flexible usage patterns. - Public folder escape hatch: The
public/directory bypasses webpack entirely; files are copied verbatim and accessed via%PUBLIC_URL%orprocess.env.PUBLIC_URLwithout content hashing. - CSS integration: The
css-loaderresolvesurl()references in stylesheets through the same asset pipeline, ensuring consistent hashing and optimization across JavaScript and CSS. - Configuration architecture: Asset handling is centralized in
webpack.config.jswith supporting logic inpaths.js,env.js, andbabel-plugin-named-asset-import.
Frequently Asked Questions
What is the file size limit for inlining images in Create React App?
Create React App uses a 10 KB (10,000 bytes) threshold defined by imageInlineSizeLimit in packages/react-scripts/config/webpack.config.js. Images imported from src/ that are smaller than this limit are automatically converted to base64 data URLs and inlined into your JavaScript bundle, while larger files are emitted to static/media/ with content hashes for optimal caching.
How do I import an SVG as a React component in CRA?
You can import SVG files as React components using the named export syntax provided by @svgr/webpack. Write import { ReactComponent as Logo } from './logo.svg'; to receive a ready-to-use React component that accepts props like width, height, and title. You can simultaneously import the same file as a URL using the default import syntax if you need both the component and the file reference.
What is the difference between putting assets in src versus public?
Assets placed in src/ are processed by webpack through the asset module system defined in packages/react-scripts/config/webpack.config.js, receiving benefits like automatic optimization, content hashing for cache busting, and potential inlining for small files. Assets in public/ bypass webpack entirely and are copied verbatim to the build output; they retain original filenames but must be referenced using %PUBLIC_URL% or process.env.PUBLIC_URL, and you must manually handle cache invalidation when updating them.
How does Create React App handle assets referenced in CSS files?
When you use url() in CSS, SCSS, or Less files, the css-loader resolves these references as module dependencies and passes them through the same webpack asset pipeline used for JavaScript imports. This means images referenced from stylesheets are subject to the same 10 KB inlining threshold and content hashing rules, emitting to static/media/ when they exceed the size limit, ensuring consistent optimization across your entire application.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →