Migrating from Create React App to Vite
60 points
by luu
2 days ago
| 13 comments
| klimer.eu
| HN
Noumenon72
1 day ago
[-]
I was really happy to discover Vite so I don't have to change the way my whole app works with Next.js. Here are the differences I found from Create React App:

* It does not have the overlay that warns you about eslint errors in your app; you can bring that back with the `vite-plugin-checker` plugin if you want.

* Running `vite` in IntelliJ with `npm run dev` will not show console logs in the IDE[1]. You don't need any of the solutions in that thread, though: just use a debug config.

* You can add TypeScript types to your env vars with vite-env.d.ts.

* Imports have to end in .tsx apparently: `import Foo from './components/Foo.tsx';`

* No process.env: Since your code is running in the browser rather than on a Node.js server, there is no process env var.

* Network tab shows a request for every file on initial load: This is because each file is treated as an ESM module for hot reload. I couldn't figure out how to suppress these messages. It won't happen in production since they still use a bundler in prod to save network round trips.

1: https://ww.reddit.com/r/react/comments/zo6nay/how_can_i_view...

reply
yurishimo
1 day ago
[-]
Regarding this point - Imports have to end in .tsx apparently: `import Foo from './components/Foo.tsx';` - since Vite uses ESM, each file that requires transpiling of some sort needs to contain a file extension so Vite knows to pass it off to some other service that converts it into plain JS before being sent to the browser.

The same is/was true for `.vue` files as well. The webpack loaded had some config that would figure it out for you, but Vite requires that you import the component with the extension.

Personally, I quite enjoy this change because now it's very explicit what file is being loaded and it opens up the possibility to load a JS file and some other file with the same name. Granted, small problem to solve, but in a huge project, it's not uncommon to see duplicate file names with different extensions sometimes.

reply
isleyaardvark
1 day ago
[-]
I'm pretty sure @vitejs/plugin-react allows you to leave off the file extension for imports. (I say this because I'm looking at a working Vite project with no file extensions, and that's one of the few differences with an out-of-the-box setup.)
reply
art0rz
1 day ago
[-]
We maintain a fairly large and complex CRA application, which has been migrated to Vite, but not fully.

Since a lot of changes would be needed and we are very risk averse (heavily regulated industry), we decided to do it piecemeal by first configuring Vite to behave exactly (or as close to as possible) like CRA and later removing these Vite configurations one by one (and making necessary code changes).

Currently our production builds are done through CRA while dev runs Vite. It works really well and minimal code changes were needed.

I've been wanting to release this config publicly to help others migrate to Vite, but have unfortunately not had time to.

reply
geekybiz
23 hours ago
[-]
Sounds like an interesting approach. I'm sure a gradual approach like the one you mention would be super-helpful for anyone trying to migrate a complicated CRA app. I'm hoping you're able to find time to share in the near future.
reply
yurishimo
1 day ago
[-]
We recently migrated a Laravel app from Webpack (Laravel Mix) to Vite and it was mostly painless. Like OP, we had to update some imports that were using require and we also have a few different entrypoints which is not that well documented for the Laravel plugin.

Another issue we ran into was certain dependencies that only have .cjs versions that work fine when building, but during dev would fail to import via ESM correctly (the dependency itself is inside of a lazy loaded component). That means that if we're working on that part of the app, we need to do a full production build (6 seconds) between each code change and then use the prod build locally to test the feature. Annoying for sure but we can live with it until someone has time to update the dependency to one that natively supports being loaded via ESM.

If anyone is running the old Laravel build pipeline, I suggest upgrading now because it's potentially only going to get more and more complicated. If you have a simple frontend with some helper functions or a few Vue components, it should be pretty easy to reason about. The course on Vite from Laracasts was helpful to explain what some of the less documented features are, which was helpful for integrating with our application.

reply
benchloftbrunch
1 day ago
[-]
> Another issue we ran into was certain dependencies that only have .cjs versions that work fine when building, but during dev would fail to import via ESM correctly

I'd say that's more node's fault for half assing their ESM-CJS interop. Like, their solution to importing CJS was to go with a dumb static analysis thing. And their solution to requiring ESM was, you can't do it.

Meanwhile bundlers had ESM and CJS working side by side almost flawlessly.

reply
danielhep
1 day ago
[-]
Good read! I have a huge CRA app I maintain at work, and we've been wanting to migrate to something else for years now. will have to check this out.
reply
tuyiown
1 day ago
[-]
beware for large apps, if you don't have heavily splitted your app with dynamic imports, vitejs is frustratingly slow by loading each ESM module separately. Seems that rspac has others interesting tradeoffs.

Works great once loaded though.

