jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Frontend Build Tools & Package Managers in 2026 — The Complete Comparison

A bilingual comparison of frontend build tools (Webpack, Vite, Rollup, esbuild, Turbopack, SWC, Bun) and package managers (npm, Yarn, pnpm, Bun) — architecture, speed, disk usage, ecosystem, and when to use each.

The Build Tool Landscape Has Exploded {Bối cảnh Build Tool đã bùng nổ}

In 2020, the choice was simple {Năm 2020, lựa chọn rất đơn giản}: Webpack for apps, Rollup for libraries {Webpack cho app, Rollup cho thư viện}. In 2026, we have 10+ serious contenders {chúng ta có hơn 10 ứng viên nghiêm túc}, written in JavaScript, Go, and Rust {viết bằng JavaScript, Go, và Rust}.

This post covers every major build tool {Bài viết này bao gồm mọi build tool chính}, what they’re good at, and when to choose them {chúng giỏi gì, và khi nào nên chọn chúng}. Then Part 2 does the same for the layer underneath — the package managers (npm, Yarn, pnpm, Bun) that install the dependencies your build tool bundles {Sau đó Phần 2 làm điều tương tự cho lớp bên dưới — các package manager (npm, Yarn, pnpm, Bun) cài đặt dependency mà build tool của bạn đem đi bundle}.

Two different jobs, often confused {Hai công việc khác nhau, hay bị nhầm}: a build tool turns your source into shippable output; a package manager downloads and links the dependencies into node_modules. You need one of each {một build tool biến source của bạn thành output xuất xưởng; một package manager tải và liên kết dependency vào node_modules. Bạn cần mỗi loại một cái}.


Quick Overview Table {Bảng tổng quan nhanh}

ToolLanguage {Ngôn ngữ}Category {Phân loại}Best for {Tốt nhất cho}
Webpack 5JavaScriptBundlerLarge legacy apps, complex configs {App lớn cũ, config phức tạp}
Vite 6JavaScript + Rust (via Rolldown)Dev server + BundlerModern web apps (React, Vue, Svelte, Astro)
Rollup 4JavaScriptBundlerLibraries, ES modules
RolldownRustBundlerVite’s production bundler (replacing Rollup) {Bundler production của Vite}
esbuildGoBundler + TranspilerUltra-fast builds, simple apps, tooling infra
TurbopackRustBundlerNext.js (exclusive)
Parcel 2Rust + JavaScriptBundlerZero-config projects, prototypes
SWCRustTranspiler + MinifierReplacing Babel/Terser in other tools
BunZig + C++Runtime + BundlerFull-stack JS, fast scripts
tsupJavaScript (wraps esbuild)Library bundlerTypeScript libraries {Thư viện TypeScript}
unbuildJavaScript (wraps Rollup)Library bundlerUniversal libraries (CJS + ESM)

Core Concepts You Need First {Khái niệm cốt lõi cần nắm trước}

People say “build tool” to mean very different jobs {Người ta gọi “build tool” cho nhiều việc rất khác nhau}. Separating them makes every comparison below click {Tách chúng ra thì mọi so sánh bên dưới sẽ rõ ngay}:

Your source code (.tsx, .ts, .css, .svg ...)


┌──────────────┐   1. TRANSPILE  — strip types, JSX → JS, modern → target JS
│  Transpiler  │      (Babel, SWC, esbuild, tsc)
└──────┬───────┘

┌──────────────┐   2. BUNDLE     — resolve imports, build one dependency graph,
│   Bundler    │      tree-shake, code-split into chunks
└──────┬───────┘      (Webpack, Rollup, Rolldown, esbuild, Parcel, Turbopack)

┌──────────────┐   3. MINIFY     — shrink output (rename vars, drop whitespace,
│  Minifier    │      dead-code elimination) (Terser, SWC, esbuild)
└──────┬───────┘

   dist/ output

   Separate concern → DEV SERVER + HMR: serve files while you code and
   hot-swap only what changed (Vite, Webpack dev server, Turbopack).

A single tool may do one job or all of them {Một tool có thể làm một việc hoặc tất cả}. esbuild does 1+2+3 {esbuild làm 1+2+3}; SWC does 1+3 {SWC làm 1+3}; Rollup does 2 {Rollup làm 2}; Vite orchestrates everything plus the dev server {Vite điều phối tất cả cộng thêm dev server}.

ESM vs CJS — why it matters {ESM vs CJS — vì sao quan trọng}

Bundlers care deeply about module format {Bundler rất quan tâm tới định dạng module}. ESM (import/export) is statically analyzable {ESM phân tích tĩnh được}, so the bundler knows exactly what is used {nên bundler biết chính xác cái gì được dùng}. CJS (require) is dynamic {CJS động}, which blocks tree-shaking {nên chặn tree-shaking}.

ESM (static)                         CJS (dynamic)
import { a } from './m';             const { a } = require('./m');
→ bundler sees "only a is used"      → could require anything at runtime
→ b, c, d are tree-shaken away       → whole module kept "just in case"

Tree-shaking in one picture {Tree-shaking trong một hình}

Tree-shaking removes exports nobody imports {Tree-shaking loại bỏ export không ai import}:

utils.js exports: [ format, parse, debounce, throttle ]
app.js imports:   [ format ]

                       ▼ tree-shake
final bundle keeps: [ format ]      ← parse/debounce/throttle dropped

Dev server & HMR {Dev server & HMR}

HMR (Hot Module Replacement) swaps a changed module without a full reload, keeping app state {HMR thay module đã đổi mà không reload toàn trang, giữ nguyên state}. This is the single biggest day-to-day DX factor {Đây là yếu tố DX hằng ngày lớn nhất}, and where native-speed tools win hardest {và là chỗ tool tốc-độ-native thắng đậm nhất}.


A Short Timeline {Dòng thời gian ngắn}

How we got from “one obvious choice” to “ten contenders” {Làm sao ta đi từ “một lựa chọn hiển nhiên” tới “mười ứng viên”}:

2012 ──● Webpack — "everything is a module", loaders/plugins era

2015 ──● Rollup — ESM-first, tree-shaking, the library bundler

2016 ──● Babel everywhere — JS transpilation standard (but slow)

2020 ──● esbuild (Go) — 10-100x faster, sparks the native rewrite wave
        │● Snowpack/Vite — "no-bundle" dev with native ESM

2021 ──● SWC (Rust) — Babel/Terser killer, adopted by Next.js

2022 ──● Turbopack (Rust) — Vercel's incremental bundler for Next.js

2023 ──● Bun (Zig) — runtime + bundler + package manager in one

2024 ──● Rolldown (Rust) — Rollup rewrite, becomes Vite's prod engine

2026 ──● Convergence — Vite/Rolldown is the default; JS-based tools fade

The throughline {Mạch xuyên suốt}: every winning tool of the last five years is written in a systems language (Go, Rust, Zig) {mọi tool thắng cuộc 5 năm qua đều viết bằng ngôn ngữ hệ thống (Go, Rust, Zig)}.


Webpack 5 {Webpack 5}

Architecture {Kiến trúc}

The original king of bundlers {Vua bundler nguyên thuỷ}. Webpack treats everything as a module {Webpack xem mọi thứ là module} — JS, CSS, images, fonts — and builds a dependency graph {và xây dựng đồ thị phụ thuộc}.

Entry → Loaders (transform) → Plugins (optimize) → Output chunks
         ↑                      ↑
         babel-loader            TerserPlugin
         css-loader              MiniCssExtractPlugin
         file-loader             HtmlWebpackPlugin

Strengths {Điểm mạnh}

  • Ecosystem {Hệ sinh thái}: thousands of loaders and plugins for any use case {hàng nghìn loader và plugin cho mọi trường hợp}
  • Code splitting: the most mature and configurable {trưởng thành và tuỳ chỉnh được nhất}
  • Module Federation: micro-frontend architecture built-in {kiến trúc micro-frontend tích hợp sẵn}
  • Stability {Ổn định}: battle-tested in millions of production apps {đã thử nghiệm trong hàng triệu app production}
  • HMR: works with any framework {hoạt động với mọi framework}

