Four Things I Forget About Create-React-App Every Year

Cheat sheet for the CRA quirks that keep coming back. Absolute imports via jsconfig, the HTTPS dev flag, the registerServiceWorker mystery, and the REACT_APP_ env var rules.

JavaScript
Frontend
4 snippets
react
design-patterns
references
yunatorres

By @yunatorres

March 29, 2026

·

Updated August 12, 2026

311 views

6

4.3 (13)

// CRA respects a jsconfig.json (or tsconfig.json) at the project root with
// compilerOptions.baseUrl = "src". With it, every import path is rooted at
// src/, so deep components can write `import Button from 'components/Button'`
// instead of `import Button from '../../../components/Button'`.
//
// The config lives next to package.json. CRA reads it on dev-server start;
// changing it requires a server restart.

const jsconfig = {
    compilerOptions: {
        baseUrl: 'src',
    },
    include: ['src'],
};
console.log('jsconfig.json contents:');
console.log(JSON.stringify(jsconfig, null, 2));

// Before. Imagine src/pages/dashboard/widgets/SalesWidget.js:
const before = [
    "import Button from '../../../components/Button';",
    "import { fetchSales } from '../../../api/sales';",
    "import { formatMoney } from '../../../utils/format';",
];
console.log('\
BEFORE (relative paths from a deep file):');
before.forEach((line) => console.log('  ' + line));

// After. Same file, with baseUrl: 'src' set.
const after = [
    "import Button from 'components/Button';",
    "import { fetchSales } from 'api/sales';",
    "import { formatMoney } from 'utils/format';",
];
console.log('\
AFTER (absolute from src/):');
after.forEach((line) => console.log('  ' + line));

// Gotchas I have hit twice each.
console.log('\
Gotchas:');
console.log('  - paths option (compilerOptions.paths) does NOT work in CRA.');
console.log('    Only baseUrl. For path aliases you need react-app-rewired or eject.');
console.log('  - jsconfig.json must be at project root, not under src/.');
console.log('  - the dev server caches the resolution; restart yarn start after editing.');
console.log('  - VS Code uses jsconfig too. Once it picks up baseUrl, autoimport stops');
console.log('    inserting the relative paths and starts inserting the absolute ones.');

// TypeScript variant. The same compilerOptions live in tsconfig.json.
const tsconfig = { compilerOptions: { baseUrl: 'src', target: 'es6', jsx: 'react-jsx' } };
console.log('\
TS variant tsconfig.json:');
console.log(JSON.stringify(tsconfig, null, 2));

Two minutes of config that ages well. The whole behaviour is one line: compilerOptions.baseUrl: 'src' in jsconfig.json (or tsconfig.json for TS projects), and CRA picks it up on the next dev-server start. Every import becomes rootable from src/, so a file three levels deep does not have to count ../../../. The two gotchas worth flagging are that the paths field for arbitrary aliases is not supported by CRA without ejecting or react-app-rewired, and that VS Code uses the same jsconfig.json for autoimport, so once you add it your tooling stops suggesting relative paths and quietly switches to the absolute ones. Modern bundlers (Vite, esbuild) handle this with resolve.alias; CRA's path is the JSON file.