reply
iliaznk
1 day ago
[-]
We have encountered that problem too. As far as I learnt from various sources that is caused by circular imports in some cases, and by using index files that re-export your modules in other.
reply
dangsux
1 day ago
[-]
Why have you wanted to migrate away? What are it’s shortcomings?
reply
Vinnl
1 day ago
[-]
It hasn't been maintained for ages, so at some point you'll want to use some new dependency and find out that you can't. And it's very slow, too, if I recall correctly.
reply
chrysoprace
1 day ago
[-]
Not OP, but we had issues with SASS that made it difficult to upgrade. It didn't like the M1 architecture when trying to upgrade from Node 12 to Node 18, where as swapping it out for Vite just worked. Given CRA is deprecated, it made sense to move away. I followed a guide[0] which covered most of the things I needed to change and the remaining issues just involved following the build errors.

We also saw much-improved build times, which was a welcome change.

[0] https://cathalmacdonnacha.com/migrating-from-create-react-ap...

reply
auggierose
1 day ago
[-]
Both CRA and Vite seem to be a mess, I don't know how people work with that.

My recommended stack: node, npm, typescript and esbuild.

reply
JoeyJoJoJr
1 day ago
[-]
> Both CRA and Vite seem to be a mess

CRA, yes. Vite, not in my experience. Vite does what it says it does, gets out of your way, is configurable enough when you need it to be, and has great testing tools. I don’t think I’ve ever had an issue with Vite.

reply
arbol
1 day ago
[-]
Vite uses esbuild under the hood. It can also create bundles with all your deps, which is essential when you don't want an npm i step involved during deployment (lambdas/containers).
reply
Aldipower
1 day ago
[-]
To be clear, it is esbuild that bundles not vite.

esbuild index.js --bundle --outfile=build.cjs --format=cjs --platform=node

You can use pkg then to make this bundle executable.

pkg build.cjs

This is not vite. :-)

reply
arbol
1 day ago
[-]
Fair point. But vite makes this easier than esbuild. I get a bundle that's immediately executable with node
reply
silverwind
1 day ago
[-]
Vite uses esbuild only during development, in the build it uses rollup.
reply
auggierose
1 day ago
[-]
Yes, I've noticed that. So to debug problems I need to know both rollup and esbuild, and how exactly Vite configs boil down to configs for these two.
reply
solarkraft
23 hours ago
[-]
Has this happened to you? I expected this to happen to me, but I’ve never noticed a disparity.
reply
rk06
1 day ago
[-]
some people value HMR in development, and like lots of build plugin. esbuild plugin support is not that great, hence they put up with vite. Case study: Remix went from esbuild ot vite
reply
auggierose
1 day ago
[-]
I tried Remix, and boy, this is a real mess. Some people just don't value understanding what their code and configuration actually does, I guess. I can assure you, it saves a lot of time if you do. I don't like plugins, especially when the documentation for how plugins work is non-existent or seems to have been written by children.
reply
mattmanser
1 day ago
[-]
Vite is great, don't know why you'd make your own life hard like that.
reply
auggierose
1 day ago
[-]
On the contrary, this stack makes my life as comfortable as possible.

I do understand esbuild, I don't understand whatever it is that Vite does.

reply
lyxell
1 day ago
[-]
With Vite you do not have to hack together Hot Module Reloading yourself.
reply
auggierose
1 day ago
[-]
esbuild has live reloading, which works great. No hacking needed.

Edit: I don't know how Vite's HMR works, but it wouldn't surprise me if it is just piggybacking on esbuild's live reloading. Anyone here knows if there is a difference between the two?

reply
yurishimo
1 day ago
[-]
When you say "live reloading", does that mean updating the component while maintaining state (HMR) or just triggering some sort of global page refresh?

My guess is that Vite relies on underlying tools as much as possible and only ejects when they need to. This is also why they are writing their own version of esbuild/rollup so that more of the dev pipeline can be controlled by Vite.

reply
auggierose
1 day ago
[-]
What I mean by "live reloading" is that when you for example save a TypeScript file, esbuild does an incremental build and your webpage can check for that and reload. There is no state maintained. I think maintaining state would be a very difficult problem to solve correctly. Actually, I think that is impossible, and invites all sorts of heisenbugs, working only for simple cases.
reply
lyxell
1 day ago
[-]
I’ve used both standalone esbuild as well as Vite for hundreds of hours and HMR works flawlessly in React using Vite, it’s the only reason I use Vite instead of just using esbuild directly. I suggest you to try it out.
reply
ativzzz
1 day ago
[-]
I've been working with web apps for a long time and have always turned live reloading off whenever I can.

My code editor always auto saves changes so i just alt tab to browser and refresh to see changes

With live reload, often what happens, especially as projects get larger, is the watcher takes longer to trigger so the live reload is slower than just alt-tabbing, and it sometimes goes off on its own, probably due to multiple quick file changes, which is really annoying

I've never enjoyed this feature