Weaknesses {Điểm yếu}

  • Speed {Tốc độ}: slowest bundler in 2026 {bundler chậm nhất 2026}. Large projects: 30-120s cold builds {Project lớn: 30-120s build lạnh}
  • Configuration {Cấu hình}: notoriously complex {phức tạp khét tiếng}. A typical config is 100-300 lines {Config thường 100-300 dòng}
  • Dev experience {Trải nghiệm dev}: slow startup, slow HMR on large codebases {khởi động chậm, HMR chậm trên codebase lớn}
  • Mental model {Mô hình tư duy}: loader chains, plugin hooks — steep learning curve {chuỗi loader, hook plugin — đường cong học dốc}

When to use {Khi nào dùng}

  • Maintaining existing large apps that already use Webpack {Bảo trì app lớn đã dùng Webpack}
  • Need Module Federation for micro-frontends {Cần Module Federation cho micro-frontend}
  • Complex custom build pipelines that no other tool supports {Pipeline build tuỳ chỉnh phức tạp mà không tool nào khác hỗ trợ}

When NOT to use {Khi nào KHÔNG dùng}

  • New projects in 2026 {Project mới năm 2026} — Vite or Turbopack (for Next.js) are strictly better {Vite hoặc Turbopack (cho Next.js) tốt hơn hoàn toàn}
  • If build speed matters (it always does) {Nếu tốc độ build quan trọng (luôn quan trọng)}
// webpack.config.js — typical setup
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.tsx',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'swc-loader', // replaced babel-loader
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
    ],
  },
  plugins: [new HtmlWebpackPlugin({ template: './index.html' })],
  resolve: { extensions: ['.tsx', '.ts', '.js'] },
};

Vite 6 {Vite 6}

Architecture {Kiến trúc}

Vite takes a fundamentally different approach {Vite có cách tiếp cận khác căn bản}: no bundling in development {không bundle khi development}. It serves native ES modules directly to the browser {Nó phục vụ ES module native trực tiếp cho browser}, using the browser’s own import system {dùng hệ thống import của chính browser}.

Development:
  Source files → esbuild (dep pre-bundling) → Native ESM → Browser
  HMR: only re-serves the changed module

Production:
  Source files → Rolldown (Rust bundler) → Optimized chunks

Strengths {Điểm mạnh}

  • Instant dev startup {Khởi động dev tức thì}: no bundling, just serve files {không bundle, chỉ serve file}. Even 10K+ module apps start in < 500ms {App hơn 10K module cũng khởi động dưới 500ms}
  • Lightning HMR {HMR siêu nhanh}: typically under 50ms regardless of app size {thường dưới 50ms bất kể app size}
  • Simple config {Cấu hình đơn giản}: 10-30 lines for most projects {10-30 dòng cho hầu hết project}
  • Plugin ecosystem {Hệ sinh thái plugin}: compatible with Rollup plugins {tương thích plugin Rollup}
  • Framework agnostic {Không phụ thuộc framework}: React, Vue, Svelte, Solid, Astro, Qwik all use Vite {đều dùng Vite}
  • CSS support: PostCSS, Tailwind, CSS Modules, preprocessors — all built-in {tất cả tích hợp sẵn}

Weaknesses {Điểm yếu}

  • Dev/prod parity {Đồng nhất dev/prod}: historically, dev (native ESM) and prod (Rollup bundle) could behave differently {trước đây, dev và prod có thể hoạt động khác nhau}. Rolldown integration fixes this {Tích hợp Rolldown sửa vấn đề này}
  • Large dependency pre-bundling {Pre-bundling dependency lớn}: first cold start can be slow if many deps need pre-bundling {Lần chạy lạnh đầu có thể chậm nếu nhiều dep cần pre-bundle}
  • SSR complexity {Phức tạp SSR}: SSR pipeline is less mature than Next.js/Webpack’s {Pipeline SSR ít trưởng thành hơn Next.js/Webpack}

When to use {Khi nào dùng}

  • Most new projects in 2026 {Hầu hết project mới năm 2026} — this is the default choice {đây là lựa chọn mặc định}
  • React (with React Router or TanStack Router), Vue, Svelte, Solid, Astro
  • Any project that values developer experience {Bất kỳ project nào coi trọng trải nghiệm developer}

When NOT to use {Khi nào KHÔNG dùng}

  • Next.js projects (use Turbopack) {Project Next.js (dùng Turbopack)}
  • Need Module Federation (Webpack’s territory) {Cần Module Federation}
// vite.config.ts — typical setup
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';

export default defineConfig({
  plugins: [react()],
  build: {
    sourcemap: true,
    rollupOptions: {
      output: { manualChunks: { vendor: ['react', 'react-dom'] } },
    },
  },
});

Rollup 4 {Rollup 4}

Architecture {Kiến trúc}

Rollup is the ES module-native bundler {Rollup là bundler native ES module}. It was the first bundler designed around import/export {Nó là bundler đầu tiên thiết kế xung quanh import/export} rather than treating modules as an afterthought {thay vì xem module là thứ phụ}.

ES Module sources → Tree-shaking → Scope hoisting → Output (ESM/CJS/UMD)

Strengths {Điểm mạnh}

  • Best tree-shaking {Tree-shaking tốt nhất}: designed from ground up for dead code elimination {thiết kế từ đầu cho loại bỏ code chết}
  • Smallest output {Output nhỏ nhất}: scope hoisting produces the most compact bundles {scope hoisting tạo bundle nhỏ gọn nhất}
  • Multiple output formats {Nhiều định dạng output}: ESM, CJS, UMD, IIFE from same source {từ cùng source}
  • Clean plugin API {Plugin API sạch}: simpler and more composable than Webpack {đơn giản và có thể kết hợp hơn Webpack}
  • Battle-tested for libraries {Đã thử nghiệm cho thư viện}: most npm packages are built with Rollup {hầu hết package npm được build bằng Rollup}

Weaknesses {Điểm yếu}

  • Slow for large apps {Chậm cho app lớn}: JavaScript-based, doesn’t parallelize well {Dựa trên JavaScript, không song song hoá tốt}
  • No dev server {Không có dev server}: needs additional tooling (hence Vite wrapping it) {cần công cụ thêm (vì vậy Vite bọc nó)}
  • HMR: not built-in {không tích hợp sẵn}
  • Non-ESM transforms: needs plugins for CommonJS, JSON, etc. {cần plugin cho CommonJS, JSON}

When to use {Khi nào dùng}

  • Building npm libraries {Xây dựng thư viện npm} that need small output
  • Need ESM + CJS dual package output {Cần output ESM + CJS đồng thời}
  • Using Vite (which uses Rollup under the hood for production) {Dùng Vite (sử dụng Rollup bên dưới cho production)}

When NOT to use {Khi nào KHÔNG dùng}

  • Application bundling (use Vite instead) {Bundle app (dùng Vite thay thế)}
  • Need fast iteration (no dev server/HMR) {Cần lặp nhanh}
// rollup.config.js — a typical library setup
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import esbuild from 'rollup-plugin-esbuild'; // fast TS transpile

export default {
  input: 'src/index.ts',
  output: [
    { file: 'dist/index.cjs', format: 'cjs' },
    { file: 'dist/index.mjs', format: 'esm' },
  ],
  external: ['react'], // don't bundle peer deps into a library
  plugins: [resolve(), commonjs(), esbuild({ minify: true })],
};

Rolldown {Rolldown}

Architecture {Kiến trúc}

Rolldown is the Rust rewrite of Rollup {Rolldown là Rollup viết lại bằng Rust}, created by the Vite team {được tạo bởi team Vite}. It aims to be a drop-in replacement for Rollup with dramatically better performance {Nó nhắm thay thế Rollup hoàn toàn với performance tốt hơn đáng kể}.

Rollup API (JavaScript) → Rolldown Core (Rust) → Same output quality
                                                   10-30x faster

Strengths {Điểm mạnh}

  • Rollup-compatible API {API tương thích Rollup}: most Rollup plugins work unchanged {hầu hết plugin Rollup hoạt động không thay đổi}
  • 10-30x faster than Rollup {Nhanh hơn Rollup 10-30 lần}: Rust parallelism + zero-copy parsing {Song song Rust + phân tích zero-copy}
  • Built-in transforms {Transform tích hợp}: TypeScript, JSX, minification — no extra plugins needed {không cần plugin thêm}
  • Unified dev/prod {Đồng nhất dev/prod}: same bundler for both in Vite {cùng bundler cho cả hai trong Vite}
  • Tree-shaking: same quality as Rollup {chất lượng như Rollup}

