The debate over which framework is "best" for web apps usually boils down to a choice between using a massive, all-in-one meta-framework or going back to basics. For me, a combination of Vite, React, and TypeScript is more than enough.
By "Vanilla Vite," I mean keeping the stack lean. I don't feel the need to pull in a massive engine if a simpler tool gets the job done. As long as the app doesn't require complex file-based routing or heavy server mutations, this approach works incredibly well.
When you run the install command, you aren't getting anything magical. You just
get a fast dev server, ESM support, and an index.html as your entry point—not
some CLI-generated file that works in ways you don't quite understand. For
building product interfaces that already have their own APIs, this feels much
more natural.
I also have a rule against overcomplicating configurations. I won't add a second
build pipeline just for the sake of "consistency" if it isn't necessary yet.
Keep the config thin. For instance, if you use an @ alias, make sure it’s
synced with tsconfig. If not, your editor and bundler won't play nice, and
that's a "frustration tax" you'll pay every time you're just trying to blast
ahead coding.
When it comes to routing, I don't subscribe to any specific framework "religion." For small to medium apps, I’m still comfortable using a declarative router like React Router. I focus on what’s practical: making sure URLs can hold state via query strings so they're shareable, building layouts that don't re-render the whole page on navigation, and using lazy loading only once the app actually gets big. There's no need to overdo it on day one.
This approach has its downsides, though. I know when it's time to "level up" to a meta-framework or use SSR. If SEO becomes critical for marketing pages, or if authentication needs to be handled on the server for better security, Vite alone might not cut it. Once server mutations become a product requirement rather than just a technical preference, that's when I switch.
One mistake I see often is treating Vite as if it were Next.js. It’s a
client-side SPA; we have to be aware of that limitation. Also, don't let the
speed of Vite make you lazy with tsc. Even though Vite can build without
checking types, I still want my code defined by a precise typing system to avoid
headaches down the road.
Using Vite statically with a clean API provides a certain operational peace of mind. Deployment is just a collection of files, rollbacks are as simple as checking your CDN history, and the actual experience of coding stays fast. Not everything has to be complicated to be effective.