documentation

Quick Start

Get your first bundle snapshot recorded in under 5 minutes

How it works

dendrobundle doesn't build anything. Your bundler already produces a stats file describing every asset and module in your build — dendrobundle consumes that JSON. The whole flow is:

  1. Build your app the way you always do.
  2. Have your bundler emit its stats JSON (most can, with one flag or plugin).
  3. POST the file to the push API with curl.

Use the bundler select at the top right of this page to switch the examples to your toolchain.

Create an account and project

Register at dendrobundle.com/register. No credit card required. After signing in, create a project from the dashboard, then open the project's page — API tokens are managed per project, so you need to be on the project before you can create one.

Generate an API token

From your project, go to Project Settings, then API Tokens, then New token. Copy the token immediately — it's shown once.

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzhmMmEiLCJqdGkiOiJ0b2tfYzdlMSJ9.kQ2pX…   # save this; shown once

Store it as a CI secret: DENDROBUNDLE_TOKEN.

Submit your first snapshot

Drop this script into your project (e.g. scripts/push-bundle.sh), make it executable, and run it after a build. Pick your bundler above to see the matching stats command.

#!/usr/bin/env bash
set -euo pipefail

# 1. build + emit stats (webpack)
npm run build
npx webpack --profile --json > stats.json

# 2. submit the snapshot
curl -sS "https://dendrobundle.com/api/push?branch=$(git branch --show-current)&commit=$(git rev-parse HEAD)" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @stats.json
#!/usr/bin/env bash
set -euo pipefail

# 1. build + emit stats (esbuild metafile)
npx esbuild src/index.ts --bundle --outfile=dist/bundle.js --metafile=stats.json

# 2. submit the snapshot
curl -sS "https://dendrobundle.com/api/push?branch=$(git branch --show-current)&commit=$(git rev-parse HEAD)" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @stats.json
#!/usr/bin/env bash
set -euo pipefail

# 1. build + emit stats (vite via rollup-plugin-visualizer, template: 'raw-data')
#    see the Supported Formats page for the one-time vite.config.js setup
npm run build

# 2. submit the snapshot
curl -sS "https://dendrobundle.com/api/push?branch=$(git branch --show-current)&commit=$(git rev-parse HEAD)" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @dist/rollup-stats.json
#!/usr/bin/env bash
set -euo pipefail

# 1. build with source maps, then extract the module breakdown
npm run build -- --sourcemap
npx source-map-explorer --json 'dist/*.js' > stats.json

# 2. submit the snapshot
curl -sS "https://dendrobundle.com/api/push?branch=$(git branch --show-current)&commit=$(git rev-parse HEAD)" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @stats.json

The format is auto-detected — webpack-stats, esbuild metafile, source-map-explorer, and rollup-stats (rollup-plugin-visualizer) JSON all work. See Supported Formats for details.

On success you'll get:

{
  "snapshotId": "snap_01j9x2k4m8p3q7r5t6w9y2z0",
  "format": "webpack-stats",
  "totalSizeBytes": 2528604,
  "assetCount": 23
}

View your snapshot

Open your dashboard and click the project — the new snapshot is at the top of its history, with the treemap, asset breakdown, and size diff against the previous build.

Pass branch and commit

Tag snapshots so dendrobundle can diff across branches:

curl -sS "https://dendrobundle.com/api/push?branch=$BRANCH&commit=$COMMIT_SHA" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @stats.json

In CI, $BRANCH and $COMMIT_SHA come from the environment. All parameters and required headers are documented in the API Reference; provider-specific variable names are in CI Integration.

Framework quick starts

Pick your framework with the framework select at the top right. Each example ends in the same curl call — only the stats-generation step differs. The Supported Formats page covers each format in depth.

Next.js

Next.js builds with webpack under the hood. Emit webpack stats from the production build:

// next.config.js
module.exports = {
  webpack(config, { isServer }) {
    if (!isServer) {
      const { StatsWriterPlugin } = require('webpack-stats-plugin');
      config.plugins.push(
        new StatsWriterPlugin({
          filename: '../webpack-stats.json',
          stats: { assets: true, chunks: true, modules: true },
        }),
      );
    }
    return config;
  },
};
npm run build
curl -sS "https://dendrobundle.com/api/push?branch=$(git branch --show-current)&commit=$(git rev-parse HEAD)" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @webpack-stats.json

Uses the webpack-stats format.

React (Vite)

Add rollup-plugin-visualizer with the raw-data template — Vite builds on Rollup, so the same plugin works:

// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';

export default {
  plugins: [
    visualizer({ template: 'raw-data', filename: 'dist/rollup-stats.json', gzipSize: true, brotliSize: true }),
  ],
};
npm run build
curl -sS "https://dendrobundle.com/api/push?branch=$(git branch --show-current)&commit=$(git rev-parse HEAD)" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @dist/rollup-stats.json

Uses the rollup-stats format.

Angular

The Angular CLI can emit webpack-compatible stats directly:

ng build --stats-json
curl -sS "https://dendrobundle.com/api/push?branch=$(git branch --show-current)&commit=$(git rev-parse HEAD)" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @dist/<project-name>/stats.json

For esbuild-based builders (Angular 17+ default), source-map-explorer is the most accurate option:

ng build --source-map
npx source-map-explorer --json 'dist/**/*.js' > stats.json

Uses the webpack-stats or source-map-explorer format.

Nuxt

Nuxt 3 builds with Vite — add rollup-plugin-visualizer with the raw-data template:

// nuxt.config.ts
import { visualizer } from 'rollup-plugin-visualizer';

export default defineNuxtConfig({
  vite: {
    plugins: [
      visualizer({ template: 'raw-data', filename: 'rollup-stats.json', gzipSize: true, brotliSize: true }),
    ],
  },
});
npm run build
curl -sS "https://dendrobundle.com/api/push?branch=$(git branch --show-current)&commit=$(git rev-parse HEAD)" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @rollup-stats.json

Uses the rollup-stats format.

SvelteKit

SvelteKit builds with Vite — add rollup-plugin-visualizer with the raw-data template:

// vite.config.js
import { sveltekit } from '@sveltejs/kit/vite';
import { visualizer } from 'rollup-plugin-visualizer';

export default {
  plugins: [
    sveltekit(),
    visualizer({ template: 'raw-data', filename: 'rollup-stats.json', gzipSize: true, brotliSize: true }),
  ],
};
npm run build
curl -sS "https://dendrobundle.com/api/push?branch=$(git branch --show-current)&commit=$(git rev-parse HEAD)" \
  -H "Authorization: Bearer $DENDROBUNDLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d @rollup-stats.json

Uses the rollup-stats format.

What's next