Weaknesses {Điểm yếu}

  • Newer ecosystem {Hệ sinh thái mới}: not all Rollup plugins fully compatible yet {chưa phải tất cả plugin Rollup đều tương thích}
  • Tied to Vite {Gắn với Vite}: primarily designed as Vite’s engine, standalone use is secondary {chủ yếu thiết kế cho Vite, dùng độc lập là phụ}
  • Evolving API {API đang phát triển}: some edge cases still being resolved {một số edge case vẫn đang giải quyết}

When to use {Khi nào dùng}

  • You use Vite → Rolldown is already your production bundler {Bạn dùng Vite → Rolldown đã là bundler production của bạn}
  • Building libraries that previously used Rollup and need faster builds {Xây thư viện trước dùng Rollup và cần build nhanh hơn}

You rarely install Rolldown directly {Bạn hiếm khi cài Rolldown trực tiếp} — you opt into it through Vite {bạn chọn nó thông qua Vite}:

# Use the Rolldown-powered Vite (the default engine going forward)
npm install vite@latest
# or, to try it standalone as a Rollup drop-in:
npm install -D rolldown
// rolldown.config.js — note the API mirrors Rollup almost 1:1
import { defineConfig } from 'rolldown';

export default defineConfig({
  input: 'src/index.ts',
  output: { dir: 'dist', format: 'esm' },
  // TypeScript/JSX transforms are built in — no extra plugins
});

esbuild {esbuild}

Architecture {Kiến trúc}

Written in Go {Viết bằng Go}, esbuild was the tool that started the “fast bundler” revolution {esbuild là tool bắt đầu cuộc cách mạng “bundler nhanh”}. It does bundling, transpiling, and minification in a single pass {Nó bundle, transpile, và minify trong một lượt duy nhất}.

Source → Parse (parallel) → Link → Tree-shake → Code-gen → Output
         All in Go, all in memory, all parallel

Strengths {Điểm mạnh}

  • Extreme speed {Tốc độ cực nhanh}: 10-100x faster than Webpack {nhanh hơn Webpack 10-100 lần}. Bundles React in 40ms {Bundle React trong 40ms}
  • Zero config for simple cases {Không config cho case đơn giản}
  • Built-in TypeScript + JSX {TypeScript + JSX tích hợp}: no babel needed {không cần babel}
  • Minification: fastest minifier available (replaces Terser) {minifier nhanh nhất (thay thế Terser)}
  • CSS bundling: basic but fast {cơ bản nhưng nhanh}
  • Infrastructure role {Vai trò hạ tầng}: powers Vite’s dep pre-bundling, tsup, many other tools {cung cấp sức mạnh cho Vite dep pre-bundling, tsup, nhiều tool khác}

Weaknesses {Điểm yếu}

  • Limited plugin API {Plugin API hạn chế}: can’t do everything Webpack/Rollup plugins can {không làm được mọi thứ plugin Webpack/Rollup có thể}
  • No HMR {Không có HMR}: it’s a bundler, not a dev server {nó là bundler, không phải dev server}
  • Basic code splitting {Code splitting cơ bản}: less sophisticated than Webpack or Rollup {ít tinh vi hơn Webpack hoặc Rollup}
  • No type checking {Không kiểm tra type}: strips types but doesn’t validate them {xoá type nhưng không xác thực}
  • CSS limitations {Giới hạn CSS}: no PostCSS, no CSS Modules (without plugins) {không có PostCSS, CSS Modules}

When to use {Khi nào dùng}

  • Simple apps/scripts {App/script đơn giản} that don’t need complex transforms {không cần transform phức tạp}
  • Build tooling infrastructure {Hạ tầng build tooling}: as a fast transpiler/minifier inside other tools {làm transpiler/minifier nhanh bên trong tool khác}
  • CI pipelines {Pipeline CI}: when build speed is critical {khi tốc độ build quan trọng}
  • TypeScript libraries via tsup (esbuild wrapper) {Thư viện TypeScript qua tsup}

When NOT to use {Khi nào KHÔNG dùng}

  • Need HMR/dev server (use Vite which uses esbuild internally) {Cần HMR/dev server}
  • Complex CSS pipeline (PostCSS, Tailwind) {Pipeline CSS phức tạp}
  • Need advanced code splitting {Cần code splitting nâng cao}
# esbuild — one command does it all
esbuild src/index.tsx --bundle --minify --sourcemap --outdir=dist --format=esm
# Finishes in ~50ms for most projects

Turbopack {Turbopack}

Architecture {Kiến trúc}

Built by Vercel in Rust {Được Vercel xây bằng Rust}, Turbopack is the Next.js-exclusive bundler {là bundler chỉ dành cho Next.js} designed to replace Webpack in the Next.js ecosystem {thiết kế để thay thế Webpack trong hệ sinh thái Next.js}.

Source → Turbo Engine (incremental computation) → Optimized output
         Function-level caching, maximal parallelism
         Only recomputes what changed

Strengths {Điểm mạnh}

  • Incremental by design {Thiết kế tăng dần}: function-level caching means only changed code is reprocessed {cache cấp hàm nghĩa là chỉ code thay đổi được xử lý lại}
  • Fast HMR {HMR nhanh}: maintains state better than Webpack for Next.js {giữ state tốt hơn Webpack cho Next.js}
  • Optimized for React Server Components {Tối ưu cho React Server Component}
  • Deep Next.js integration {Tích hợp sâu Next.js}: understands App Router, Server Actions, etc.
  • Production builds (Next.js 16): stable for both dev and prod {ổn định cho cả dev và prod}

Weaknesses {Điểm yếu}

  • Next.js only {Chỉ Next.js}: cannot be used with other frameworks {không thể dùng với framework khác}
  • Not standalone {Không độc lập}: no CLI, no separate npm package for general use {không có CLI, không có package npm riêng cho sử dụng chung}
  • Less configurable {Ít tuỳ chỉnh}: intentionally opinionated for Next.js {có chủ đích opinionated cho Next.js}
  • Webpack plugin incompatibility {Không tương thích plugin Webpack}: most Webpack plugins don’t work {hầu hết plugin Webpack không hoạt động}
  • Closed ecosystem {Hệ sinh thái đóng}: tied to Vercel’s roadmap {gắn với lộ trình Vercel}

When to use {Khi nào dùng}

  • You use Next.js → Turbopack is the default since v16 {Bạn dùng Next.js → Turbopack là mặc định từ v16}

When NOT to use {Khi nào KHÔNG dùng}

  • Any non-Next.js project {Bất kỳ project không phải Next.js}

Parcel 2 {Parcel 2}

Architecture {Kiến trúc}

Parcel’s philosophy is zero configuration {Triết lý của Parcel là không cần cấu hình}. It auto-detects everything from your source files {Nó tự động phát hiện mọi thứ từ file source}: framework, preprocessors, targets {framework, preprocessor, target}.

Source → Asset Graph (auto-detected) → Transforms (parallel, Rust) → Packager → Output
         HTML entry points                SWC for JS/TS
         package.json targets             PostCSS for CSS
         browserslist for compat          Image optimization

Strengths {Điểm mạnh}

  • True zero-config {Thực sự không cần config}: point at an HTML file, it figures out the rest {trỏ vào file HTML, nó tự tìm ra phần còn lại}
  • Fast: uses Rust (SWC) for transforms, parallel processing {dùng Rust (SWC) cho transform, xử lý song song}
  • Multi-target: can output for multiple environments from one build {có thể output cho nhiều môi trường từ một build}
  • Built-in everything {Mọi thứ tích hợp}: PostCSS, image optimization, tree-shaking, code splitting, HMR
  • HTML entry points {Điểm vào HTML}: unlike most bundlers that start from JS {khác hầu hết bundler bắt đầu từ JS}

