---
# System prepended metadata

title: a small primer on vite plugins
tags: [vite, typescript, webdev]

---

# primer on vite plugins

Vite is a build tool + development server for web apps. It uses native ES modules in development (no bundling) and Rollup for production builds.

Vite offers **[plugins](https://vite.dev/guide/using-plugins)** (based on Rollup's [plugin](https://rollupjs.org/plugin-development/#plugins-overview) interface): objects defined that allow one to customise Vite's behaviour by, for example, transpiling code before bundling, or finding third-party modules in your node_modules folder. 


Plugins are injected into the `vite.config.ts` file. A simple, yet critical detail here is that ordering the plugins in the array affect the way in which they are executed. So if one were to write a custom plugin, and place it before the `react()` plugin, as so: 

```ts=
plugins: [
    customPlugin(),
    react()
 ],
```

-- the custom plugin is compiled first.

The only way around this is to "[enforce](https://vite.dev/guide/using-plugins#enforcing-plugin-ordering)" the same, which requires one to specify whether the plugin is to run pre core Vite plugins, after, or post Vite build plugins. It is invoked as so: 

```ts 
plugins: [
    {
      ...image(),
      enforce: 'pre',
    },
  ],
```


To understand how plugins work in practice, I wrote a [simple plugin](https://gist.github.com/ultraviolet10/e140612cb4c4e74224fd0d6c67c30445) that logs every lifecycle hook Vite calls during development and build. It implements 11 major lifecycle hooks and logs them with: 
- execution order (numbered sequence)
- timing
- data such as file path, modes, etc.

The hooks implemented are as follows:

(vite-Specific Hooks)
 - `config` - Modify Vite config before resolution
 - `configResolved` - Access final resolved config
 - `configureServer` - Add dev server middleware
 - `transformIndexHtml` - Transform the HTML entry point
 - `handleHotUpdate` - Custom HMR handling

  (rollup Hooks ~ *universal*):
  - `options` - Receive Rollup options
  - `buildStart` - Build initialization
  - `resolveId` - Resolve module imports
  - `load` - Load module content
  - `transform` - Transform module code
  - `buildEnd` - Build completion
  - `closeBundle` - Final cleanup

To actually study these in action, we simply run `bun run dev` and `bun run build`, which reveals important differences: 

**Development mode**:

- Files are transferred on-demand when requested
- `handleHotUpdate` emits when you edit files
- No `closeBundle`, since nothing is written to disk

![Screenshot 2026-01-05 at 7.31.13 PM](https://hackmd.io/_uploads/HyoB4BK4Zl.png)

**Build mode**:
- All files processed upfront
- `closeBundle` fires after writing the `dist/` folder
- No dev server hooks

![Screenshot 2026-01-05 at 7.35.30 PM](https://hackmd.io/_uploads/BkIEHHFNZx.png)

This plugin is just an example to understand how to build more performant plugins that transform code, add routes, inject scripts, or customize the dev server.


----
until the next one! :wave: 