vite-mastery

4.1 · difficulty 2/4 · 10 min read

Hooks Panorama

A complete overview of all Vite/Rolldown hooks. Where are the boundaries between universal build hooks, Vite-specific hooks, and Rolldown extension hooks? What is the execution order?

Vite 8.1Stable

Three Categories

Vite hooks come from three layers:

CategorySourceWhere it runs
Universal Build HooksRollup / Rolldown APIRuns in both dev and build
Vite-Specific HooksVite extensions on top of RollupOnly effective in a Vite environment
Rolldown Extension HooksRolldown extensions on top of RollupVite 8+ and standalone Rolldown usage

Universal build hooks are part of the standard Rollup API — plugins written using only these hooks can theoretically be reused directly in a Rollup project.

Vite-specific hooks are added by Vite itself, covering concepts like the dev server, HTML processing, and HMR that Rollup has no notion of.

Rolldown extension hooks are capabilities that Rolldown adds on top of the Rollup-compatible API, such as access to the OXC AST and multi-environment annotations.

Hook Execution Order During Dev Server Startup

text
Vite Dev Server startup sequence:

config ─────────────────────── Before user config is read (can be modified)
configResolved ─────────────── Config is finalized (read-only)
configureServer ────────────── After dev server is created (can add middleware)

─── Waiting for browser requests ───

(For each module request):
resolveId ──────────────────── Resolve module path
load ───────────────────────── Read module contents
transform ──────────────────── Transform module code

(On file change):
handleHotUpdate ────────────── HMR update handling

(On HTML request):
transformIndexHtml ─────────── Modify index.html

Hook Execution Order During Build

text
Vite Build sequence:

config / configResolved ─────── Same as dev

options ─────────────────────── Read/modify Rolldown input options
buildStart ──────────────────── Build starts (initialization)

(For each module):
resolveId ──────────────────────
load ────────────────────────── } Build the module graph
transform ──────────────────────
moduleParsed ───────────────────

buildEnd ────────────────────── Module graph construction complete

renderStart ─────────────────── Begin generating output files
renderChunk ─────────────────── Transform each chunk
generateBundle ──────────────── All chunks have been generated

writeBundle ─────────────────── Chunks written to disk
closeBundle ─────────────────── Entire process complete

Panorama (Interactive)

Click any hook to see its details:

Hook timing browser

Click a hook on the left to inspect details

Hook Calling Convention Quick Reference

HookConventionMeaning
buildStartparallelAll plugins' hooks execute concurrently
resolveIdfirstThe first plugin to return non-null wins
loadfirstSame as above
transformsequentialCode is passed through plugins in order
moduleParsedparallelExecutes concurrently
buildEndparallelExecutes concurrently
renderChunksequentialProcesses chunks in order
generateBundlesequentialExecutes in order
writeBundleparallelExecutes concurrently
closeBundleparallelExecutes concurrently
configsequentialExecutes in order; return values are merged
configResolvedparallelExecutes concurrently
configureServersequentialExecutes in order
transformIndexHtmlsequentialExecutes in order
handleHotUpdatesequentialExecutes in order

Key distinctions:

  • parallel: All plugins' hooks for this call execute simultaneously without waiting for each other
  • sequential: Executes in plugin array order; the next plugin only runs after the previous one finishes
  • first: Executes in order; the first plugin to return a non-null/undefined value wins, and subsequent plugins are skipped

Which Hooks Fire in Each Phase

Hookdevbuild
config
configResolved
configureServer
configurePreviewServerpreview only
options
buildStart✅ (Vite 8)
resolveId
load
transform
buildEnd
renderChunk
generateBundle
writeBundle
closeBundle✅ (on close)
transformIndexHtml
handleHotUpdate

Self-check

  1. What calling conventions do resolveId and transform use respectively? Why are they different?
  2. Does configureServer fire during pnpm build? If your plugin needs to initialize during build, which hook should you use?
  3. What is the difference in timing between buildEnd and closeBundle?
  4. In Vite 7, what problem would a plugin that relies on buildStart for global initialization have in dev mode?
ts
// Determine whether each hook in the following plugin fires in dev / build mode:

export function myPlugin(): Plugin {
  return {
    name: "my-plugin",
    config(config) {
      /* A */
    },
    configureServer(server) {
      /* B */
    },
    buildStart() {
      /* C */
    },
    resolveId(id) {
      /* D */
    },
    transform(code, id) {
      /* E */
    },
    generateBundle(_, bundle) {
      /* F */
    },
    handleHotUpdate(ctx) {
      /* G */
    },
  }
}

// Fill in the table:
// | Hook | dev | build |
// |------|-----|-------|
// | A    | ?   | ?     |
// | B    | ?   | ?     |
// | C    | ?   | ?     |
// | D    | ?   | ?     |
// | E    | ?   | ?     |
// | F    | ?   | ?     |
// | G    | ?   | ?     |