Weaknesses {Điểm yếu}

  • Smaller ecosystem {Hệ sinh thái nhỏ hơn}: fewer plugins than Webpack or Vite {ít plugin hơn Webpack hoặc Vite}
  • Less adoption {Ít được sử dụng}: most frameworks chose Vite as their default {hầu hết framework chọn Vite làm mặc định}
  • Config when needed {Config khi cần}: when you DO need customization, the escape hatches are less documented {khi cần tuỳ chỉnh, giải pháp thay thế ít tài liệu hơn}
  • Less predictable {Ít dự đoán được}: auto-detection magic can surprise you {phép thuật tự phát hiện có thể gây bất ngờ}

When to use {Khi nào dùng}

  • Prototypes, demos, quick experiments {Prototype, demo, thử nghiệm nhanh}
  • Vanilla JS/TS projects without a framework {Project vanilla JS/TS không dùng framework}
  • When you want zero setup time {Khi bạn muốn không mất thời gian setup}

When NOT to use {Khi nào KHÔNG dùng}

  • Large team projects (less ecosystem, less community knowledge) {Project team lớn}
  • Framework-based apps (React/Vue/Svelte) — frameworks prefer Vite {App dựa trên framework — framework ưu tiên Vite}
# Parcel — literally just this
parcel src/index.html
# Auto-detects TypeScript, React, PostCSS, everything

SWC {SWC}

Architecture {Kiến trúc}

SWC is not a bundler — it’s a Rust-based compiler {SWC không phải bundler — nó là compiler dựa trên Rust} that replaces Babel and Terser {thay thế Babel và Terser}. It does transpilation, minification, and soon more {Nó làm transpilation, minification, và sắp thêm nữa}.

Source → SWC Core (Rust) → Transformed output
         TypeScript → JavaScript
         JSX → createElement calls
         Modern JS → Target-compatible JS
         Minification (replacing Terser)

Strengths {Điểm mạnh}

  • 20-70x faster than Babel {Nhanh hơn Babel 20-70 lần}: single-threaded; even faster with parallelism {đơn luồng; nhanh hơn nữa với song song}
  • Drop-in Babel replacement {Thay thế Babel hoàn toàn}: supports most Babel transforms {hỗ trợ hầu hết transform của Babel}
  • Used everywhere {Dùng khắp nơi}: Next.js, Vite (plugin-react-swc), Parcel, Deno all use SWC {đều dùng SWC}
  • Minification: 7x faster than Terser with comparable output size {nhanh hơn Terser 7 lần với output size tương đương}

Weaknesses {Điểm yếu}

  • Not a bundler {Không phải bundler}: doesn’t resolve imports, doesn’t do code splitting {không resolve import, không code splitting}
  • Plugin limitations {Hạn chế plugin}: Rust plugin API exists but less accessible than Babel’s JS API {API plugin Rust tồn tại nhưng ít truy cập được hơn API JS của Babel}
  • Some Babel plugins missing {Thiếu một số plugin Babel}: very niche transforms may not be available {transform rất đặc thù có thể không có}

When to use {Khi nào dùng}

  • You’re already using it (via Vite, Next.js, Parcel) {Bạn đã đang dùng nó}
  • Need to replace Babel in a custom pipeline for speed {Cần thay Babel trong pipeline tuỳ chỉnh để nhanh hơn}
  • Need fast minification as Terser alternative {Cần minification nhanh thay thế Terser}
// .swcrc — drop-in Babel replacement config
{
  "jsc": {
    "parser": { "syntax": "typescript", "tsx": true },
    "transform": { "react": { "runtime": "automatic" } },
    "target": "es2022",
    "minify": { "compress": true, "mangle": true }
  }
}

Bun (Bundler) {Bun (Bundler)}

Architecture {Kiến trúc}

Bun is a JavaScript runtime (like Node.js) {Bun là JavaScript runtime (như Node.js)} that includes a bundler as part of its toolchain {bao gồm bundler như phần của chuỗi công cụ}. Written in Zig and C++ for maximum performance {Viết bằng Zig và C++ cho performance tối đa}.

Bun Runtime
├── Package manager (bun install) — faster than npm/pnpm
├── Bundler (bun build) — esbuild-class speed
├── Test runner (bun test)
├── Transpiler (native TS/JSX support)
└── Runtime (replaces Node.js)

Strengths {Điểm mạnh}

  • All-in-one {Tất cả trong một}: runtime + bundler + package manager + test runner {runtime + bundler + quản lý package + test runner}
  • Extremely fast {Cực nhanh}: comparable to esbuild for bundling, faster node_modules install {tương đương esbuild cho bundle, cài node_modules nhanh hơn}
  • Native TypeScript {TypeScript native}: runs .ts files directly without build step {chạy file .ts trực tiếp không cần build}
  • Node.js compatible {Tương thích Node.js}: most npm packages work {hầu hết package npm hoạt động}

Weaknesses {Điểm yếu}

  • Bundler is basic {Bundler cơ bản}: less mature than Vite/Webpack for production apps {ít trưởng thành hơn Vite/Webpack cho app production}
  • No HMR/dev server in bundler {Bundler không có HMR/dev server}
  • Framework support {Hỗ trợ framework}: React/Vue/Svelte frameworks still prefer Vite {framework React/Vue/Svelte vẫn ưu tiên Vite}
  • Compatibility gaps {Khoảng cách tương thích}: some Node.js APIs not yet implemented {một số API Node.js chưa được implement}
  • Production maturity {Độ trưởng thành production}: fewer production deployments to learn from {ít deployment production để học từ}

When to use {Khi nào dùng}

  • Scripts, CLI tools, backend services {Script, tool CLI, service backend}
  • Projects where you want one tool for everything {Project muốn một tool cho mọi thứ}
  • Need fastest possible node_modules install {Cần cài node_modules nhanh nhất có thể}

When NOT to use {Khi nào KHÔNG dùng}

  • Complex frontend apps with framework (use Vite) {App frontend phức tạp với framework}
  • Need production-grade code splitting, CSS pipeline {Cần code splitting cấp production, pipeline CSS}
# Bun bundler
bun build ./src/index.tsx --outdir=./dist --minify --sourcemap=external
# Similar to esbuild but with Bun's runtime features

tsup {tsup}

Architecture {Kiến trúc}

tsup is an opinionated library bundler {tsup là bundler thư viện có chủ kiến} that wraps esbuild {bọc esbuild}. It’s designed for one use case {Nó được thiết kế cho một trường hợp sử dụng}: bundling TypeScript libraries with minimal config {bundle thư viện TypeScript với config tối thiểu}.

Strengths {Điểm mạnh}

  • Library-focused {Tập trung thư viện}: generates CJS + ESM + types in one command {tạo CJS + ESM + types trong một lệnh}
  • Fast {Nhanh}: powered by esbuild {được esbuild cung cấp sức mạnh}
  • DTS generation {Tạo DTS}: automatically generates .d.ts declaration files {tự động tạo file khai báo .d.ts}
  • Zero config {Không cần config}: works with just a package.json entry point {hoạt động chỉ với entry point package.json}

Weaknesses {Điểm yếu}

  • Not for apps {Không cho app}: no dev server, no HMR, no HTML handling {không dev server, HMR, xử lý HTML}
  • Limited transforms {Transform hạn chế}: inherits esbuild’s limitations {kế thừa hạn chế của esbuild}
  • DTS can be slow {DTS có thể chậm}: TypeScript type-checking is still JavaScript-speed {kiểm tra type TypeScript vẫn tốc độ JavaScript}

When to use {Khi nào dùng}

  • Publishing TypeScript libraries to npm {Xuất bản thư viện TypeScript lên npm}
  • Need dual CJS/ESM output with types {Cần output CJS/ESM đồng thời với types}
// tsup.config.ts
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['cjs', 'esm'],
  dts: true,
  clean: true,
  sourcemap: true,
});

unbuild {unbuild}

Architecture {Kiến trúc}

Created by the UnJS team (same people behind Nuxt) {Được tạo bởi team UnJS (cùng team Nuxt)}. unbuild wraps Rollup for bundling and mkdist for stub-based builds {bọc Rollup cho bundling và mkdist cho build dạng stub}.

Strengths {Điểm mạnh}

  • Passive build mode {Chế độ build thụ động}: “stub” mode links to source during development, only bundles for publish {chế độ “stub” liên kết với source khi dev, chỉ bundle khi publish}
  • Auto-inferred config {Config tự suy luận}: reads package.json exports to determine entry points {đọc exports package.json để xác định entry point}
  • Dual CJS/ESM {CJS/ESM đồng thời}: automatic format detection {phát hiện format tự động}

