Source Maps
Date: 2026-08-17
A mapping from generated code back to the source that produced it, so a stack trace names your file and line rather than
app.a3f9c.js:1:48201. The only real decision is whether to expose them publicly, and it’s a smaller security question than it’s usually treated as.
A source map is a JSON file relating positions in generated output to positions in original source, letting debuggers and error reporters show the code you wrote.
What it contains
{
"version": 3,
"sources": ["src/basket.ts", "src/price.ts"],
"sourcesContent": ["export function ...", "..."],
"names": ["calculateTotal", "items"],
"mappings": "AAAA,SAASA,eAAeC..."
}mappings— the position data, in a compact encodingsourcesContent— the complete original source, embedded. This is the part with the privacy implicationnames— original identifiers, so minifiedtbecomestotalagain
Without one
TypeError: Cannot read properties of undefined
at t (app.a3f9c2e1.js:1:48201)
Effectively undebuggable. With a map:
TypeError: Cannot read properties of undefined
at calculateTotal (src/basket.ts:42:18)
This is the entire value, and it applies to production error reporting far more than to local debugging — locally you have the source anyway.
The modes
source-map separate .map file,
referenced in a comment
→ browsers fetch it when
DevTools is open
hidden-source-map generated, but NO
reference comment
→ upload to your error
reporter; the public
never fetches it
inline-source-map base64'd into the file
→ huge. Development only
eval-source-map fastest rebuilds
→ development only
false none
hidden is the right production answer for most teams. You get readable stack traces in your error reporter, and the map isn’t served publicly.
The exposure question, honestly
A public source map reveals your source code. How much that matters depends on what’s in it:
- Client-side JavaScript is already public. Minified code is inconvenient to read, not secret. Anyone motivated can un-minify it
- What a map genuinely adds is comments, original variable names, file structure and dead code paths — which is convenience for a reader, not new capability
- The real risk is what shouldn’t be in client code at all — a hard-coded key, an internal endpoint, a comment describing an unreleased feature. Those are the actual problem, and the map just makes them easier to find — Secrets Management
So the honest position: hidden maps are the sensible default, and if a public map would expose something dangerous, you have a bigger problem than the map.
Uploading to an error reporter
# generate without a public reference
vite build # sourcemap: 'hidden'
# upload, then delete before deploying
sentry-cli sourcemaps upload ./dist
rm ./dist/**/*.mapThe release identifier must match between the uploaded map and the deployed code, or the reporter can’t pair them — and a mismatched release is the usual reason “we uploaded source maps and traces are still minified”.
Where they break
- Not uploaded on deploy. Silently minified traces for weeks before anyone notices
- Stale maps from a previous release, producing confidently wrong line numbers — worse than none
- Chained transformations — TypeScript → transpiler → bundler → minifier, where any step not forwarding the incoming map loses the chain and points at intermediate code
- Third-party scripts have no maps, so their errors stay opaque — one reason a global error handler is full of unattributable noise — Third-Party Scripts
- Served with the wrong content type, or blocked by a CDN rule matching
.map
CSS too
Often forgotten, and useful for the same reason:
generated .css-1x2y3z { padding: 8px }
mapped src/Button.module.scss:14
Worth enabling in development, where “which file produced this rule” is a real question in any component-scoped CSS setup.
The practical setup
DEVELOPMENT full source maps, fastest
variant
PRODUCTION hidden-source-map
→ upload to the error reporter
→ delete before deploying
→ verify the release ID matches
And check it works. Throw a deliberate error in a staging deploy and confirm the trace is readable — this is a thing that silently stops working and is only discovered during an incident.