reply
ddoolin
18 hours ago
[-]
It must not be that difficult as I use it every day in my Vite project. To be clear I am no Vite evangelist, but it does maintain existing state when it live reloads modules, it doesn't just refresh the page.
reply
Aldipower
1 day ago
[-]
Thanks, so I won't use vite. Hot Module Realoading never worked reliable to me.
reply
homebrewer
1 day ago
[-]
CRA's HMR is a mess that breaks more often than it works, but I haven't had any problems with Vite's HMR after using it for the past three (I think?) years.
reply
conradfr
1 day ago
[-]
Because you did not use Vite.
reply
lyxell
1 day ago
[-]
I can’t speak for how it works in other frameworks but HMR for React works flawlessly in Vite.
reply
o_m
1 day ago
[-]
It can be simplified even more if you just use Deno and don't bundle.
reply
auggierose
1 day ago
[-]
I've looked at Deno, but I don't see any advantage of it over my simple stack.
reply
Aeolun
1 day ago
[-]
I’ve recently discovered rsbuild, which doesn’t have vite’s issue where a large application with many small modules takes minutes to load (in dev), because all 6000 files are an individual request.
reply
rossng
1 day ago
[-]
CRA seemed near-abandoned when I first tried it in 2020. I'm surprised to hear that people are still recommending it as the default. Vite works so well out of the box.
reply
ricardobayes
1 day ago
[-]
It's easier than people think, and it's very well worth it. CRA is now obsolete and not getting any updates. Vite is so much faster as well (code changes reflect immediately).
reply
leros
1 day ago
[-]
How is Vite compared to Next with static export? I moved my React apps over to Next earlier this year and I've been pretty happy.
reply
mkhalil
1 day ago
[-]
CRA is meant as a starter application. IMO for anything serious, an app should either "eject" or use a framework.
reply
nayroclade
1 day ago
[-]
CRA seems pretty much abandoned. I wouldn’t recommend it for anything nowadays. There are much better options for both starter and serious apps.
reply
JoeyJoJoJr
1 day ago
[-]
I would never touch another project that has ejected from its build tool, and I am almost certain that I would never do an eject on my own projects. The maintenance burden is too high and it always becomes a complete mess as dependencies go out of date.

I’d be interested to know for what reasons others have ejected? I have worked on 3 projects where some idiot dev had ejected for the silliest reasons. Since holding firm on my “never eject stance” I’ve never encountered a problem that I couldn’t solve without reading the docs more.

reply
williamdclt
1 day ago
[-]
I’ve been unhappy with both options.

Ejecting gives you an incredibly complicated configuration (as CRA needs configs that can handle _any_ project) with a high maintenance cost as you say.

Not ejecting gave me other headaches. Any advanced stuff that wasn’t directly supported by CRA (that happened multiple times) required me to understand the underlying config _and_ how CRA interfaces with it _and_ how to force CRA to generate the right configs. The classic leaky abstraction problem. Plus, CRA wasn’t bug-free leading to its own problems where I also had to dig into internals. That’s all significant maintenance burden too (added complexity).

Out of the two options I’d almost certainly stick with “don’t eject”, but nowadays I heavily favour “don’t use these tools, keep build tooling simple”. And like so many people, I’m leaning more and more towards “it’s all so crazy complex (and broken) that it’s not worth it, don’t use React”. Things are getting _more_ complex with new developments rather than less, this is an insane ecosystem.

reply
JoeyJoJoJr
1 day ago
[-]
CRA was indeed a headache. Thankfully I use Vite now and I hardly ever think about it for my relatively simple SPAs.

What stack/tools are you currently using?

reply
mkhalil
14 hours ago
[-]
"I would never touch another project that has ejected from its build tool"

Maybe if it was something that first relied on a large framework, but CRA is not that. It is just a tool that kickstarts the development process, especially for the inexperienced.

Considering all it really provides is ESLint rules and a webpack config, if you're working on a serious project, a day or two of work shouldn't be a blocker. Besides, there are other options you can use besides webpack (e.g. vite, as in this post).

I know webpack scares many people, and TBH it is not a nice DX, but many enterprise/big projects require customizations that required ejections and a blanket statement like "I would never touch another project that has ejected" would severely limit your opportunities in working in the enterprise world.

reply
mattmanser
1 day ago
[-]
When I switched from CRA/webpack to Vite I saw an order of magnitude speed-up, from minutes to a few seconds to compile. My laptop is old, needs replacing and starting to struggle but really shows how bloated webpack is.

It's fairly trivial to switch too, though means you need to start understanding how webpack/vite/package.json/tsconfig actually work. I did the same as the author and just made a new vite app and then copied stuff over and moved things as required.

The only complication I could really see where the environment variable names, which we only used two anyway.

By the way, I don't know why the author changed to using import 'components/blah' to '~/components/blah' as there's no real explanation in the blog post. The link for their commit is broken so can't see what the author has done.

You actually don't need to do that, all you have to do is set 'baseUrl' in your tsconfig (e.g. "baseUrl": "./src"). That tells typescript where to look for imports. Then Rollup via Vite handles the rest in compilation.

reply
arbol
1 day ago
[-]
Webpack is much slower because it's written in JS. Vite uses esbuild to compile, which is written in Go. That's probably why you saw the speed up.
reply
racked
1 day ago
[-]
Surprisingly painless, in my experience. Vite is good.
reply
globalise83
1 day ago
[-]
Preact with Vite works very fast and Preact seems like a good long-term solution for projects which will use the single-page application approach (React seems to move ever-closer to the server rendering approach).
reply