How to Fix Heroku’s CSS Loader Error (‘Can’t Resolve ./app.css’) in React

When I first deployed my Create React App to Heroku, I thought everything would “just work.” Instead, I was greeted with this frustrating error:

Module not found: Error: Can't resolve './app.css' in '/tmp/build_xxx/src'

At first glance, it looked like a silly problem surely it was just a missing file. But the more I dug, the more I realized this issue is a combination of Linux case sensitivity, Git commits, and my own mistake of trying to override Create React App (CRA) with a custom Webpack config.

Let me walk you through exactly what went wrong, how I fixed it, and how I ended up with a cleaner, more scalable CSS setup.

The First Error I Saw in My Project

Here’s the minimal code that triggered the error.

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './app.css'; // CRA will crash if this file is missing or misnamed

function App() {
  return <h1>Hello Heroku</h1>;
}

ReactDOM.render(<App />, document.getElementById('root'));

src/app.css

h1 {
  font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, "Helvetica Neue", Arial, "Noto Sans";
  font-size: 2rem;
}

On my Mac, everything worked fine. But Heroku builds on Linux, which is case-sensitive. That means App.css and app.css are two different files. If you get the casing wrong in your import, Heroku can’t find it—even if it worked locally.

Why This Error Happens on Heroku

Here’s what I learned:

  1. Linux cares about casing
    App.cssapp.css. On macOS/Windows, it doesn’t matter. On Heroku, it does.
  2. CRA ignores custom webpack.config.js
    I added a webpack.config.js and installed css-loader—but CRA uses its own config. My changes did nothing.
  3. Git is the source of truth
    If I created a file locally but never committed it (git add + git commit), Heroku simply never saw it.

What Was Wrong in My Setup

I had:

  • Installed css-loader in devDependencies.
  • Added a webpack.config.js that CRA never read.
  • An import mismatch: I thought I had app.css, but the file was actually named App.css.

Heroku didn’t care about my local setup—it just failed with Can't resolve './app.css'.

Step by Step Fix I Applied

Here’s the exact process I followed to fix it:

  1. Remove custom Webpack and css-loader rm webpack.config.js yarn remove css-loader
  2. Match the filename and import exactly
    • If I write import './app.css';, the file must be exactly src/app.css.
  3. Commit the CSS file to Git git add -A git commit -m "Fix CSS path/case and remove custom webpack" git push heroku main
  4. Optional: Clear Heroku cache if needed heroku plugins:install heroku-builds heroku builds:cache:purge -a <my-app-name> git push heroku main

After that, the app built successfully and my styles loaded perfectly on Heroku.

Leveling Up: Better CSS Practices in CRA

Once I fixed the basics, I took the opportunity to clean up my CSS setup without touching Webpack.

Add Normalize.css

I already had normalize.css installed, so I imported it in src/index.js:

import 'normalize.css';   // reset browser styles
import './app.css';       // then load my own styles

Use CSS Modules

CRA supports CSS Modules automatically for *.module.css files.

src/Button.module.css

.button {
  padding: 0.75rem 1rem;
  border-radius: 9999px;
  border: none;
  cursor: pointer;
}
.primary {
  background: #111827;
  color: #fff;
}

src/components/Button.js

import React from 'react';
import styles from '../Button.module.css';

export default function Button({ children, variant = 'primary' }) {
  return (
    <button className={`${styles.button} ${styles[variant]}`}>
      {children}
    </button>
  );
}

Now I had scoped, conflict-free styles.

Add Global Variables

src/styles/vars.css

:root {
  --brand: #4f46e5;
  --text: #111827;
  --muted: #6b7280;
}

src/app.css

@import './styles/vars.css';

body {
  color: var(--text);
}
a {
  color: var(--brand);
}

Split CSS by Purpose

I separated base, layout, and utilities:

  • styles/base.css → resets & defaults
  • styles/layout.css → containers, grids
  • styles/util.css → margin/padding helpers

Then I imported them all in index.js.

The Final Working Example

Here’s what my final cleaned-up project looked like.

package.json (no css-loader, no webpack.config.js)

"dependencies": {
  "normalize.css": "^8.0.1",
  "react": "^17.0.2",
  "react-dom": "^17.0.2",
  "react-scripts": "5.0.0"
}

src/index.js

import 'normalize.css';
import './app.css';

import React from 'react';
import ReactDOM from 'react-dom';

function App() {
  return (
    <main className="container">
      <h1>Heroku + CRA CSS works </h1>
    </main>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

src/app.css

.container {
  max-width: 720px;
  margin: 2rem auto;
  padding: 1rem;
}
h1 {
  font-size: 2rem;
  line-height: 1.2;
}

And finally deployment worked perfectly.

Final Thought

This whole experience taught me a simple but important lesson: don’t fight CRA’s defaults unless you absolutely have to. The error wasn’t about loaders or Webpack tweaks it was just about path casing and file management.

Once I stopped over-engineering and trusted the defaults, things worked exactly as intended. On top of that, I took the opportunity to clean up my CSS setup with Normalize, CSS Modules, and a structured file system.

So if you’re stuck with Heroku’s Can’t resolve './app.css' error check your filenames, commit your files, and keep it simple. Chances are, the fix is easier than you think.

Related blog posts