Weaknesses {Điểm yếu}

  • Niche {Chuyên biệt}: designed specifically for universal JS libraries {thiết kế riêng cho thư viện JS universal}
  • Less known {Ít được biết}: smaller community than tsup {cộng đồng nhỏ hơn tsup}
  • Slower than tsup {Chậm hơn tsup}: uses Rollup (JS) instead of esbuild (Go) {dùng Rollup (JS) thay vì esbuild (Go)}

When to use {Khi nào dùng}

  • Building universal JS/TS libraries (CJS + ESM) {Xây thư viện JS/TS universal}
  • Want stub-based development workflow {Muốn workflow dev dạng stub}
  • UnJS/Nuxt ecosystem packages {Package hệ sinh thái UnJS/Nuxt}
// build.config.ts — entries are often auto-inferred from package.json
import { defineBuildConfig } from 'unbuild';

export default defineBuildConfig({
  entries: ['src/index'],
  declaration: true, // emit .d.ts
  clean: true,
  rollup: { emitCJS: true }, // dual CJS + ESM
});

The “stub” mode is the killer feature {Chế độ “stub” là tính năng đắt giá nhất}: unbuild --stub writes a tiny re-export to source instead of bundling {ghi một file re-export nhỏ trỏ về source thay vì bundle}, so library changes are reflected instantly while developing a monorepo {nên thay đổi thư viện phản ánh tức thì khi dev trong monorepo}.


Speed Comparison {So sánh tốc độ}

Benchmark: bundling a medium React app (500 modules, 50K lines) {Benchmark: bundle app React trung bình (500 module, 50K dòng)}:

ToolCold Build {Build lạnh}Hot Rebuild {Rebuild nóng}Dev Startup {Khởi động dev}
Webpack 5~18s~2s~8s
Vite 6 (Rolldown)~1.2s~0.1s~300ms
Rollup 4~8s~3sN/A (no dev server)
esbuild~0.4s~0.05sN/A
Turbopack~1.5s~0.08s~400ms
Parcel 2~3s~0.3s~1.5s
Bun build~0.5s~0.06sN/A

Cold build time, drawn to scale (each ≈ 0.5s) {Thời gian build lạnh, vẽ theo tỉ lệ (mỗi ≈ 0.5s)}:

Cold build (lower = faster)
esbuild     █                                  0.4s
Bun         █                                   0.5s
Vite 6      ██                                  1.2s
Turbopack   ███                                 1.5s
Parcel 2    ██████                              3.0s
Rollup 4    ████████████████                    8.0s
Webpack 5   ████████████████████████████████   18.0s
            └── native (Go/Rust/Zig) ──┘  └── JavaScript ──┘

Hot rebuild — the number you feel hundreds of times a day {Rebuild nóng — con số bạn cảm nhận hàng trăm lần mỗi ngày}:

Hot rebuild (lower = faster)
esbuild     ▏  0.05ms-class   (instant)
Bun         ▏  ~0.06s
Turbopack   ▏  ~0.08s  ← incremental, function-level cache
Vite 6      ▏  ~0.1s
Parcel 2    ██ ~0.3s
Webpack 5   ████████████████ ~2s
Rollup 4    ██████████████████████ ~3s (no incremental dev)

Key insight {Nhận thức quan trọng}: native tools (Rust/Go/Zig) are 10-40x faster than JavaScript-based tools {tool native nhanh hơn tool JavaScript 10-40 lần}. This is the primary reason for the migration {Đây là lý do chính cho sự di chuyển}.

Output size also matters {Kích thước output cũng quan trọng}

Speed is not everything {Tốc độ không phải tất cả} — for libraries and ship-to-user bundles, smaller output wins {với thư viện và bundle gửi tới user, output nhỏ hơn thắng}. Same React app, minified + gzipped {Cùng app React, minify + gzip}:

Final bundle size (lower = better)
Rollup 4    ████████████████              ~142 KB  ← best scope hoisting
Rolldown    ████████████████▏             ~143 KB
Vite 6      ████████████████▏             ~144 KB  (Rolldown under the hood)
esbuild     ██████████████████            ~158 KB  (looser minify/splitting)
Webpack 5   ███████████████████           ~165 KB  (runtime overhead)

Rollup-family tools still produce the leanest output {Nhóm Rollup vẫn cho output gọn nhất} thanks to mature scope hoisting and tree-shaking {nhờ scope hoisting và tree-shaking trưởng thành}; esbuild trades a few KB for raw speed {esbuild đánh đổi vài KB lấy tốc độ}.


The Ecosystem Relationships {Mối quan hệ hệ sinh thái}

Understanding which tools use which is crucial {Hiểu tool nào dùng tool nào là rất quan trọng}:

┌─────────────────────────────────────────────────────────────────────────┐
│                         Framework Layer                                  │
│  Next.js (Turbopack) │ Nuxt (Vite) │ Astro (Vite) │ SvelteKit (Vite)  │
├─────────────────────────────────────────────────────────────────────────┤
│                         Bundler Layer                                    │
│  Webpack 5 │ Vite 6 │ Turbopack │ Parcel 2                             │
├─────────────────────────────────────────────────────────────────────────┤
│                         Engine Layer                                     │
│  Rolldown (Rust)  │  esbuild (Go)  │  SWC (Rust)                       │
│  └─ Vite prod     │  └─ Vite deps  │  └─ Next.js, Vite, Parcel         │
│                   │  └─ tsup       │  └─ Transpile + Minify             │
└─────────────────────────────────────────────────────────────────────────┘
  • Vite uses esbuild (dep pre-bundling) + Rolldown (production bundling) + SWC (transpilation via plugin)
  • Next.js uses Turbopack (bundling) + SWC (transpilation)
  • Parcel uses SWC (transpilation) + custom Rust core (bundling)
  • tsup uses esbuild (everything)

Decision Flowchart {Sơ đồ quyết định}

What are you building?
{Bạn đang xây gì?}

├── Web application {Ứng dụng web}
│   ├── Using Next.js? → Turbopack (default, no choice needed)
│   ├── Using other framework (React Router, Vue, Svelte, Astro)?
│   │   └── Vite (industry standard)
│   ├── Vanilla JS/TS, want zero config?
│   │   └── Parcel (auto-detects everything)
│   └── Legacy app with Webpack?
│       └── Keep Webpack (migration cost > benefit for now)

├── npm library {Thư viện npm}
│   ├── TypeScript, simple output?
│   │   └── tsup (fast, minimal config)
│   ├── Need tree-shaking + multiple formats?
│   │   └── Rollup or Rolldown
│   └── Universal library (UnJS ecosystem)?
│       └── unbuild

├── Script / CLI tool {Script / tool CLI}
│   ├── Want all-in-one runtime?
│   │   └── Bun (runtime + bundler + test)
│   └── Just need fast bundling?
│       └── esbuild (one command)

└── Custom build pipeline {Pipeline build tuỳ chỉnh}
    ├── Need transpilation speed?
    │   └── SWC (replace Babel)
    └── Need everything + existing Webpack ecosystem?
        └── Webpack 5 (last resort)

Migration Guide {Hướng dẫn di chuyển}

Webpack → Vite {Webpack sang Vite}

The most common migration in 2026 {Di chuyển phổ biến nhất năm 2026}:

Webpack conceptVite equivalent
webpack.config.jsvite.config.ts
entryHTML file with <script type="module">
Loaders (babel-loader, css-loader)Built-in transforms + plugins
MiniCssExtractPluginBuilt-in CSS handling
DefinePlugindefine in config
devServerBuilt-in dev server
HtmlWebpackPluginUse HTML file as entry
resolve.aliasresolve.alias (same concept)
require.contextimport.meta.glob()

Migration steps {Các bước di chuyển}:

  1. Install Vite + framework plugin {Cài Vite + plugin framework}
  2. Create index.html as entry point {Tạo index.html là điểm vào}
  3. Move env variables to import.meta.env {Chuyển biến env sang import.meta.env}
  4. Replace require() with import {Thay require() bằng import}
  5. Replace Webpack-specific syntax (require.contextimport.meta.glob) {Thay cú pháp riêng Webpack}
  6. Test, fix, iterate {Test, sửa, lặp}

