A JavaScript syntax error breaks the entire script bundle — parser fails before any code runs, so all features depending on that bundle fail simultaneously. Causes range from a missing bracket to modern syntax (optional chaining, top-level await) that older browsers don't parse. The fix is build-time linting plus correct transpilation targets. This guide walks through audit, ESLint setup, Babel/SWC configuration, and the CI workflow that prevents recurrence. For related fixes, see the JS Checker Fixes index.
ESLint catches syntax errors at the editor and build level — before they reach users.
npm install --save-dev eslint @eslint/js
// Modern flat config: eslint.config.js
import js from '@eslint/js';
export default [
js.configs.recommended,
{
rules: {
// Common rules to add
'no-unused-vars': 'warn',
'no-undef': 'error',
}
}
];
// Run
npx eslint src/
For TypeScript projects, add typescript-eslint. For React, add eslint-plugin-react.
Modern JS features need transpilation for older browsers. Babel and SWC both read Browserslist config to decide what to transpile.
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
// Reads .browserslistrc automatically
useBuiltIns: 'usage',
corejs: 3
}]
]
};
// .swcrc
{
"jsc": {
"target": "es2017" // or use .browserslistrc
}
}
# .browserslistrc > 0.5% last 2 versions not dead
// SyntaxError: Unexpected token
function process(items { // missing )
return items.map(i => i * 2);
}
// Fix: complete the bracket
function process(items) {
return items.map(i => i * 2);
}
// Works in modern browsers, syntax error in Safari < 13.1 const name = user?.profile?.name; // Fix: transpile via Babel/SWC OR use logical && const name = user && user.profile && user.profile.name;
// Module scope only
const data = await fetchData(); // SyntaxError if not in module
// Fix: wrap in async IIFE
(async () => {
const data = await fetchData();
})();
// Modern syntax — needs transpilation for older browsers
class User {
#password;
setPassword(p) { this.#password = p; }
}
// Babel handles it. Verify your build targets the right browsers.
// ES2017+ — older browsers error fn(a, b, c,); // Fix: transpile OR remove trailing comma fn(a, b, c);
# GitHub Actions
name: Lint
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npx eslint src/ --max-warnings 0
Fail PRs with new ESLint errors. --max-warnings 0 also fails on warnings, useful for legacy codebases trying to clean up gradually.
Lab tools catch syntax errors against browsers you test. Production traffic has browsers you don't test. Real-user monitoring catches the gap.
// Sentry browser SDK
import * as Sentry from '@sentry/browser';
Sentry.init({
dsn: 'YOUR_DSN',
environment: 'production',
});
Sentry groups errors by stack trace, browser, OS, and frequency. Top syntax errors typically appear in the first few groups within hours of a problematic deploy.
Verify syntax errors are cleared after build and transpile fixes.
Run JS Checker →