Source Maps

Source Maps with Vite

Enable production source maps in Vite and upload them to your error tracker so minified React or Vue stack traces become readable again.

Vite builds fast and ships minified JavaScript by default — which is great for performance, terrible for debugging production errors. When your app throws an exception, the stack trace points to index-abc123.js:1:45678, a location that means nothing to you. You need the original source: the .tsx file, the line number, the variable names. That's where vite source maps come in.

Source maps are files that map minified code back to your original TypeScript or JavaScript. Enable them in Vite, upload them to your error tracker, and every stack trace snaps back to your real source code — component names, variable names, the exact line. This guide walks through the setup, the CI/CD pipeline, and the gotchas specific to Vite.

Why you need source maps for Vite builds

Vite ships bundles that are minified and often split across multiple chunks. A production error lands in your error tracker looking like this:

Error: Cannot read properties of undefined (reading 'map')
  at Object.<anonymous> (index-4f9a.js:1:52210)
  at Module._load (node:internal/modules/loader:internal_modules.js:?)

That index-4f9a.js:1:52210 is a line and column in minified code — it has no variable names, no original function boundaries, nothing human-readable. Without source maps, you're reconstructing the error from the minified bundle by hand, which is slow and error-prone.

With source maps, your error tracker (like LightTrace) translates that location back to your original source:

Error: Cannot read properties of undefined (reading 'map')
  at fetchUserData (user.ts:24:15)
  at App.tsx:42:10

Now you can see the actual function, the actual file, and dive straight into the code that failed. For javascript-error-monitoring at scale, source maps are non-negotiable — they turn a 2-hour debugging session into a 5-minute fix.

Enable source maps in your Vite config

Generating source maps in Vite is a single config option. Add this to your vite.config.ts:

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  build: {
    sourcemap: true,
  },
})

That's it. When you run npm run build (or vite build), Vite will generate .map files alongside your JavaScript bundles:

dist/
  index.html
  assets/
    index-4f9a.js
    index-4f9a.js.map
    vendor-7x8y.js
    vendor-7x8y.js.map

Each .map file contains a compact representation of the mapping: for every position in the minified code, it stores the corresponding source file, line, and column. They're JSON, human-readable but compressible — a typical map file is 10–30% the size of its bundle.

Source maps are not shipped to the browser. They live on your error tracker's servers only. When an error occurs, the browser sends the minified stack trace to your tracker, which uses the map files to decode it back to the original source.

Understand source map files and formats

Each .js.map file is a JSON document with three main parts:

  • sources — the paths to your original TypeScript/JavaScript files (e.g., src/components/User.tsx)
  • mappings — a compressed string that maps each byte of the minified code to a position in the originals
  • sourcesContent — optionally, the full text of each original source file

Here's a snippet of a real source map (simplified):

{
  "version": 3,
  "file": "index-4f9a.js",
  "sourceRoot": "",
  "sources": ["../../src/App.tsx", "../../src/User.tsx"],
  "names": ["fetchUserData", "map", "email"],
  "mappings": "AAAA,SAASA,gBACT...",
  "sourcesContent": ["export function App() { ... }"]
}

The mappings string looks like gibberish but is a carefully designed compression format (VLQ encoding) that the error tracker decompresses to find the real location for each error. You don't need to read it — your error tracker handles that.

Upload source maps to your error tracker

Generating maps locally is only half the story. You need to upload them somewhere your error tracker can access them — typically during CI/CD, right after your build.

Most error trackers (including LightTrace) provide a CLI tool for uploading. Here's the pattern:

npm run build
npx @lighttrace/cli upload-sourcemaps dist --project web --release "v1.0.0"

Add this to your CI/CD pipeline after building. Here's an example GitHub Actions workflow:

name: Deploy
on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18
      
      - run: npm install
      - run: npm run build
      
      - name: Upload source maps
        run: npx @lighttrace/cli upload-sourcemaps dist --project web --release "${{ github.sha }}"
        env:
          LIGHTTRACE_KEY: ${{ secrets.LIGHTTRACE_KEY }}
      
      - name: Deploy
        run: npm run deploy

The key points:

  • Build first, then upload maps. Maps are generated in dist/ as part of the build.
  • Use a release identifier. Include a version, git SHA, or timestamp so you know which build a stack trace came from. LightTrace correlates map uploads with error events by release.
  • Store the API key securely. Use GitHub Secrets (or your CI's equivalent) for the LIGHTTRACE_KEY.
  • Upload in CI only. Never commit .map files to version control. Generate and upload them in your CI/CD pipeline, then delete them (or let the build clean up dist/).

Set up a CI step to upload source maps even if your deploy fails. If a partial rollout happens, you want the maps available for the code that did ship. Some teams upload maps before deploying to avoid any window where errors can't be decoded.

Vite source maps in development vs production

Vite has different source map settings for dev and production. The example above sets sourcemap: true in build, which applies only to production builds (running vite build). During development (running vite dev), Vite generates source maps by default anyway — they're cheap and fast because the code isn't minified.

If you want to be more explicit or tune this, use the sourcemap option in the build block with one of these values:

  • true — generate .map files (recommended for production)
  • 'inline' — embed the map inside the .js file (larger bundles, rarely needed)
  • false — no source maps (not recommended; saves a few seconds on build, costs hours on debugging)
  • 'hidden' — generate maps but don't reference them in the bundle (useful if you want to serve maps from a different endpoint)

For almost all projects, sourcemap: true is the right choice.

Common Vite source map gotchas

Maps don't upload by themselves. Generating them in vite.config.ts is only step one. If you skip the CI upload step, your error tracker will receive the minified stack traces and won't be able to decode them. Set up the upload automation early.

Release identifiers must match. When you upload maps, you tag them with a release (e.g., v1.0.0 or a git SHA). When errors land, your SDK must also include that same release identifier in the event. If they don't match, the error tracker can't find the right maps. In your Sentry SDK init, set the release explicitly:

import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: "https://<key>@your-lighttrace-host/1",
  release: "web@v1.0.0", // Must match the upload
  environment: "production",
});

Source path mismatches cause decoding to fail. If your uploaded maps reference src/App.tsx but your error tracker's settings strip the src/ prefix, the paths won't match and the maps won't work. When you upload, make sure the source paths in your maps align with what your error tracker expects. Most tools have configuration for this (often called a "source root" or "stripCommonPath" option).

Debugging production errors with the maps

Once maps are uploaded, every stack trace your error tracker captures will automatically be decoded. Visit your error dashboard and you'll see:

Error: Cannot read properties of undefined (reading 'map')
  at fetchUserData (src/user.ts:24:15)
  at processResults (src/App.tsx:42:10)
  at <anonymous> (src/index.tsx:8:5)

Actual files, actual line numbers, actual function names. For the full workflow of debugging production errors using these traces, see how to debug production errors. Combined with breadcrumbs (which show what the user did before the crash), source maps turn a minified stack trace into enough information to fix the bug fast.

If you're using React, the same source maps help react-error-tracking — error boundaries and render errors benefit from readable traces just as much as unhandled promise rejections.

Start tracking errors in minutes

Enable source maps in Vite, upload them to LightTrace, and turn minified stack traces into readable production errors you can actually fix.

Source maps are a small setup cost with a huge debugging payoff. Enable them now — the first production error you decode will justify the five minutes it takes to set up the CI pipeline.

Fix your next production error faster

Point any Sentry SDK at LightTrace — free up to 5,000 events/month.