Part 2 — Package Managers {Phần 2 — Package Manager}

A build tool is useless without dependencies, and a package manager is what puts them on disk {Build tool vô dụng nếu không có dependency, và package manager là thứ đặt chúng lên disk}. It does three jobs {Nó làm ba việc}:

  1. Resolve the dependency graph from package.json + a lockfile {Resolve đồ thị dependency từ package.json + lockfile}
  2. Fetch packages from a registry (npm registry by default) into a cache {Fetch package từ registry (mặc định npm registry) vào cache}
  3. Link them into a layout your runtime/bundler can resolve (node_modules or Plug’n’Play) {Link chúng thành layout mà runtime/bundler resolve được (node_modules hoặc Plug’n’Play)}

The whole rivalry between npm, Yarn, pnpm and Bun comes down to how they do step 3 {Toàn bộ cuộc đua giữa npm, Yarn, pnpm và Bun nằm ở cách chúng làm bước 3} — and how fast they do all three {và tốc độ làm cả ba}.

The node_modules problem {Vấn đề node_modules}

Early npm nested dependencies literally {npm thời đầu lồng dependency theo đúng nghĩa đen}, producing infamous Windows path-length crashes {tạo ra lỗi vượt độ dài path khét tiếng trên Windows}. The fix was hoisting {Cách sửa là hoisting}: flatten everything to the top of node_modules {làm phẳng mọi thứ lên đầu node_modules}. That solved depth but created two new problems {Việc đó sửa độ sâu nhưng tạo hai vấn đề mới}:

Flat / hoisted node_modules (npm, Yarn Classic)
{node_modules phẳng / hoisted}

node_modules/
├── react/                ← your direct dep
├── lodash/               ← HOISTED — a transitive dep of something else
└── ...                   ← thousands of packages, all flat

Problem 1 — Phantom dependencies {Dependency ma}:
  your code can `import lodash` even though it is NOT in your
  package.json. It works by accident, breaks when the hoist changes.

Problem 2 — Doppelgangers / duplication {Trùng lặp}:
  app needs lodash@4, a dep needs lodash@3 → BOTH copied to disk,
  sometimes many times across the tree.

pnpm and Yarn Berry were built specifically to kill these two problems {pnpm và Yarn Berry sinh ra để diệt đúng hai vấn đề này}.


Package Manager Overview {Tổng quan Package Manager}

ManagerLanguage {Ngôn ngữ}node_modules strategy {Chiến lược}LockfileDefault since {Có từ}
npmJavaScriptFlat / hoisted {Phẳng / hoisted}package-lock.jsonShips with Node.js {Đi kèm Node.js}
Yarn Classic (v1)JavaScriptFlat / hoisted {Phẳng / hoisted}yarn.lock2016 (maintenance mode {chế độ bảo trì})
Yarn Berry (v2-v4)JavaScriptPlug’n’Play (no node_modules) or linker {PnP hoặc linker}yarn.lock + .pnp.cjs2020
pnpmTypeScript + Rust (parts)Symlinks → global content-addressable store {Symlink → store}pnpm-lock.yaml2017
BunZig + C++Hoisted + global hardlink cache {Hoisted + cache hardlink}bun.lock (text) {văn bản}2023

Three ways to lay out dependencies {Ba cách bố trí dependency}

1. FLAT / HOISTED  (npm, Yarn Classic, Bun)
   node_modules/{react, lodash, …}  ← one big flat folder, copied per project
   ✓ simple resolution · ✗ phantom deps · ✗ disk duplication across projects

2. CONTENT-ADDRESSABLE STORE + SYMLINKS  (pnpm)
   ~/.pnpm-store/<hash>/…            ← every version stored ONCE, globally
   node_modules/.pnpm/…              ← real files (hardlinked from store)
   node_modules/react → symlink      ← only your DIRECT deps are visible
   ✓ no phantom deps · ✓ near-zero extra disk per project · ✓ fast

