diff --git a/packages/plugin-bundle-stats/README.md b/packages/plugin-bundle-stats/README.md
new file mode 100644
index 000000000..c6acbfe40
--- /dev/null
+++ b/packages/plugin-bundle-stats/README.md
@@ -0,0 +1,36 @@
+# @code-pushup/bundle-stats-plugin
+
+[](https://www.npmjs.com/package/@code-pushup/bundle-stats-plugin)
+[](https://npmtrends.com/@code-pushup/bundle-stats-plugin)
+[](https://www.npmjs.com/package/@code-pushup/bundle-stats-plugin?activeTab=dependencies)
+
+🕵️ **Code PushUp plugin for measuring bundle size and show insights in tables and import trees.** 🔥
+
+---
+
+This plugin analyzes your build output from modern bundlers to provide detailed bundle size insights and help you track bundle optimization over time. It supports multiple bundlers and provides configurable thresholds for monitoring bundle size targets.
+
+Bundle statistics are mapped to Code PushUp audits in the following way:
+
+- **Value**: Total bundle size in bytes for the analyzed configuration
+- **Score**: Calculated based on size thresholds with penalties for oversized artifacts
+- **Display Value**: Human-readable bundle size (e.g., "2.5 MB")
+- **Issues**: Detailed warnings and errors for bundles exceeding thresholds
+
+## Getting started
+
+1. If you haven't already, install [@code-pushup/cli](../cli/README.md) and create a configuration file.
+
+2. Install as a dev dependency with your package manager:
+
+ ```sh
+ npm install --save-dev @code-pushup/bundle-stats-plugin
+ ```
+
+ ```sh
+ yarn add --dev @code-pushup/bundle-stats-plugin
+ ```
+
+ ```sh
+ pnpm add --save-dev @code-pushup/bundle-stats-plugin
+ ```
diff --git a/packages/plugin-bundle-stats/package.json b/packages/plugin-bundle-stats/package.json
new file mode 100644
index 000000000..3d19c0816
--- /dev/null
+++ b/packages/plugin-bundle-stats/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "@code-pushup/bundle-stats-plugin",
+ "version": "0.77.0",
+ "license": "MIT",
+ "description": "Code PushUp plugin for measuring bundle size and show insights in tables and import trees.",
+ "homepage": "https://github.com/code-pushup/cli/tree/main/packages/plugin-bundle-stats#readme",
+ "bugs": {
+ "url": "https://github.com/code-pushup/cli/issues?q=is%3Aissue%20state%3Aopen%20type%3ABug%20label%3A\"🧩%20bundle-stats-plugin\""
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/code-pushup/cli.git",
+ "directory": "packages/plugin-bundle-stats"
+ },
+ "keywords": [
+ "CLI",
+ "Code PushUp",
+ "plugin",
+ "bundle size",
+ "bundle stats"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "type": "module",
+ "files": [
+ "./*.md",
+ "./src/**"
+ ],
+ "dependencies": {},
+ "peerDependencies": {},
+ "scripts": {},
+ "devDependencies": {}
+}
diff --git a/packages/plugin-bundle-stats/project.json b/packages/plugin-bundle-stats/project.json
new file mode 100644
index 000000000..467f293cf
--- /dev/null
+++ b/packages/plugin-bundle-stats/project.json
@@ -0,0 +1,12 @@
+{
+ "name": "plugin-bundle-stats",
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
+ "sourceRoot": "packages/plugin-bundle-stats/src",
+ "projectType": "library",
+ "targets": {
+ "build": {},
+ "lint": {},
+ "lint-report": {}
+ },
+ "tags": ["scope:plugin", "type:feature", "publishable"]
+}
diff --git a/packages/plugin-bundle-stats/src/index.ts b/packages/plugin-bundle-stats/src/index.ts
new file mode 100644
index 000000000..87a21a621
--- /dev/null
+++ b/packages/plugin-bundle-stats/src/index.ts
@@ -0,0 +1 @@
+export { BUNDLE_STATS_PLUGIN_SLUG } from './lib/constants.js';
diff --git a/packages/plugin-bundle-stats/src/lib/constants.ts b/packages/plugin-bundle-stats/src/lib/constants.ts
new file mode 100644
index 000000000..29c502dbf
--- /dev/null
+++ b/packages/plugin-bundle-stats/src/lib/constants.ts
@@ -0,0 +1 @@
+export const BUNDLE_STATS_PLUGIN_SLUG = 'bundle-stats';
diff --git a/packages/plugin-bundle-stats/tsconfig.json b/packages/plugin-bundle-stats/tsconfig.json
new file mode 100644
index 000000000..893f9a925
--- /dev/null
+++ b/packages/plugin-bundle-stats/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "module": "ESNext",
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "noImplicitOverride": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "types": ["vitest"]
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.lib.json"
+ },
+ {
+ "path": "./tsconfig.test.json"
+ }
+ ]
+}
diff --git a/packages/plugin-bundle-stats/tsconfig.lib.json b/packages/plugin-bundle-stats/tsconfig.lib.json
new file mode 100644
index 000000000..ef2f7e2b3
--- /dev/null
+++ b/packages/plugin-bundle-stats/tsconfig.lib.json
@@ -0,0 +1,16 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "declaration": true,
+ "types": ["node"]
+ },
+ "include": ["src/**/*.ts"],
+ "exclude": [
+ "vite.config.unit.ts",
+ "vite.config.integration.ts",
+ "src/**/*.test.ts",
+ "src/**/*.mock.ts",
+ "mocks/**/*.ts"
+ ]
+}
diff --git a/packages/plugin-bundle-stats/tsconfig.test.json b/packages/plugin-bundle-stats/tsconfig.test.json
new file mode 100644
index 000000000..bb1ab5e0c
--- /dev/null
+++ b/packages/plugin-bundle-stats/tsconfig.test.json
@@ -0,0 +1,17 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"]
+ },
+ "include": [
+ "vite.config.unit.ts",
+ "vite.config.integration.ts",
+ "mocks/**/*.ts",
+ "src/**/*.test.ts",
+ "src/**/*.test.tsx",
+ "src/**/*.test.js",
+ "src/**/*.test.jsx",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/packages/plugin-bundle-stats/vite.config.integration.ts b/packages/plugin-bundle-stats/vite.config.integration.ts
new file mode 100644
index 000000000..0459f3ef5
--- /dev/null
+++ b/packages/plugin-bundle-stats/vite.config.integration.ts
@@ -0,0 +1,30 @@
+///
+import { defineConfig } from 'vite';
+import { tsconfigPathAliases } from '../../tools/vitest-tsconfig-path-aliases.js';
+
+export default defineConfig({
+ cacheDir: '../../node_modules/.vite/plugin-bundle-stats',
+ test: {
+ reporters: ['basic'],
+ globals: true,
+ cache: {
+ dir: '../../node_modules/.vitest/plugin-bundle-stats',
+ },
+ alias: tsconfigPathAliases(),
+ pool: 'threads',
+ poolOptions: { threads: { singleThread: true } },
+ coverage: {
+ reporter: ['text', 'lcov'],
+ reportsDirectory: '../../coverage/plugin-bundle-stats/int-tests',
+ exclude: ['mocks/**', '**/types.ts'],
+ },
+ environment: 'node',
+ include: ['src/**/*.int.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ globalSetup: ['../../global-setup.ts'],
+ setupFiles: [
+ '../../testing/test-setup/src/lib/cliui.mock.ts',
+ '../../testing/test-setup/src/lib/reset.mocks.ts',
+ '../../testing/test-setup/src/lib/chrome-path.mock.ts',
+ ],
+ },
+});
diff --git a/packages/plugin-bundle-stats/vite.config.unit.ts b/packages/plugin-bundle-stats/vite.config.unit.ts
new file mode 100644
index 000000000..9fd17033d
--- /dev/null
+++ b/packages/plugin-bundle-stats/vite.config.unit.ts
@@ -0,0 +1,32 @@
+///
+import { defineConfig } from 'vite';
+import { tsconfigPathAliases } from '../../tools/vitest-tsconfig-path-aliases.js';
+
+export default defineConfig({
+ cacheDir: '../../node_modules/.vite/plugin-bundle-stats',
+ test: {
+ reporters: ['basic'],
+ globals: true,
+ cache: {
+ dir: '../../node_modules/.vitest/plugin-bundle-stats',
+ },
+ alias: tsconfigPathAliases(),
+ pool: 'threads',
+ poolOptions: { threads: { singleThread: true } },
+ coverage: {
+ reporter: ['text', 'lcov'],
+ reportsDirectory: '../../coverage/plugin-bundle-stats/unit-tests',
+ exclude: ['mocks/**', '**/types.ts'],
+ },
+ environment: 'node',
+ include: ['src/**/*.unit.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ globalSetup: ['../../global-setup.ts'],
+ setupFiles: [
+ '../../testing/test-setup/src/lib/cliui.mock.ts',
+ '../../testing/test-setup/src/lib/extend/ui-logger.matcher.ts',
+ '../../testing/test-setup/src/lib/fs.mock.ts',
+ '../../testing/test-setup/src/lib/console.mock.ts',
+ '../../testing/test-setup/src/lib/reset.mocks.ts',
+ ],
+ },
+});
diff --git a/tsconfig.base.json b/tsconfig.base.json
index b183db5a3..546bff5e1 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -20,6 +20,9 @@
"allowSyntheticDefaultImports": true,
"verbatimModuleSyntax": true,
"paths": {
+ "@code-pushup/bundle-stats-plugin": [
+ "packages/plugin-bundle-stats/src/index.ts"
+ ],
"@code-pushup/ci": ["packages/ci/src/index.ts"],
"@code-pushup/cli": ["packages/cli/src/index.ts"],
"@code-pushup/core": ["packages/core/src/index.ts"],