CLI tool and programmatic API for performance budget monitoring that measures JavaScript bundle size and execution time with plugin support for webpack, esbuild, and various bundlers.
—
Size Limit uses a modular plugin architecture that allows different bundlers, measurement tools, and analysis features to be combined flexibly.
The plugin system defines a standard interface for extending Size Limit functionality.
// Plugin interface for plugin developers
interface Plugin {
// Plugin identification
name: string; // Plugin name (e.g., "@size-limit/webpack")
// Lifecycle hooks
before?(config: object, check: Check): Promise<void>;
finally?(config: object, check: Check): Promise<void>;
// Processing steps (0-100 available)
step0?(config: object, check: Check): Promise<void>;
step1?(config: object, check: Check): Promise<void>;
// ... step2 through step99
step100?(config: object, check: Check): Promise<void>;
// Progress messages for steps
wait0?: string; // Progress message for step0
wait1?: string; // Progress message for step1
// ... wait2 through wait100
}
// Plugin loading system
class Plugins {
constructor(list: Plugin[]);
list: Plugin[]; // Array of loaded plugins
isEmpty: boolean; // True if no plugins loaded
has(type: string): boolean; // Check if plugin type is available
}Official plugins that provide essential Size Limit functionality.
// File measurement plugin
// Package: @size-limit/file
// Provides: Basic file size measurement with compression options
// Webpack integration plugin
// Package: @size-limit/webpack
// Provides: Webpack bundling, tree-shaking, custom config support
// ESBuild integration plugin
// Package: @size-limit/esbuild
// Provides: ESBuild bundling, fast compilation, modern JS support
// Time measurement plugin
// Package: @size-limit/time
// Provides: Browser execution time measurement, network simulation
// CSS support plugin
// Package: @size-limit/webpack-css
// Provides: CSS processing and measurement with webpack
// Bundle analysis plugins
// Package: @size-limit/webpack-why
// Package: @size-limit/esbuild-why
// Provides: Detailed bundle analysis, dependency graphs, size breakdownsPlugins are installed as separate npm packages and automatically detected.
Usage Examples:
# Install individual plugins
npm install --save-dev @size-limit/file
npm install --save-dev @size-limit/webpack
npm install --save-dev @size-limit/time
# Install preset bundles
npm install --save-dev @size-limit/preset-app
npm install --save-dev @size-limit/preset-big-lib
npm install --save-dev @size-limit/preset-small-libSize Limit automatically detects and loads available plugins from project dependencies.
// Plugin detection rules:
// 1. Scans package.json dependencies, devDependencies, optionalDependencies
// 2. Matches packages with names starting with "@size-limit/" or "size-limit-"
// 3. Dynamically imports matching packages
// 4. Flattens plugin arrays (plugins can export multiple plugins)
// 5. Creates Plugins instance with loaded plugin list
async function loadPlugins(pkg: PackageInfo): Promise<Plugins>;
interface PackageInfo {
packageJson: {
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
optionalDependencies?: Record<string, string>;
};
path: string;
}Plugins are activated based on configuration options in each check.
// Plugin activation mapping
const PLUGIN_OPTIONS = {
brotli: "file", // @size-limit/file
gzip: "file", // @size-limit/file
webpack: "webpack", // @size-limit/webpack
config: ["webpack", "esbuild"], // @size-limit/webpack or @size-limit/esbuild
entry: "webpack", // @size-limit/webpack
ignore: ["webpack", "esbuild"], // @size-limit/webpack or @size-limit/esbuild
import: ["webpack", "esbuild"], // @size-limit/webpack or @size-limit/esbuild
modifyWebpackConfig: "webpack", // @size-limit/webpack
modifyEsbuildConfig: "esbuild", // @size-limit/esbuild
running: "time", // @size-limit/time
time: "time", // @size-limit/time
uiReports: "webpack", // @size-limit/webpack
compareWith: "webpack" // @size-limit/webpack
};Configuration Examples:
{
"size-limit": [
{
"name": "File only",
"path": "dist/lib.js",
"webpack": false,
"running": false
},
{
"name": "Webpack + Time",
"path": "src/app.js",
"webpack": true,
"running": true,
"time": {
"networkSpeed": "3G"
}
},
{
"name": "ESBuild",
"path": "src/modern.js",
"config": "esbuild.config.js",
"modifyEsbuildConfig": "(config) => ({ ...config, target: 'es2020' })"
}
]
}Preset packages provide pre-configured plugin combinations for common use cases.
// @size-limit/preset-app
// Includes: @size-limit/webpack, @size-limit/time, @size-limit/file
// Use case: Large applications with custom bundlers
// @size-limit/preset-big-lib
// Includes: @size-limit/webpack, @size-limit/file
// Use case: Libraries > 10kB that need bundling
// @size-limit/preset-small-lib
// Includes: @size-limit/file
// Use case: Small libraries < 10kB that don't need bundlingPreset Usage:
# Install preset
npm install --save-dev @size-limit/preset-app
# Configuration automatically uses included plugins
{
"size-limit": [
{
"path": "dist/app.js",
"limit": "100 kB"
}
]
}Size Limit executes plugins through a structured lifecycle with numbered steps.
// Plugin execution order:
// 1. before() hook for all plugins
// 2. step0() through step100() for all plugins (in sequence)
// 3. finally() hook for all plugins (always executed, even on error)
// Example execution flow:
async function executePlugins(plugins: Plugins, config: object) {
try {
// Before phase
for (let plugin of plugins.list) {
if (plugin.before) {
await plugin.before(config, check);
}
}
// Step phases
for (let stepNumber = 0; stepNumber <= 100; stepNumber++) {
for (let plugin of plugins.list) {
const stepMethod = `step${stepNumber}`;
if (plugin[stepMethod]) {
await plugin[stepMethod](config, check);
}
}
}
} finally {
// Finally phase
for (let plugin of plugins.list) {
if (plugin.finally) {
await plugin.finally(config, check);
}
}
}
}Developers can create custom plugins following the standard interface.
Custom Plugin Example:
// custom-plugin.js
export default {
name: "custom-size-plugin",
// Initialize plugin
async before(config, check) {
console.log(`Starting analysis for ${check.name}`);
},
// Main processing step
async step50(config, check) {
// Custom size calculation logic
const customSize = await calculateCustomSize(check.files);
check.customMetric = customSize;
},
// Cleanup
async finally(config, check) {
console.log(`Finished analysis for ${check.name}`);
},
// Progress messages
wait50: "Calculating custom metrics..."
};
// Usage with programmatic API
import sizeLimit from "size-limit";
import filePlugin from "@size-limit/file";
import customPlugin from "./custom-plugin.js";
const results = await sizeLimit(
[filePlugin, customPlugin],
["dist/bundle.js"]
);Guidelines for developing Size Limit plugins.
// Plugin development best practices:
// 1. Use descriptive plugin names with @size-limit/ or size-limit- prefix
// 2. Implement appropriate lifecycle hooks (before/finally for setup/cleanup)
// 3. Use numbered steps (step0-step100) for main processing
// 4. Provide wait messages for long-running steps
// 5. Handle errors gracefully and clean up resources in finally
// 6. Modify check objects to add measurements
// 7. Respect existing check properties and plugin interactions
// 8. Document plugin configuration options and requirementsPlugin errors are handled gracefully with proper cleanup.
// Error handling flow:
// 1. If any step throws an error, execution stops
// 2. finally() hooks are always called for cleanup
// 3. Progress spinners are marked as failed
// 4. Error is propagated to CLI or API caller
// Error handling example in plugin:
export default {
name: "example-plugin",
async step10(config, check) {
try {
await riskyOperation(check);
} catch (error) {
// Log error context
console.error(`Plugin ${this.name} failed:`, error);
// Re-throw to stop execution
throw error;
}
},
async finally(config, check) {
// Always cleanup, even on error
await cleanup();
}
};Install with Tessl CLI
npx tessl i tessl/npm-size-limit