Skip to content
yf_
← Writing

Rebuilding my site on Vite+ to stay current

5 min read
  • Vite+
  • Astro
  • Tooling
  • Learning

I rebuilt this site. The result isn’t the interesting part. It’s a portfolio, and the previous one worked well enough for four years. The interesting part is the reason, which had very little to do with the site and everything to do with the fact that rebuilding it is the only reliable way I’ve found to keep up with JavaScript tooling.

The problem with keeping up

The ecosystem moves faster than any single project touches it. In the time my last site sat untouched, the default way to build one changed more than once, bundlers got rewritten in Rust, Tailwind changed its configuration model entirely, and React shipped two major versions.

I read about all of that. Reading about a tool tells me what its authors think it’s for. It doesn’t tell me what it’s like on day three, when the abstraction leaks and I’m reading its source to work out why my build output is wrong. That’s the part I actually need to know, and there’s no way to get it without using the thing.

Work is the wrong place to find out

The obvious answer is to learn on the job. It doesn’t work, and I think the reason is structural rather than a failure of anyone’s curiosity.

When I’m building a product, introducing a new toolchain adds nothing to that product. The users don’t get a feature. The business doesn’t get revenue. What it does add is risk, a migration, a review burden on people who didn’t choose it, and one more thing the next engineer has to learn before they can be useful. If a colleague proposed swapping our build tooling for something released last month, I’d argue against it, and I’d be right to.

So the correct engineering decision at work is almost always the boring one, and the accumulated effect of making the correct decision repeatedly is that I only ever use tools I already know. That’s good for the product and quietly bad for me.

Personal projects are the testing ground

Which leaves my own projects, where the stakes are set at a level that makes experimentation reasonable. I’m the only user. Nothing is on call. If a tool turns out to be a mistake, the cost is a weekend and I’ve still learned the shape of the mistake.

Two things follow from that, and they’re the ones I think people get wrong.

The tool doesn’t have to be production-ready. I’m not putting a company on it. I’m finding out whether it’s the sort of thing I’d want to put a company on in two years, which is a question I can only answer early.

And it doesn’t have to be the right tool for the job. My site would be perfectly well served by static HTML and some CSS. The job is not really the point. The job is a pretext for handling the tool. What I’m protecting against isn’t picking something unsuitable for a portfolio. It’s the thing I keep skipping over turning out to be the one I needed, and finding that out from a job description rather than from a weekend.

What I built this time

Astro for the site, React only where something is genuinely interactive, Tailwind 4, and Vite+ underneath all of it. Astro and Tailwind 4 I’d used enough to have opinions about. Vite+ was the actual subject of the exercise.

What Vite+ replaced

Vite+ is VoidZero’s attempt to put the whole toolchain behind one CLI, driven as vp. It bundles Vite, Vitest, Rolldown, Oxlint, Oxfmt and tsdown, and it’s MIT licensed.

The clearest way to describe what it did is to count configuration files. My old project had an .eslintrc.js, a tailwind.config.js, a remix.config.js, a tsconfig.json, a package.json juggling seven scripts across two runners, and, for reasons I can no longer reconstruct, both a yarn.lock and a package-lock.json.

Linting, formatting, type-checking and test configuration now sit in blocks inside one file:

import { defineConfig } from "vite-plus";

export default defineConfig({
  fmt: {},
  lint: {
    options: { typeAware: true, typeCheck: true },
  },
  staged: {
    "*": "vp check --fix",
  },
});

What that bought me:

  • vp check runs formatting, linting and type-checking together and clears the whole project in under a second and a half. The lint and type-check pass on its own reports in about 600ms.
  • vp install worked out the package manager and downloaded it without being told.
  • The staged block wired up a pre-commit hook with no other moving parts.
  • Scaffolding, dependency management, the dev server and the tests are all the same CLI.

That last one sounds like a small thing and isn’t. A lot of the friction in a JavaScript project is the tax of remembering which of five tools owns the thing I’m trying to do.

It’s not frictionless. vp build runs Vite’s own production build rather than the project’s build script, so an Astro project needs vp run build; built-in commands and package scripts are separate namespaces and it took me a wrong build to internalise that. It’s also 0.3.x, and I wouldn’t put a team on it yet for that reason alone.

Where I landed

I liked it more than I expected to. No individual piece is novel, and I’d already used Vitest and Oxlint on their own. What changed is that the seams between them are gone, and the amount of project setup I had to think about dropped to roughly nothing. I spent my time on the site instead of on the scaffolding around it, which is not how this usually goes.

I’ll reach for it on the next personal project, and I expect I’ll be arguing for it at work within a year or two, once the version number stops starting with a zero. That’s exactly the outcome these rebuilds are for: I now have an opinion about a tool I would otherwise have kept reading about and never used.