| | |

esbuild 11 ⚡ Source Maps — –sourcemap for debugging output

⏱️ Estimated read: 11 min | |

esbuild’s --sourcemap option generates a source map that connects the minified or bundled output back to the original source files. When a browser or a debugging tool loads the output, the source map lets the developer set breakpoints in the original TypeScript or JavaScript files and step through the code as it was written, even though the browser is executing the transformed output . The option is a debugging tool, not a production optimization. It adds a .map file (or an inline data URL) and a //# sourceMappingURL comment to the output, and it should be configured deliberately based on whether the output is for a developer’s browser or for a production deployment.

This chapter covers the three source map modes esbuild supports (external, inline, and both), the sourcesContent field that determines whether the original source is embedded in the map, and the security and deployment considerations that determine which mode is appropriate.

Key point: The external mode writes the .map file but omits the //# sourceMappingURL comment, which is the “hidden” source map pattern. The inline mode embeds the map as a data URL in the output. The both mode does both. The choice depends on whether the browser should discover the map automatically or the developer should attach it manually.


Why source maps matter for esbuild output

The minification problem. esbuild’s --minify option reduces the output to a fraction of its original size. The result is unreadable: variable names are single characters, line breaks are removed, and the stack trace points to a column in a single-line file . A production error that reports TypeError: Cannot read properties of undefined (reading 'id') at e.js:1:48213 is not a debugging aid. The source map is what translates that position back to the original file, line, and column .

The transformation problem. Even without minification, esbuild transforms the source: it bundles modules, strips TypeScript types, and converts JSX. The output’s line numbers do not match the source’s line numbers. The source map is the mapping between the two .

The browser problem. A browser’s developer tools read the //# sourceMappingURL comment at the end of the output file. When the comment is present, the browser fetches the .map file and uses it to display the original sources in the Sources panel . When the comment is absent, the browser does not know the map exists, and the developer must attach it manually in the developer tools .


a. The external mode

The external mode writes the .map file next to the output but omits the //# sourceMappingURL comment. This is the mode that the documentation describes as the way to “omit the special //# sourceMappingURL= comment from the generated .js file but you still want to generate the .js.map files” .

# CLI
esbuild src/app.ts --bundle --minify --sourcemap=external --outfile=dist/app.js

The output directory contains app.js and app.js.map. The app.js does not have the trailing comment. A browser that loads app.js does not automatically fetch the map. The map is available for manual attachment in the developer tools, or for upload to an error monitoring service that symbolicates stack traces on the server side .

This is the mode for production builds. The map is generated and archived for debugging, but it is not served to the public. The sourcesContent field (enabled by default) embeds the original source in the map, which means the map file must be protected as carefully as the source code . The production deployment moves the .map files out of the public directory and stores them in a private artifact store .


b. The inline mode

The inline mode embeds the entire source map as a base64 data URL at the end of the output file. There is no separate .map file. The documentation describes it as inserting “the entire source map into the .js file instead of generating a separate .js.map file” .

# CLI
esbuild src/app.ts --bundle --sourcemap=inline --outfile=dist/app.js

The output is a single file that contains the code and the map. The browser finds the map in the data URL and displays the original sources without a separate network request. This is convenient for a development build or a debugging session where the number of files matters.

The cost is size. The inline map increases the output file by the size of the map, which is often comparable to the size of the code. It also embeds the original source, which means the output file cannot be shared publicly without exposing the source. The inline mode is not appropriate for a production deployment.


c. The both mode

The both mode does what the name suggests: it writes the separate .map file and embeds the map as an inline data URL in the output. The documentation describes it as having “the effect of both inline and external simultaneously” .

# CLI
esbuild src/app.ts --bundle --sourcemap=both --outfile=dist/app.js

The output directory contains app.js and app.js.map, and app.js ends with an inline data URL. The browser uses the inline map, and the separate map file is available for archiving or upload. This mode is useful when the build serves both a development environment (where the inline map is convenient) and a production pipeline (where the separate map is archived). It is rarely the right choice for a single-purpose build.


Complete Example Session

# ============================================
# PART 1: THE BASIC SOURCE MAP
# ============================================
esbuild src/app.ts --bundle --sourcemap --outfile=dist/app.js

# Default mode: external.
# Output: dist/app.js and dist/app.js.map.
# The app.js has the //# sourceMappingURL comment.
# ============================================
# PART 2: THE EXTERNAL MODE (NO COMMENT)
# ============================================
esbuild src/app.ts --bundle --minify --sourcemap=external --outfile=dist/app.js