3. PLUG'N'PLAY  (Yarn Berry)
   .pnp.cjs                          ← a resolver map; NO node_modules at all
   .yarn/cache/*.zip                 ← deps stay zipped on disk
   ✓ instant installs · ✓ strict · ✗ needs PnP-aware tooling

npm {npm}

Architecture {Kiến trúc}

npm is the default {npm là mặc định} — it ships with Node.js, so it is the baseline every other tool is measured against {nó đi kèm Node.js, nên là chuẩn để mọi tool khác đối chiếu}. It installs a flat, hoisted node_modules and records exact versions in package-lock.json {Nó cài node_modules phẳng, hoisted và ghi version chính xác vào package-lock.json}.

package.json + package-lock.json
        │  npm install

resolve graph → fetch to ~/.npm cache → hoist & copy into node_modules/

Strengths {Điểm mạnh}

  • Zero setup {Không cần cài đặt}: already on every machine with Node.js {đã có sẵn trên mọi máy có Node.js}
  • Maximum compatibility {Tương thích tối đa}: every tool, CI, and tutorial assumes npm {mọi tool, CI, tutorial đều mặc định npm}
  • npx {npx}: run a package binary without installing it globally {chạy binary của package mà không cài global}
  • Workspaces {Workspaces}: built-in monorepo support since v7 {hỗ trợ monorepo tích hợp từ v7}
  • Caching + offline {Cache + offline}: a shared content cache with --prefer-offline {cache nội dung dùng chung}

Weaknesses {Điểm yếu}

  • Slowest of the four {Chậm nhất trong bốn}: still copies files per project {vẫn copy file theo từng project}
  • Phantom dependencies {Dependency ma}: hoisting lets you import undeclared packages {hoisting cho phép import package chưa khai báo}
  • Disk duplication {Trùng lặp disk}: 10 projects = 10 full copies of node_modules {10 project = 10 bản copy đầy đủ}
  • No strictness {Không nghiêm ngặt}: the loose layout hides dependency mistakes until they break {layout lỏng lẻo giấu lỗi dependency đến khi vỡ}

When to use {Khi nào dùng}

  • Default for small/simple projects and quick experiments {Mặc định cho project nhỏ/đơn giản và thử nghiệm nhanh}
  • Maximum-compatibility environments (legacy CI, locked toolchains) {Môi trường cần tương thích tối đa}
npm install                 # install from package-lock.json
npm ci                      # clean, reproducible install for CI {cài sạch cho CI}
npm install react           # add a dependency
npx vite                    # run a binary without a global install

Yarn (Classic + Berry) {Yarn (Classic + Berry)}

Architecture {Kiến trúc}

Yarn comes in two very different generations {Yarn có hai thế hệ rất khác nhau}. Classic (v1) is a faster, deterministic npm clone with the same flat node_modules {Classic (v1) là bản npm clone nhanh hơn, xác định hơn, cùng node_modules phẳng}. Berry (v2-v4) is a rewrite around Plug’n’Play (PnP) {Berry (v2-v4) là bản viết lại quanh Plug’n’Play (PnP)}: no node_modules at all {không có node_modules luôn} — instead a .pnp.cjs resolver maps every import to a zipped package in .yarn/cache {thay vào đó .pnp.cjs ánh xạ mọi import tới package nén trong .yarn/cache}.

Yarn Classic:  yarn.lock → flat node_modules (like npm, but faster)
Yarn Berry:    yarn.lock + .pnp.cjs → NO node_modules
               imports resolved through PnP map → .yarn/cache/*.zip

Strengths {Điểm mạnh}

  • PnP = instant + strict {PnP = tức thì + nghiêm ngặt}: no file copying, and phantom deps are impossible {không copy file, và dependency ma là không thể}
  • Zero-installs {Zero-install}: commit .yarn/cache and clones run with no install step {commit .yarn/cache thì clone về chạy luôn không cần install}
  • Plugin system {Hệ thống plugin}: extensible CLI (constraints, version policies) {CLI mở rộng được}
  • Great workspaces {Workspaces tốt}: mature monorepo tooling {tooling monorepo trưởng thành}
  • Classic still everywhere {Classic vẫn phổ biến}: huge install base, very stable {lượng cài đặt lớn, rất ổn định}

Weaknesses {Điểm yếu}

  • PnP compatibility friction {Ma sát tương thích PnP}: tools that scan node_modules directly may need patches {tool quét trực tiếp node_modules có thể cần vá}
  • Two worlds {Hai thế giới}: Classic and Berry feel like different tools — migration is real work {Classic và Berry như hai tool khác nhau — migrate là việc thật}
  • Classic is in maintenance {Classic đã ngừng phát triển}: no new features, the team is on Berry {không thêm tính năng mới}
  • node-modules linker {Linker node-modules}: many teams disable PnP and lose half the benefit {nhiều team tắt PnP và mất nửa lợi ích}

When to use {Khi nào dùng}

  • Berry when you want strictness + zero-installs and your toolchain is PnP-ready {Berry khi muốn nghiêm ngặt + zero-install và toolchain sẵn sàng PnP}
  • Classic only to maintain existing v1 repos {Classic chỉ để bảo trì repo v1 cũ}
yarn                        # install (Classic or Berry)
yarn add react              # add a dependency
yarn dlx vite               # Berry's npx equivalent {tương đương npx}
yarn workspaces foreach run build   # run a script across the monorepo

pnpm {pnpm}

Architecture {Kiến trúc}

pnpm (“performant npm”) keeps a single global content-addressable store {pnpm giữ một store địa chỉ-theo-nội-dung toàn cục duy nhất} where each package version is stored exactly once {nơi mỗi version package được lưu đúng một lần}. Each project’s node_modules is built from hardlinks to that store, plus symlinks that expose only your declared dependencies {node_modules của mỗi project dựng từ hardlink tới store, cộng symlink chỉ phơi bày dependency bạn đã khai báo}.

~/.local/share/pnpm/store/   ← global store: react@19 stored ONCE
        │ hardlink (no copy, no extra disk)

project/node_modules/.pnpm/react@19/…   ← real files
project/node_modules/react → symlink ───┘   ← only DIRECT deps visible

Strengths {Điểm mạnh}

  • Disk efficient {Tiết kiệm disk}: 10 projects share one store — near-constant total size {10 project chia sẻ một store — tổng dung lượng gần như không đổi}
  • Strict by default {Nghiêm ngặt mặc định}: symlinked layout makes phantom deps impossible {layout symlink khiến dependency ma là không thể}
  • Fast {Nhanh}: hardlinking beats copying; warm installs are very quick {hardlink nhanh hơn copy; install nóng rất nhanh}
  • First-class monorepos {Monorepo hạng nhất}: pnpm-workspace.yaml, filtering, --filter task running {lọc và chạy task theo --filter}
  • npm-compatible {Tương thích npm}: same commands, same registry, real node_modules {cùng lệnh, cùng registry, node_modules thật}

Weaknesses {Điểm yếu}

  • Symlinks surprise some tools {Symlink làm vài tool bất ngờ}: rare bundlers/packagers that don’t follow symlinks need config {vài bundler hiếm không theo symlink cần config}
  • Stricter = more upfront fixes {Nghiêm ngặt = sửa nhiều lúc đầu}: migrating reveals previously hidden phantom deps {migrate lộ ra dependency ma từng bị ẩn}
  • Less default than npm {Ít mặc định hơn npm}: must be installed/enabled (via Corepack) {phải cài/bật (qua Corepack)}

When to use {Khi nào dùng}

  • Most teams in 2026 — the sweet spot of speed, disk, and correctness {Hầu hết team năm 2026 — điểm cân bằng giữa tốc độ, disk và đúng đắn}
  • Monorepos of any size {Monorepo mọi kích thước}
  • CI where disk and install time are billed {CI nơi disk và thời gian install bị tính tiền}
corepack enable             # activate pnpm/yarn shipped with Node {bật pnpm/yarn}
pnpm install                # install using the global store
pnpm add react              # add a dependency
pnpm -r run build           # run "build" in every workspace package
pnpm --filter web dev       # target a single workspace {nhắm một workspace}

Bun (Package Manager) {Bun (Package Manager)}

Architecture {Kiến trúc}

Beyond the bundler covered in Part 1, bun install is a standalone package manager {Ngoài bundler đã nói ở Phần 1, bun install là một package manager độc lập} written in Zig {viết bằng Zig}. It is the fastest installer available {Nó là installer nhanh nhất hiện có}, using a global hardlink cache and a binary-then-text lockfile {dùng cache hardlink toàn cục và lockfile}. Since Bun 1.2 the default lockfile is the text bun.lock (the old binary bun.lockb is legacy) {Từ Bun 1.2 lockfile mặc định là bun.lock dạng văn bản (bun.lockb nhị phân cũ là legacy)}.

bun install
  → resolve → global cache (~/.bun/install/cache, hardlinked)
  → hoisted node_modules (npm-compatible layout) → bun.lock (text)
  Often 10-25x faster than npm on a cold install.

Strengths {Điểm mạnh}

  • Fastest installs, by a lot {Install nhanh nhất, cách biệt lớn}: routinely 10-25x faster than npm cold {thường nhanh hơn npm 10-25 lần khi cold}
  • Drop-in {Thay thế trực tiếp}: reads package.json, produces npm-compatible node_modules {đọc package.json, tạo node_modules tương thích npm}
  • All-in-one {Tất cả trong một}: same binary is your runtime, bundler, test runner, and PM {cùng binary là runtime, bundler, test runner và PM}
  • Workspaces {Workspaces}: monorepo support built in {hỗ trợ monorepo tích hợp}
  • bunx {bunx}: fast npx equivalent {tương đương npx nhưng nhanh}

Weaknesses {Điểm yếu}

  • Younger ecosystem {Hệ sinh thái non hơn}: fewer years of edge-case hardening than npm/Yarn {ít năm tôi luyện edge-case hơn npm/Yarn}
  • Hoisted layout {Layout hoisted}: shares npm’s phantom-dependency looseness (not strict like pnpm) {chia sẻ sự lỏng lẻo dependency-ma của npm (không nghiêm ngặt như pnpm)}
  • Best inside Bun’s world {Tốt nhất trong thế giới Bun}: some advantages assume you also use the Bun runtime {vài lợi thế giả định bạn cũng dùng Bun runtime}
  • Occasional compatibility gaps {Đôi khi có khoảng cách tương thích}: rare native/postinstall edge cases {vài edge case native/postinstall hiếm}

When to use {Khi nào dùng}

  • You want the fastest possible installs in dev and CI {Bạn muốn install nhanh nhất có thể ở dev và CI}
  • Projects already using the Bun runtime/bundler {Project đã dùng Bun runtime/bundler}
  • Greenfield apps comfortable with a newer tool {App mới sẵn sàng dùng tool mới hơn}
bun install                 # blazing-fast install → bun.lock
bun add react               # add a dependency
bun run build               # run a package.json script
bunx vite                   # npx equivalent {tương đương npx}

Install Speed & Disk {Tốc độ Install & Disk}

Benchmark intent: a medium app (~1,200 dependencies) {Ý nghĩa benchmark: app trung bình (~1.200 dependency)}. Numbers are illustrative orders of magnitude {Con số mang tính bậc độ lớn minh hoạ}, not lab-precise — real results vary by network, disk, and cache state {không phải chính xác phòng lab — kết quả thật thay đổi theo mạng, disk và trạng thái cache}.

Cold install — no cache, no lockfile shortcuts {Install lạnh — không cache, không lockfile tắt}:

Cold install (lower = faster)
Bun           ███                          ~8s
Yarn Berry    ████████                     ~20s   (PnP, no node_modules copy)
pnpm          ██████████                   ~25s
Yarn Classic  ████████████                 ~30s
npm           ████████████████             ~40s

Warm install — cache primed, lockfile present (the everyday number) {Install nóng — đã có cache + lockfile (con số hằng ngày)}:

Warm install (lower = faster)
Bun           ▏ ~1s
Yarn Berry    █  ~2s   (zero-installs → can be ~0s)
pnpm          ██ ~3s
Yarn Classic  ██████ ~6s
npm           ██████████ ~10s

Disk usage across 10 projects that share most dependencies {Dung lượng disk qua 10 project chia sẻ phần lớn dependency} — this is where layout strategy dominates {đây là nơi chiến lược layout quyết định}:

Manager1 project10 projects {10 project}Why {Vì sao}
npm~1.0x~10xFull copy per project {Copy đầy đủ mỗi project}
Yarn Classic~1.0x~10xFull copy per project {Copy đầy đủ mỗi project}
Bun~1.0x~1-2xGlobal hardlink cache {Cache hardlink toàn cục}
pnpm~1.0x~1-2xOne global store, hardlinked {Một store toàn cục, hardlink}
Yarn Berry (PnP)small {nhỏ}small {nhỏ}Zipped cache, no node_modules {Cache nén, không node_modules}

Takeaway {Điểm rút ra}: Bun wins raw speed {Bun thắng tốc độ thô}; pnpm and Yarn Berry win on disk and correctness {pnpm và Yarn Berry thắng về disk và đúng đắn}; npm wins on ubiquity {npm thắng về phổ biến}.


Lockfiles Compared {So sánh Lockfile}

The lockfile pins the exact resolved tree so installs are reproducible {Lockfile ghim chính xác cây đã resolve để install tái lập được}. Never hand-edit it; commit it {Đừng sửa tay; hãy commit nó}.

ManagerLockfileFormatNotes {Ghi chú}
npmpackage-lock.jsonJSONVerbose but universal {Dài dòng nhưng phổ quát}
Yarn Classicyarn.lockCustom text {Văn bản riêng}Human-readable {Đọc được}
Yarn Berryyarn.lock (+ .pnp.cjs)YAML-ishPairs with the PnP map {Đi cùng map PnP}
pnpmpnpm-lock.yamlYAMLCompact, diff-friendly {Gọn, dễ diff}
Bunbun.lockText (since 1.2) {Văn bản (từ 1.2)}Replaces binary bun.lockb {Thay bun.lockb nhị phân}

Golden rules {Quy tắc vàng}: commit exactly one lockfile {commit đúng một lockfile}; use npm ci / pnpm i --frozen-lockfile / yarn install --immutable in CI for reproducible builds {dùng các lệnh frozen trong CI để build tái lập}; never mix two managers in one repo {đừng trộn hai manager trong một repo}.


Monorepos & Workspaces {Monorepo & Workspaces}

All four support workspaces — multiple packages in one repo sharing a single install {Cả bốn đều hỗ trợ workspaces — nhiều package trong một repo dùng chung một lần install} — but maturity differs {nhưng độ trưởng thành khác nhau}:

ManagerWorkspace configFiltered tasks {Task lọc}Verdict {Kết luận}
npmworkspaces in package.json-w, --workspaceWorks, basic {Chạy được, cơ bản}
Yarn Berryworkspaces fieldworkspaces foreach, constraintsPowerful {Mạnh}
pnpmpnpm-workspace.yaml--filter, -rBest-in-class {Tốt nhất}
Bunworkspaces field--filterFast, newer {Nhanh, mới hơn}

For monorepos, pnpm is the common default in 2026 {Với monorepo, pnpm là mặc định phổ biến năm 2026} thanks to strict isolation + --filter ergonomics {nhờ cô lập nghiêm ngặt + công thái học --filter}, often paired with Turborepo or Nx for task caching {thường ghép với Turborepo hoặc Nx để cache task}.


Package Manager Decision Flowchart {Sơ đồ quyết định Package Manager}

Which package manager?
{Dùng package manager nào?}

├── Brand-new project, want a safe modern default? {Project mới, muốn mặc định hiện đại an toàn?}
│   └── pnpm (fast, disk-efficient, strict, great monorepos)

├── A monorepo / many packages? {Monorepo / nhiều package?}
│   └── pnpm (--filter) ─ optionally + Turborepo/Nx for caching

├── Absolute fastest installs, OK with a newer tool? {Install nhanh tuyệt đối, chấp nhận tool mới?}
│   └── Bun (especially if already using the Bun runtime)

├── Want strict + zero-installs and your tooling is PnP-ready? {Muốn nghiêm ngặt + zero-install?}
│   └── Yarn Berry (PnP)

├── Maximum compatibility / locked CI / tiny project? {Tương thích tối đa / CI khoá / project nhỏ?}
│   └── npm (it's already there)

└── Maintaining an existing repo? {Bảo trì repo có sẵn?}
    └── Keep its current manager — don't mix lockfiles {Giữ manager hiện tại — đừng trộn lockfile}

Tip {Mẹo}: use Corepack (ships with Node.js) to pin the package manager per project via the packageManager field in package.json {dùng Corepack (đi kèm Node.js) để ghim package manager cho từng project qua field packageManager trong package.json} — everyone on the team then uses the exact same version {cả team dùng đúng cùng một version}.


The Future (2026-2027) {Tương lai (2026-2027)}

  1. JavaScript tooling dies {Tooling JavaScript chết}: all performance-critical tools are now Rust/Go/Zig {tất cả tool quan trọng performance bây giờ là Rust/Go/Zig}
  2. Bundler convergence {Hội tụ bundler}: Vite (via Rolldown) handles 80%+ of projects {Vite xử lý hơn 80% project}
  3. Framework-owned bundlers {Bundler thuộc framework}: Next.js (Turbopack), Angular (esbuild), Nuxt/Astro/Svelte (Vite) {mỗi framework sở hữu bundler riêng}
  4. Zero-config becomes standard {Không config trở thành chuẩn}: no one writes 200-line configs anymore {không ai viết config 200 dòng nữa}
  5. Dev/prod unification {Đồng nhất dev/prod}: same bundler for both (Rolldown in Vite, Turbopack in Next) {cùng bundler cho cả hai}
  6. Package managers consolidate {Package manager hội tụ}: npm stays the default-by-default, but pnpm is now the team standard and Bun keeps pushing install speed; Corepack pins the choice per repo {npm vẫn là mặc-định-của-mặc-định, nhưng pnpm đã thành chuẩn của team và Bun tiếp tục đẩy tốc độ install; Corepack ghim lựa chọn cho từng repo}

What to learn {Nên học gì}

If you’re starting today {Nếu bạn bắt đầu hôm nay}:

  1. Vite — the universal standard {tiêu chuẩn phổ quát}. Know it deeply. {Hiểu sâu.}
  2. esbuild — for scripts, CLI tools, and understanding how fast tools work {cho script, CLI, và hiểu cách tool nhanh hoạt động}
  3. Webpack — not to use, but to understand when you encounter legacy projects {không để dùng, mà để hiểu khi gặp project cũ}

Quick Reference Card {Thẻ tham khảo nhanh}

Scenario {Tình huống}Choose {Chọn}Why {Vì sao}
New React/Vue/Svelte appViteFast, simple, ecosystem standard {Nhanh, đơn giản, chuẩn hệ sinh thái}
New Next.js appTurbopackDefault, no choice needed {Mặc định}
Publish TS library to npmtsupFastest DX for library authors {DX nhanh nhất cho tác giả thư viện}
Quick prototypeParcelZero config, instant start {Không config, khởi động tức thì}
Large legacy appWebpack (keep)Migration cost too high {Chi phí di chuyển quá cao}
Full-stack scriptBunAll-in-one, native TS {Tất cả trong một}
Replace Babel in pipelineSWC20-70x faster, compatible {Nhanh hơn, tương thích}
Custom fast bundlingesbuildSimplest CLI, Go-speed {CLI đơn giản nhất, tốc độ Go}
Universal JS libraryunbuildStub mode + auto config {Chế độ stub + auto config}

Package manager pick {Chọn package manager}

Scenario {Tình huống}Choose {Chọn}Why {Vì sao}
New project, modern defaultpnpmFast, disk-efficient, strict {Nhanh, tiết kiệm disk, nghiêm ngặt}
Monorepo / many packagespnpm (+ Turborepo)Best --filter + isolation {--filter + cô lập tốt nhất}
Fastest installsBun10-25x faster cold {Nhanh hơn 10-25 lần khi cold}
Strict + zero-installsYarn Berry (PnP)No node_modules, committable cache {Không node_modules, cache commit được}
Maximum compatibilitynpmShips with Node, assumed everywhere {Đi kèm Node, mặc định khắp nơi}
Existing repoKeep current {Giữ hiện tại}Never mix lockfiles {Đừng trộn lockfile}