# Importing CSS and Images in React: A 2019 Guide

> Published 2026-10-26T00:41:55.896Z on https://youblog.app/p/importing-css-and-images-in-react-a-2019-guide/
> Source video: https://www.youtube.com/watch?v=aRh81Z3YklE

Importing CSS and images in React with webpack means adding two loader rules: style-loader and css-loader for CSS, and url-loader or the webpack 5 asset modules for images. A 2019 tutorial by Dev doido demonstrated exactly that setup, and the same configuration still explains why an image import returns a URL string instead of a file.

## Importing CSS and Images in React: What the Build Actually Does

Importing CSS and images in React works because the bundler rewrites each import into something JavaScript can use. Webpack sends a `.css` import through style-loader and css-loader, and an image import through a file rule that returns a URL string. Dev doido demonstrated this pipeline in a 2019 tutorial on his channel for the React course [from zero to infinity](https://www.youtube.com/watch?v=dQw4w9WgXcQ).

The transcript's central lesson is worth separating from the tooling. React itself does not understand CSS or PNG files. Every import of a non-JavaScript asset is a request to the build, and the build answers with a rule you wrote or a default someone else wrote.

That explains the sequence in the video: a component rendered, then a stylesheet turned the page black, then an image appeared, and only then did JSX break. Each step depended on a different piece of configuration.

Webpack is a module bundler that walks a dependency graph starting from an entry file. Loaders tell it how to transform each file type before the result reaches the browser bundle.

## Why Did the JSX Syntax Fail in the 2019 Build?

The JSX error came from the Babel configuration, not the React component. Babel transforms JSX into function calls, and the 2019 config needed the JSX plugin explicitly registered so the parser accepted the syntax. The transcript records the builder noticing a missing plugin, then adding it to the Babel config file.

This is the failure that looks most like a React bug and is least like one. The component was fine. The bundler simply reached a syntax it had not been told to expect, and the build stopped.

Babel is a JavaScript compiler that rewrites modern and non-standard syntax into code browsers or bundlers can process. Its configuration lived in a separate file in that project, which is why the error message pointed at parsing rather than at the component tree.

## Which Loaders Handle CSS Imports?

Two loaders handle a CSS import: css-loader reads the file and resolves any `url()` references inside it, and style-loader injects the resulting rules into the page at runtime. The transcript names both and shows them applied as one rule matching every `.css` file.

The division of labor matters when something breaks. If an image referenced from inside a stylesheet fails to resolve, the problem sits in css-loader or in the file rule. If the styles never appear at all, style-loader is the suspect.

Webpack applies loaders from right to left in the array, so the css-loader entry must come after style-loader in source order. Getting that order wrong produces either a string of raw CSS or a silent no-op depending on the setup.

The 2019 demonstration ended with a black background applied through an imported stylesheet. That is a trivial outcome, and it is the correct one: it proves the chain from import statement to rendered page is intact.

## How Do Image Imports Return a URL?

An image import returns a URL string because a file loader copies the asset into the output directory and hands back its public path. In the 2019 video the rule matched gif, png and jpg with a case-insensitive pattern and the component used that URL as an `img` source.

The case-insensitive flag is a small detail with practical value. Files saved as `.PNG` from a design tool otherwise slip past the rule and fail at build time with an error that reads like a missing module.

The same rule pattern still exists in webpack, though the loaders have changed. Modern webpack configuration usually reaches for asset modules instead of the older file and url loaders.

## Webpack Loaders Then, Asset Modules and Vite Now

The 2019 setup used url-loader for images and two loaders for CSS, and the current webpack documentation instead describes asset modules as the built-in way to handle images and fonts. The imports in the component look the same; the rule written in the config does not.

This is the part of the tutorial that aged. Someone following a 2019 article today will install loaders webpack no longer requires for images, and will find plugin advice that conflicts with current Babel presets.

Webpack is still maintained and still documents loaders for CSS, as the [webpack style-loader documentation](https://webpack.js.org/loaders/style-loader/) shows. The older url-loader and file-loader packages remain published but are superseded for most image use by asset modules.

Vite, a build tool that serves source files natively during development, keeps the import syntax and drops the loader file. Its [static asset handling documentation](https://vite.dev/guide/assets) describes image imports returning resolved URLs and CSS imports becoming injected styles, which is the same contract the loaders provided.

## What the Tutorial Got Right About Components

The component structure in the tutorial holds up: a class component holds state, renders a list, and is imported into a root file that mounts it to a DOM node. That pattern survived every framework change since 2019, even as class components gave way to function components with hooks.

React, the UI library maintained by Meta, introduced hooks in version 16.8 in February 2019. The tutorial was published in September 2019 and used a class component with state, which was already a choice rather than a requirement by then.

The mounting step used `ReactDOM.render`, which the transcript shows applied to a div created in the HTML file. React 18, released in March 2022, replaced that API with `createRoot` from the `react-dom/client` entry point, and the [React 18 upgrade guide](https://react.dev/blog/2022/03/08/react-18-upgrade-guide) documents the migration.

None of that invalidates the lesson. Importing a stylesheet or an image is a build concern, and build concerns outlive component APIs.

## Does This 2019 Setup Still Work in 2026?

The loader approach still works in webpack with adjustments, but the tutorial's exact steps are not a good template in 2026. Anyone starting a React project now gets Vite-based tooling from the official templates, where CSS and image imports need no configuration at all.

For a reader maintaining an older webpack project, the useful parts are the rule structure and the loader order. Swap url-loader for asset modules, replace the manual Babel plugin with a preset, and the configuration translates.

For a reader learning React today, the more useful exercise is writing the same component against Vite and noticing that the imports are identical while the config file is absent. That contrast teaches the boundary between React and its tooling faster than any loader explanation.

Dev doido's channel continued with React content after this episode, and the ecosystem he teaches in now, including TypeScript-heavy stacks documented at [Crazystack](https://crazystack.com.br), treats bundler configuration as something a starter template provides rather than something a learner assembles.

## FAQ

- **What do css-loader and style-loader each do?** css-loader reads a CSS file and resolves the URLs inside it. style-loader takes the resulting CSS string and injects it into the page at runtime. Webpack applies them right to left, so css-loader is listed after style-loader in the rule.

- **Why does an image import return a string instead of a file?** A file rule or asset module copies the image into the build output and returns the public path to that copy. The component receives a URL it can assign to an `img` element's source, not the binary data itself.

- **Did React change how imports work since 2019?** No. React never handled CSS or image imports; the bundler did, and still does. What changed is the mounting API, where `ReactDOM.render` was replaced by `createRoot` in React 18 during 2022.

- **Do I need webpack configuration if I use Vite?** Usually not. Vite resolves image imports to URLs and injects imported CSS without a loader file. A separate configuration is needed only when you want to change the default handling, such as inlining small assets below a size limit.

- **Was the JSX parse error in the tutorial a React bug?** It was a Babel configuration gap. The parser had not been told to accept JSX syntax, so the build stopped before React code ran. Registering the JSX plugin resolved it, and current setups use a preset that includes it.

## Turning a Tutorial Into a Written Guide

A build error that took eleven minutes to explain on video can become a page a reader finds through a search, and the configuration steps survive better in text than in a spoken walkthrough. If you have explained something on YouTube, that explanation already exists in a form worth publishing.

[Skala Blog](https://skalablog.com) takes a YouTube URL, transcribes the video, and generates an article draft you can review before publishing. The same reasoning that fixed a loader rule in 2019 can fix a reader's build in 2026.

[Source video](https://www.youtube.com/watch?v=aRh81Z3YklE)