# Output: dist/app.js and dist/app.js.map.
# app.js does NOT have the comment.
# The browser will not auto-fetch the map.
# ============================================
# PART 3: THE INLINE MODE
# ============================================
esbuild src/app.ts --bundle --sourcemap=inline --outfile=dist/app.js

# Output: dist/app.js only.
# The map is a data URL at the end of the file.
# ============================================
# PART 4: THE BOTH MODE
# ============================================
esbuild src/app.ts --bundle --sourcemap=both --outfile=dist/app.js

# Output: dist/app.js and dist/app.js.map.
# app.js has the inline data URL.
# ============================================
# PART 5: THE SOURCES CONTENT FIELD
# ============================================
# The .map file embeds the original source by default.
# Set sourcesContent: false to exclude it.
// ============================================
// PART 6: THE JAVASCRIPT API
// ============================================
await esbuild.build({
  entryPoints: ['src/app.ts'],
  bundle: true,
  minify: true,
  sourcemap: 'external',
  outfile: 'dist/app.js',
})
// ============================================
// PART 7: THE SOURCES CONTENT OPTION
// ============================================
await esbuild.build({
  sourcemap: 'external',
  sourcesContent: false,
  outfile: 'dist/app.js',
})

// The map does not embed the original source.
// It must be served alongside the source files.
// ============================================
// PART 8: THE PRODUCTION DEPLOYMENT
// ============================================
# 1. Build with sourcemap: 'external'.
# 2. Move the .map files to a private store.
# 3. Deploy the .js files without the maps.
# 4. Upload the maps to the error monitor.
// ============================================
// PART 9: THE MANUAL ATTACHMENT
// ============================================
# In Chrome DevTools:
# Sources → open the minified file
# Right-click → Add source map…
# Enter the URL or local path to the .map file.
// ============================================
// PART 10: THE DECISION
// ============================================
# Development → inline or external with comment
# Production → external without comment
# Error monitoring → external, archived

The ten parts covered the basic source map, the external mode, the inline mode, the both mode, the sourcesContent field, the JavaScript API, the sourcesContent option, the production deployment, the manual attachment, and the decision.


Quick Reference

Source map modes

Mode.map file//# sourceMappingURLUse case
externalYesNoProduction, archived maps
inlineNoYes (data URL)Development, single file
bothYesYes (data URL)Dual-purpose builds
Default (no value)YesYesDevelopment, auto-discovery

Related options

OptionDefaultPurpose
sourcemapfalseEnable source map generation
sourcesContenttrueEmbed original source in the map
sourceRoot—Set the source root path
sourcefile—Override the source filename

The production pattern

StepAction
1Build with sourcemap: 'external'
2Move .map files out of the public directory
3Deploy the .js files
4Upload the maps to the error monitor
5Attach manually in DevTools for debugging

The debugging flow

StepAction
1Open DevTools → Sources
2Find the minified file
3Right-click → Add source map
4Enter the map URL
5Set breakpoints in the original source

Best Practices

✅ Do This:

# Use external mode for production
esbuild src/app.ts --bundle --minify --sourcemap=external --outfile=dist/app.js # ✅
# Archive the maps for error monitoring
mv dist/*.map /private/maps/                                  # ✅
# Use inline mode for development
esbuild src/app.ts --bundle --sourcemap=inline --outfile=dist/app.js # ✅
// Use the JavaScript API for consistency
sourcemap: 'external'                                         // ✅

❌ Don’t Do This:

# Serve the maps publicly in production
# The map embeds the original source by default               # ❌
# Use inline mode for a production build
# The output includes the source map                            # ❌
# Assume the browser finds an external map without the comment
# The developer must attach it manually                         # ❌

Common Pitfalls

PitfallWhy It HappensFix
Browser does not show the sourceexternal mode omits the commentAttach the map manually or use the default mode
Output is unexpectedly largeinline mode embeds the mapUse external for production
Source code exposedsourcesContent is true by defaultSet sourcesContent: false or keep the map private
Stack trace is unreadableNo source map in the error monitorUpload the map to the monitor
Map does not match the codeThe map is from a different buildTie the map to the exact release version

Real-World Examples

1. External source map

esbuild src/app.ts --bundle --minify --sourcemap=external --outfile=dist/app.js

2. Inline source map

esbuild src/app.ts --bundle --sourcemap=inline --outfile=dist/app.js

3. Both

esbuild src/app.ts --bundle --sourcemap=both --outfile=dist/app.js

4. JavaScript API

esbuild.build({ sourcemap: 'external', outfile: 'dist/app.js' })

5. No sources content

esbuild.build({ sourcemap: 'external', sourcesContent: false })

6. The default

esbuild src/app.ts --bundle --sourcemap --outfile=dist/app.js

7. The production archive

find dist -name '*.map' -exec mv {} /private/maps/ \;

8. The manual attach

DevTools → Sources → right-click → Add source map

9. The error monitor

Upload the .map file to Sentry or the error monitor.

10. The decision

Dev → inline; Production → external, archived.

Visual

The three modes

┌─────────────────────────────────────────────────────────────┐
│  external                                                    │
│    app.js          (no sourceMappingURL comment)            │
│    app.js.map      (separate file)                          │
│                                                              │
│  inline                                                      │
│    app.js          (data URL at the end)                    │
│                                                              │
│  both                                                        │
│    app.js          (data URL at the end)                    │
│    app.js.map      (separate file)                          │
└─────────────────────────────────────────────────────────────┘

The source map bridge

┌─────────────────────────────────────────────────────────────┐
│  Original source (app.ts)                                    │
│       │                                                      │
│       ▼                                                      │
│  esbuild transform                                           │
│       │                                                      │
│       ▼                                                      │
│  Minified output (app.js) + app.js.map                      │
│       │                                                      │
│       ▼                                                      │
│  Browser DevTools reads the map                              │
│       │                                                      │
│       ▼                                                      │
│  Breakpoints in app.ts, execution in app.js                  │
└─────────────────────────────────────────────────────────────┘

The production deployment

┌─────────────────────────────────────────────────────────────┐
│  Build: sourcemap: 'external'                                │
│       │                                                      │
│       ├── dist/app.js ──► deploy to CDN                     │
│       │                                                      │
│       └── dist/app.js.map ──► private store                 │
│                                                              │
│  Error occurs → stack trace from CDN                         │
│       │                                                      │
│       ▼                                                      │
│  Error monitor uses the private map to symbolicate           │
└─────────────────────────────────────────────────────────────┘

The decision

┌─────────────────────────────────────────────────────────────┐
│  Development? ──► inline or default                          │
│  Production? ──► external, archived                          │
│  Public map needed? ──► default with comment                 │
│  Private map needed? ──► external without comment            │
└─────────────────────────────────────────────────────────────┘

Summary

ItemValue
Option--sourcemap
Modesexternal, inline, both
external behaviorWrites .map, omits comment
inline behaviorEmbeds map in the output
both behaviorWrites .map and embeds it
DefaultWrites .map and adds the comment
sourcesContent defaulttrue (embeds original source)
Production patternexternal, maps archived privately
Debugging toolBrowser DevTools → Sources

Key takeaways:

  • The source map is the bridge between the minified output and the original source. It maps every position in the output to the corresponding file, line, and column in the source, which is what allows a developer to set a breakpoint in the original TypeScript file while the browser executes the minified JavaScript .
  • external omits the //# sourceMappingURL comment. The .map file is written, but the browser does not automatically fetch it. This is the “hidden” source map pattern, and it is the correct choice for a production build where the map should be archived privately rather than served to the public .
  • inline embeds the map in the output. The browser finds the map in the data URL without a separate request. This is convenient for development but increases the output size and exposes the source if the output is public .
  • sourcesContent embeds the original source in the map. The default is true, which means the map file contains the source code. If the map is served publicly, the source is exposed. Set sourcesContent: false if the source must be fetched separately, or keep the map private .
  • The production pattern is external + private archive. The build uses sourcemap: 'external', the .map files are moved out of the public directory, and the maps are uploaded to the error monitoring service. The deployed .js files are map-free, and the debugging information is available only to the team .
  • The browser needs the comment for auto-discovery. With the external mode, the developer must attach the map manually in the DevTools Sources panel. With the default mode or the inline mode, the browser fetches the map automatically .

Remember: The --sourcemap option is a debugging tool, and the mode determines who can see the debugging information. The inline mode is for development, where the convenience of a single file outweighs the size. The external mode is for production, where the map is archived privately and the deployed output is map-free. The both mode is for the build that serves both purposes. And the sourcesContent field is the security decision: it embeds the original source in the map, which is useful for debugging and dangerous for a public deployment.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!