Elm and Vite

Lindsay Wardell joins us to talk about Vite and how to set it up for your Elm project.
February 13, 2023


Hello, Jeroen.
Hello, Dillon.
Today we've invited a special guest.
You might have to help me with the pronunciation there.
Yeah, I wish I could help you, but I'm not the one that speaks French in this room.
Oh, there's our special guest, Lindsay Wardell.
Lindsay, thanks for joining us.
Yeah, thanks for inviting me.
What are we talking about today, Lindsay?
Vite, which is the front-end development tool created by Evan Yu, who is the creator of Vue.js.
I wish we had a French speaker on this podcast to help us on how to pronounce that.
It would be helpful.
Yeah, I'm still surprised by the pun, because I thought, oh, he's going to make some joke about,
oh, we need to hurry up and record this or something, like, Vite, Vite.
That would have worked, but no.
That would have been too clever.
In Vite.
Going for the lowbrow here.
So what is the proper pronunciation?
Yeah, well, Lindsay, before we started recording, you said that you were discussing how to pronounce Vite.
Yeah, so in a former time, I was a podcaster on the show Vue.js on Vue.
And when the Vite framework was originally announced and released, we were talking about it,
and none of us could figure out how it was supposed to be pronounced.
I think at the time we were saying Vite, because we just couldn't figure it out, but we kept going back and forth.
Vite, Vite, should you drop a vowel, should you drop a consonant?
None of us actually know French, and the only French word we know is Vue, which I assume we're also pronouncing wrong.
So we just kind of gave up, and the Vite documentation says Vite at this point, and that's how Evan Yu pronounces it.
But I just assume that we're all wrong.
Well, for Vite, it's not too bad.
So the proper pronunciation is Vite.
So it's pretty much the same, but the E is shorter, Vite.
And Vue is Vu.
So that's pretty wrong, I'd say.
That's pretty wrong, yeah.
I wonder if it's just payback, because Evan Yu is a native Chinese speaker, right?
And he's like, all right, you're making me write all this code in English.
Now I'm going to make all you English speakers try to poorly pronounce French words.
I always assumed it was kind of like to make it sound like his name, Evan Yu, Vue.
Yeah, that's cool.
I think the original inspiration was he was just trying to find words that described a Vue layer, and he ended up on that one, and it was available on NPM.
So at this point, the entire Vue ecosystem is just named after French stuff.
That's clever.
And it's three letters.
Three letters are good for file extensions.
Oh, definitely.
I think you can say Vue.
I just say Vue.
I think if you're in the know, you can say Vue, but if you use React or Svelte or something, you need to say Vue.js just to be specific.
You mean if you're in the React.js ecosystem?
But don't say AngularJS, because that's another thing.
Yeah, that's a problem.
Thinking about it, and this is off topic from Vite, but Vue did work on AngularJS, and that's part of where the inspiration for Vue came from.
So maybe that's part of why Vue is originally Vue.js, and it's since shifted.
I don't know.
Wait, it's not Vue.js anymore?
I mean, if you go to the docs, let me pull it up right now.
Vue.js, the pervasive JavaScript framework and guide.
Yeah, it just says what is Vue.
Don't mind if you pronounce it Vite, I think it's close enough.
I will forgive you if you do so.
Thank you.
So what is Vue?
Sorry, what is Vite?
We're not going to talk about Vue on this podcast more than we need to, because that's not what we're here to talk about.
What is Vite?
So Vite is front-end tooling is how they build it on their site.
And I've actually had a hard time coming up with a one-sentence definition that fits into the normal boxes,
because what it gives you is first a development environment.
So as you're working in your code, it spins up the dev server, you're able to import your code,
it does some processing as needed, especially if you're writing Elm code.
And then when you go to build, it uses a build process, but it's a completely different build process than what it's using during development.
So you could compare it to a bundler, but it's not quite a bundler.
And you could compare it to other systems' development environments, but it's not just a development environment.
It kind of fits into all of these different sections, kind of like the cracks,
especially when you're working in JavaScript, but other languages as well.
It fits into the cracks where things aren't as good and makes them much better.
And that's why I'm having so much trouble figuring out what it's actually doing.
Is it a bundler? Is it this other thing?
Okay, it's in between. Gotcha.
And actually Vite itself is not a custom bundler.
It's using two different bundlers under the hood for doing its job.
So during development, primarily it's using rollup for all of your code.
If it needs to do any pre-processing, it'll use esbuild, unlike dependencies.
But for any of the code that you write yourself, it's using rollup and compiling your code into ES modules
so that it can just run natively in your browser without having to be bundled into a single JavaScript file.
And then when you're ready to go to production, it will push everything through esbuild
and create a single bundle at the end of the day.
So Vite itself is not a custom bundler.
It's just using other bundlers and putting it together in a unique way.
And what are ES modules and esbuild?
So esbuild is a tool written in Go for compiling JavaScript.
I don't have the specific details in front of me, but I've been told it's very fast.
It's one of the up and coming ones.
I don't think it's reached a 1.0, but that's not something unfamiliar to us.
Fair, fair.
That's what esbuild is.
It is specifically a bundler.
And the other bundler that's used is rollup.
Rollup, or I guess you could say rollup.js, was created by Rich Harris of Svelte.
And it's going off in its own direction at this point.
I don't think he's as involved as it used to be.
But that's what's used during the development experience.
I find it helpful to compare Vite to Webpack because if you just try to define it,
it's kind of like, is it a dev server?
Is it a bundler?
What is it?
But if you think about Webpack, I imagine many of our listeners have used Webpack.
And you try to just set up Webpack.
You configure Webpack, you write a bunch of config files, you install a bunch of plugins.
And then if you're trying to bundle everything, you run the bundle command.
And then what if you want to run a dev server?
Then there's kind of like maybe another package or plugin you install that helps you get a dev server,
but things kind of run differently.
So when you compare Vite to that history, I feel like one of the main problems that Vite solves,
in contrast to Webpack, is it gives you an environment where you can run a dev server or you can run a build,
and it uses kind of the same setup.
It will do more minification and things like that for the production build than it does in the dev server,
but you can sort of use your same setup.
And there's less configuration to write compared to Webpack.
That sounds accurate to me.
The thought that I have looking at it is in the Elm ecosystem, we talk about JavaScript fatigue a lot when we're talking about JavaScript.
So if you're building a React application, you need React, and then you need Redux,
and then you need something else.
And if you want immutable data, you need something else.
And if you want a linter, you need, and so on and so on and so on.
Whereas Elm, it's just Elm.
And Vite kind of feels the same while remaining a JavaScript thing.
If you're needing to build an application, you're going to need a bundler.
You're going to need a dev server.
You're going to need to do hot module reload, ideally.
And that's all very complicated.
And we can see how complicated it is in JavaScript because of things like create React app,
or in Vue 2.0, they had the Vue CLI, which also used Webpack.
So Vite is geared towards solving that problem where you have the dev server, you have the bundler,
you have everything that you need just kind of built into one tool.
So can you sort of pick up things about your config?
For example, if you want to import TypeScript files,
then you don't need any special setup because ES build automatically can transpile TypeScript files,
and it's built on top of ES build.
So it will use your TS config, and it uses various configuration files
and these standard conventions to run the bundling. Is that right?
That's supported out of the box.
Other things are supported out of the box, such as WebAssembly.
You can just import a WebAssembly file and start it up in your application, and it works fine.
You don't get hot module reload on WebAssembly, obviously,
but you can import the WebAssembly file directly.
On TypeScript, though, one of the interesting notes is because of how Vite is handling it
and passing the TypeScript file to ES build for the compilation step,
you don't need any actual type checking in your TypeScript.
It's just performing the transpilation.
So if you're wanting the lovely type checking that we're used to,
you need to set that up in your IDE.
Okay, or use a different terminal to run the TypeScript checker.
So I'm still confused as to why it's using two different bundlers,
one in production and one in development mode.
Is it because ES build is only meant for production builds and rollup is meant for both,
but it's not as good in production or something like that?
The main reason that Vite is using two different bundlers at the moment
is because ES build is still under development.
It hasn't reached 1.0.
So while it's very fast, it can't handle everything at the best efficiency.
So for the time being, Vite is using rollup and ES build
for two different parts of the experience.
The long-term plan is to settle on just one,
and that's probably going to end up being ES build because of its speed.
But for the time being, they're still having to use the two.
There's also the nice benefit with that of having the plugin ecosystem
that incorporates Vite specific features, but also the rollup plugins.
So if you want to use a rollup plugin in Vite,
you can just import it and use it as normal.
You don't have to worry about, is this compatible?
Because it just is.
But only in development mode?
It will still work in production.
I cannot speak to how it does that magic, but I know it does.
Oh, interesting.
So that's interesting because I imagine many people do associate ES build
with being like a JS and TypeScript transpiler.
I have seen in the docs things about CSS files,
so I guess it is a general purpose bundler as well.
That's interesting.
ES build?
I believe that is the long-term goal.
I'm not 100% on that.
I write applications.
It seems like a real trend I'm seeing these days in the JS ecosystem
is trying to come up with a set of agreed upon standards
rather than just everything having bespoke approaches to things.
So rollup plugins have a specific format and Vite said,
okay, well, there's this sort of plugin API and let's just use that same
API. And if we're using that same convention,
we can use ES build and for the dev server and everything can play nicer
together because they're using the same conventions.
One of the big pushes was not having to worry about a development
configuration and a production configuration.
And one of the other niceties was there's a project called Vitest,
which is just going to throw the French out the window again.
I mean, that's not even a French word at all.
It just means you have to pronounce the first bit wrong.
It might be a double entendre though, which is not a French idiom, I think.
Of course not.
But Vitest is intended as a replacement for something like Jest.
So instead of having to do all of your testing in Webpack and have a
separate configuration for your tests from the application,
you can just use your global Vite configuration, write your tests,
and everything just works.
I saw the name a few times before, Vitest, but I never understood
that it was a test framework.
I just thought it was, well, Vite means fast, so Vitest is fastest.
So it's something to make things faster again or something.
Okay, it's a test framework. Gotcha.
Yeah, and that was created by Anthony Foo, who is on the core team for Vite.
He's been extremely prolific in creating tooling around Vue and Vite.
Definitely if you're interested in this ecosystem, I would suggest checking him out.
Yeah, it's been extremely active.
Vite is just such a well-supported tool these days.
I know TurboPack was recently announced, and then there were some somewhat
controversial figures that were given.
I know Evan, you had some things to say about the methods for coming up
with these benchmark comparisons between TurboPack and Vite.
It's just like Vite is so well-maintained that I'm not ready to jump onto a new train at this point.
Vite seems great, and it seems like an agreed-upon standard, and it uses these standard conventions.
So I'm all on board for Vite, personally.
Have you tried it on a few projects at all?
I use it internally for Elm pages for the V3 beta. I'm using it.
There's an SSR mode that tooling authors can use to serve up pages.
So yeah, Elm pages V3 comes completely with a built-in Vite server.
So you can use it to have SCSS files or tailwind configuration, or import TypeScript code into your entry-point JavaScript file in your Elm pages project.
It all just works out of the box.
Back in Elm pages V2, I became somewhat disillusioned by the whole webpack world.
Some tools, some front-end frameworks like Gatsby and things like that, would expose ways to add your own webpack plugins so you could hook into the webpack internals.
That just felt like such a weird inversion where you can mess up internals in a way that could actually break the framework.
That didn't feel right.
And then it felt like there are so many possible configurations people could build.
I just came to this conclusion, Elm pages is going to focus on just compiling your Elm code and setting up everything and just focusing on that.
If you want SCSS files, if you want to import TypeScript files, there's a TypeScript compiler that turns that into JavaScript.
There's an SCSS compiler that turns that into CSS files.
And Elm pages doesn't care how you get your CSS and JavaScript files to it, but that's the only thing it understands.
I called it a bring your own bundler philosophy.
But Vite completely changed my perspective on that because it came with this sort of standards-based approach where it's like, if you have an SCSS file, you can import it.
And you have the thing you need to compile SCSS installed.
You don't need to configure it to explain how to transpile SCSS files because that's a standard.
You just include an SCSS file in your index.html and it figures that out.
Or you include a.ts file in your.html and it figures that out.
And then you just compile it.
Or you include a.ts file in your script in your index.html file and it figures that out.
Or you include a.ts file in your script in your index.html file and it knows what to do with that.
And so it completely changed my perspective on that.
And I integrated Vite in Elm pages v3 and it's been great.
The front end ecosystem in general. I'm really excited to hear how it's in Elm pages v3 and I'm looking forward to trying it more myself.
But Vite is being used by Vue, by Svelte, by Solid, by Astro.
I think there's some integrations for things like Eleventy as well.
So there's a lot of projects that are now using Vite and adopting it in the background and bringing together this whole ecosystem.
So that everyone's on the same basic tooling from the start.
Then you just add the framework and libraries on top of it that you want.
Exactly. Yeah.
Yeah. It's a breath of fresh air, especially like, you know, Jeroen and I have talked about this world where you have like,
everybody has a custom Babel configuration that gives them like slightly different JS syntax.
And then you've got JSX and TSX and then variants on that and all these things.
And it's just like, can we just agree like what the basic syntax is and just like not mess with that stuff?
So all the tooling can just not need all this customization to just tell it like what syntax is and how things work.
And it just feels like the pendulum is swinging back there.
Things got a little bit out of hand and now we're like, let's just kind of take a more conventional approach.
Yeah. I imagine it's also because the JavaScript community is less intent on shipping new things.
Like they're not building a new JSX as far as I know.
And it's just like, okay, well people are using JSX. Okay. Let's include it.
People are using TypeScript. Let's include it. People are using SCSS. Let's include it.
In that way you have a standard because that's what people use.
And there are new versions of JavaScript coming on every year.
So let's just add support for them whenever they arrive and that's it.
So that does make things a lot easier. Yeah.
And in the Elm ecosystem, like we've had this for a long time because Elm 0.19 has been released a few years ago and not much has changed since then.
So for us, it's very stable as well, but for very different reasons.
I think I've come to a point where I'll admit I've only used Elm 0.19.
I started in the Elm ecosystem right after it released.
So I have not yet seen the pain of having to transfer between versions.
Yes. Same here. I think I started looking at it in 0.16 or 0.17.
Yeah. 0.16 when there were still signals.
0.19 was released like a month before and hasn't changed since.
One other piece that we haven't touched on, and I realized Dillon, you asked the question, I forgot to answer it.
ESM, which is a huge piece of what makes the Vite development experience so lovely.
And as I'm describing this, I'm going to be describing it from a JavaScript perspective primarily because that's where you see the biggest benefit.
But this also helps with Elm development as well.
During development using Vite, all of the scripts, all of the code, everything is just compiled into standard native ES modules.
ES in this case being ECMAScript.
You mean that even things that use CommonJS or other formats that you use like require, they get compiled to use import and export syntax?
Good job.
During the development process, everything is in ES modules.
Rather than the experience that we've had with Webpack, for example, of you have to wait for all of the code to compile before you can even start up your application in the browser.
I had an experience at a previous job on a Vue application that used Webpack.
Start the application, go and get a snack, come back, it's done, it's ready, now I can finally get to work.
Depending on the file that you change, all of that would have to happen again.
With Vite, because everything is ES module, it's A, every single file, if you make a change, only that file is replaced in the browser.
The browser is acting as the bundler because you're just sending all of the code to it and it's loading things using the native syntax.
The other benefit to that is if you're on a page and only some of the code is getting loaded on that particular page, Vite won't bother compiling the rest of it for the time.
It will only load the route that you're using and it will only load the code that you're using.
Right, and when you're talking about native imports, we should clarify that CommonJS imports are something that bundlers know how to pack up into a bundled asset, but browsers have no idea what require means.
If you see require in the browser, it just explodes and says, I don't know what that is.
But if you say import...
CommonJS is a remnant from the early days of Node where everyone said, well, we need a way to bundle things and we need modules.
And so between the front end needing something like that and the back end needing something like that, the pseudo standard that was developed was CommonJS.
But when the TC39 was working on how do we actually standardize this?
There is a community standard right now. They felt that going with require and going with the CommonJS syntax was not what best served the JavaScript ecosystem.
And so they went with the import syntax that is more modern, we're more familiar with today.
So that is what is used in the browser by default.
And you could actually do this without a bundler as well.
If you wanted to create a section of your code that used ES modules in the browser, no compiler necessary, you would just have to set the script type to module.
And suddenly you can import things and you could export things if you really wanted to.
And everything would work as you're used to when you're using a compiled language that uses that same syntax.
You said you need to change the script type in package.json?
No, just in the so if you have an index.html file, and you wanted to import JavaScript as a module, you could just create a script tag and say script type module.
Gotcha. Yep.
And then you can use your import syntax and use that as you would if you were using a bundler.
All right.
It's a little different, right? Like if it's a script type module, can you not do things in the top level? Everything has to be like you can export things, but you can't just have top level code. Am I understanding that correctly?
I believe there are some restrictions. I don't remember exactly which ones they are.
Okay, but it's supposed to be better for tree shaking and things like that, right? Like for static analysis purposes, there are some benefits to ESM standards.
There are, if you're using a bundler, yes.
The difficulty with ESM, even today as a standard, is that if you're using ESM in production, there will be a waterfall of HTTP requests.
So if you have a large single page application, you're going to fire off JavaScript request after JavaScript request until the whole thing is loaded that you need. And that's not really ideal, even today.
Wasn't that what HTTP 2 was meant for? Solving that kind of issues?
I think the main problem is that if you load up your index.html file and it references a single JavaScript file, you fetch that file, but then it's going to reference other files.
So it's not that you can't do the requests in parallel, it's that it doesn't know what requests to make.
Yeah, of course.
Yeah. And there do exist plugins that like hoist module preload directives to figure out what that waterfall is going to have and then add preload directives to tell you.
But yeah, generally, people just in the production build just turn everything into a single bundled asset instead, or well, not necessarily a single one, but they chunk the assets rather than just having everything be ESM imports.
So if somebody wants to try using Vite with Elm, what is the minimal thing they can do to get started with that? And what's the simplest way they can get started with that?
So if someone wanted to take Vite and just run, for example, npm create vite, set up a new project, whether they're using another JavaScript framework or just vanilla JavaScript, the first step that would need to be done is install the Elm plugin.
And that plugin is just called Vite plugin Elm. And it is lovely. I've been using it for some time on my own projects. I'm sure you're familiar with it as well, Dillon.
Then in your Vite config, there's a Vite.config.js file. And you have two, as a bare minimum, you need two imports. One is import define config from Vite.
And that gives you the configuration file and also gives you TypeScript typing, which is helpful as you're working in that file. So you don't need to be constantly referencing the documentation.
And then you need to import the plugin itself. And then as far as the configuration goes, the way the file is structured, you just say export default define config, which is a function, takes an object.
And as a key, you say plugins, which takes an array, and then you invoke the Elm plugin as a function inside of that array, and you're done.
That's all you need to do to integrate Elm into a Vite application. From there, you of course need to run Elm init, create your Elm file and import it into the JavaScript. But you can import it as if it were JavaScript at that point.
You can just import Elm from main.elm and instantiate your application as you normally would. So that would be the bare minimum to get Elm working inside of Vite.
What I would recommend as a simple way to do that instead is, this is a shameless plug, by the way, I have a template for creating Vite applications using Elm that includes nice tooling such as Elm test and Elm review and some example code to go with that as well.
So that would be my recommendation. And that is just called Vite template Elm. And you can find that on GitHub, lindsaykwardell slash Vite Elm template. Sorry, I said Vite template Elm and it's Vite Elm template. I'm good at knowing what my own things are called.
Yeah, that's great.
What we tend to do is just name things the easiest way. The only question is like, which order is it?
I mean, you could have said template Elm Vite and that would be terribly wrong. But we would still get it.
Yeah, it's got to be Vite first. And I think I went with Elm second because that's the language. I don't know. Yeah, so that would be the simplest way to get started. So you get the hot module reload, which actually works with Elm as well. It's lovely. I really appreciate it.
You also get the integration with Vite static asset handling. Vite not only can handle TypeScript and JavaScript and WebAssembly and CSS, but you can also import images straight into it. And there's a nice little plugin. It's an Elm package that will prefix things properly so that Elm code gets compiled to JavaScript code that actually handles the assets in a way that Vite likes.
You can do it manually. It's just a string, but it's nice to have something a little stronger. The tooling is all installed via Elm tooling, so you can spin things up in your CI CD. So you get Elm, Elm format, Elm JSON, Elm test. You get Elm review and unit test examples. And there's a GitHub Action CI for running tests, just as an example. So that's what the template includes. It's not intended to be like, this is the best solution, but this is a simple solution that shows everything you would need.
Yeah, it's pretty exciting how little boilerplate there is for getting started with something that is not a cute tool that sort of gets you started and then you outgrow. It's a very simple setup that gets you up and running quickly. But then it's the tool that everybody uses now that has a ton of support and can do whatever you grow into doing later. So that is a breath of fresh air, for sure.
I mean, Webpack also did that, but you had to configure it a lot.
Yeah. My inspiration for this template was actually using the Create Elm app, which is built on Webpack. I had an application written in that and I kept running into situations where I felt like I needed to eject and then handle the Webpack config myself, and I just really didn't want to. So when the plugin came out that supported Elm and Vite, I immediately ported the application over to Webpack.
And then backtracked to a template that I could reuse in future applications.
Yeah. Do you also have the concept of ejecting in your template?
There is nothing to eject. The nice thing about Vite is everything is self-contained, but if you need to override something, it's very easy to do so. And that philosophy carried over from Vue.js as well, where you had a config. Yes, it used Webpack, but you didn't need to worry about ejecting. You just extended it with your own stuff.
For those who don't know, ejecting is when you use Create Elm app or Create React app, you run commands through that CLI, like Create Elm app, Create Elm app run, Create Elm app start or whatever.
But then whenever you did something too custom, you would have to opt out of using Create React app, Create Elm app. And at that point, you have to eject and you're on your own. And I found it always very scary, like, oh, what if I want to do something custom?
Oh, now I have to eject. Oh, now there are so many things that were handled for me automatically that are not anymore. So I'm really happy that you don't have that concept. It's just like, these are the scripts and you do whatever you want. And they're all simple.
That's really nice.
And if you want to extend it with a JavaScript framework, like Vue or Svelte or Solid or React or something, it's really easy to do so because it is just using Vite. I have the opposite problem, Dillon, where I want people to have all the foot guns.
Because I don't want to lock it down. I want people to play with it and explore and feel comfortable with Elm, interacting with the tooling that they already have.
Yeah. I mean, the beautiful thing is like all these Create React app, Create Elm app, they were addressing a real need, a real pain point at the time, which was that Webpack was this, you know, very verbose configuration that like you go and copy paste these like very complicated setups just to like, tell it you want like raw imports for CSS.
Or you want like, you want it to transpile Elm and things would break frequently. And you're just like, I just I just want to like import an Elm file. I just want to import SCSS.
Like, can I just do that? And it's like, yeah, well, here's this configuration. But there's this one thing that's kind of broken in it. And it made things more customizable than they needed to be.
And so it gave you an overly general purpose way of configuring things, which most of the time isn't what people want. They just want to use SCSS. That's, that's all. They just want to import Elm. That's all. And so now...
I don't know if you can tell that Dimon had horror stories about Webpack.
Well, Elm Pages v2 was built as a Webpack plugin. So I have horror stories. It was painful. It was very painful. Writing custom Webpack plugins was very sparsely documented and error prone and difficult to debug.
It was like, honestly, probably the hardest technical accomplishment of my career was getting that thing working. It was... And I'm not proud of that. It just was.
But so like create React app and create Elm app were addressing a real pain point, which is just you needed a simpler abstraction for how to configure a basic Elm app or basic React app.
And then Eject was, well, let's address the problem that you might outgrow that and need to do something more custom. But if you don't, then we can hide some of that complexity from you.
But now with Vite, it's like, well, there's really not that much complexity to hide. You just use the Elm plugin and now you can import Elm.
Like we don't need to create a wrapper tool that abstracts the complexity of the basic setup for that for you.
So, man, I think it's a pretty good time in the web ecosystem, I would say. I think we've really gotten some nice, nice tools.
So, yeah. Are there any other kinds of assets that Vite can process that you enjoy using, Lindsay, when you're working with Vite?
Primarily what I've used has been CSS, TypeScript and images. So I haven't dug in a ton to all of the flexibility that it offers there.
I did play around a bit with WebAssembly and compiling Rust and running that inside of Vite. That was really fun.
I don't know Rust well enough to make use of it, but I did some simple addition and called it in JavaScript. So that made me happy.
One of the other features, though, that I really like that ties into this use of standards is using web workers inside of Vite.
So when you instantiate a web worker, there's the web standard way of doing it. And Vite just supports that standard.
So if you want to instantiate a web worker, you just call it the exact same way. Or there's a special syntax as well that they offer from a previous version of Vite, but it's no longer the recommended way.
So you could call new worker just like you would if you were in a standard JavaScript file, reference the URL for your worker JavaScript.
Or you can import worker from worker and then append it with a query parameter of worker.
And you want to give people a short introduction to what a web worker is?
Absolutely. Web workers are if you have a long running or high calculation task in JavaScript or just the front end in general, and you need to run that.
Typically, if you're just doing it in the main application, you're going to cause issues with the browser.
Example of this can be CSS not updating properly if somebody moves their mouse and the hover animation doesn't change, that could be what's happening.
The goal of a web worker is to get it off of the main thread and into its own subsection of the browser.
And then you can pass messages to and from the web worker just via JavaScript.
So, for example, on my blog, I wrote a blog post about this where you could have two Elm applications, one being the view layer and one being the web worker.
And these two Elm applications are just communicating via this messaging protocol and then passing the messages into their ports.
So it becomes an effective way to have two Elm applications talk to each other if you need to do complex calculations inside of Elm.
Yeah, Elm's architecture is actually very interesting for this because of the way it separates the sort of DOM mutation.
And because of the whole virtual DOM diffing and everything, conceptually, you can really separate the state part of it from the DOM updating part of it,
which is basically what web workers can't touch the DOM, but they can run code.
And it's a good kind of way to split things up.
Yeah, and the fact that Elm has platform.worker that doesn't require looking at the DOM,
so you can just render that in a headless environment such as a worker or a node.
But you can use that to instantiate an Elm application that can then just communicate via ports.
It takes an input and that's all of the inputs to its update function.
Yeah, I think this could definitely be a really interesting thing to experiment with.
I could imagine at a framework level abstracting this way, especially because I think there's a clear segregation between the processes.
So you have to use array buffers to send messages back and forth, which is a little bit awkward.
Can you send JSON as well, maybe?
I believe you can just send JSON.
You can send JSON too? Yeah.
My side projects in Elm is a turn-based strategy game and I'm using a web worker to do the AI calculations.
So I just send the game state over as JSON.
So from Elm to Elm again?
Because it's too slow to have it in the Elm main thread?
Yep. The problem I was having in the game context, in a late stage of the game,
there were a lot of pieces on the board, there was a lot of options available to the computer,
and it would have to cycle through all of the cells, all of the units, all of the combinations of units,
and make a decision on what to do.
And when it was doing that in the main thread, all of the CSS would stop working.
So if you tried to do anything, or even just open the menu and say, I'm done playing, get me out of here, you couldn't do that.
So I switched that all to being sent via JSON to a web worker,
and then just decoding it back into Elm, performing the calculation, and then shipping it back.
So it can still take time, because it's having to do all those calculations, but it's not blocking the CSS anymore.
That would be really interesting if there was some abstraction where you could basically say,
here's a message to send back when this long-running computation is done on a web worker thread.
And then you could say, I'm awaiting the AI response for this, and I'm going to show this as loading
until I get that message and update my model, having gotten the computation from the AI's prediction or whatever.
Could be really interesting.
So Vite, why do we need a special syntax for importing something as a web worker with Vite?
How does it help us with that?
I believe the original problem was how Vite was doing its compilation to ES modules.
The worker syntax was just not playing nicely with how the bundling was happening under the hood.
So the version I was working with at the time, I believe, was Vite 2.
Vite 4.0 has now released, and looking at the docs, they now support the standard new worker syntax.
And when you were talking about this, importing this WebAssembly Rust,
was Vite actually running the Rust compilation step for you, or was it importing Wasm that you had compiled from Rust?
There is a plugin for Vite that will compile Wasm.
However, it still has to be a separate step from running the Vite dev server.
But you are using the Vite config at that point, you're using the standard tooling that you're using for the rest of your application.
You just need to compile that first, then start your dev server.
So when I was playing with it, I had a separate step before actually instantiating the Vite dev server.
But I could just run npm run dev, and I didn't have to think about it.
I don't understand. How come you need to have a separate step for that?
It may have changed, but the last time I looked, the Wasm file isn't actually generated until you do the compilation.
Otherwise, you're just writing Rust files and you're in the Rust environment.
So when you're compiling Rust, you need to say, I'm targeting WebAssembly,
and that's what generates the Wasm file for you to import inside of Vite.
I thought of the way that Vite worked is you have an index.html file, and you have a bunch of imports.
And if you import something like something.wasm, then it figures out, okay, I know this file, and it comes from this step.
Is that the problem? It doesn't know which task makes it?
I believe if you updated the Wasm file, it would work fine.
The setup that I was using had it as part of spinning up the dev environment.
So I don't think it would be hot reload in that sense, because WebAssembly can't do that, I believe.
But you do get the benefit of if you do a refresh, then it's there.
I wonder if you would be able to set up like a Vite plugin to say when I import a Rust file, compile it to Wasm and then import that.
I would love that.
So you mentioned that there is hot reload even for Elm.
Can you explain what it does exactly and how well it hot reloads?
Sure. So for people less familiar, hot module reload is if you are working...
I'm going to use JavaScript as an example.
If you're working in a JavaScript application, and you have three different files that you're working in, and you change one of them,
hot module reload first will grab that new code that you've written, load it into the browser.
But also execute that and make the change.
So if you're changing what the UI should look like, it will make that change for you.
If you're changing the logic of a function, it will make that change for you.
It will replace the code, and you don't have to refresh your entire page.
I feel very spoiled by this feature whenever it's available.
I've tried working with other frameworks that don't have it, and it makes me very sad.
So where it comes in handy in Elm is the same.
If you're working in an Elm application, and you just change the logic of a function, Elm will hot reload and just replace the logic of the function.
The downside where it comes to Elm is if you're modifying functions in the same file where you define what your model is,
or where the core of your application is, it will hot reload that whole thing.
So if you are working in an Elm application with a single file, and you make a change, it will reload the entire file.
So your entire application will initialize again.
So if you are breaking your Elm application down into separate files where you've got a file per type, for example,
and that type has certain functions, and you make the changes to the file of that type, that file will reload, your code will continue to run,
your model will still be there, everything will be in the state that it was.
But now the new functions will be available that you just wrote.
So it works with Elm, and I still highly appreciate not having to hit the refresh button on my page myself.
But there are some gotchas because of how Elm works and where the model is stored.
Right, yeah. I think I remember Simon Nadal talking on this episode for Elmwatch about techniques he made to improve that.
But yeah, this sounds pretty hard to do. Like if your model changes, for instance, then you have to refresh the whole page anyway.
Although now that you mention it, I do remember at one point changing my model, referencing the key, and seeing a null appear on screen, which was very weird.
Yep, that's not wanted.
Yeah, I find most of the time, the reason I want hot module reloading is if I'm tweaking a view,
I don't want to have to keep finding the state I was in that shows that view.
But if I'm changing my model and the way that messages are flowing through the system,
then I'm fine just refreshing and starting that process over. So it usually works pretty well.
Yeah, I've been pretty happy with how the hot reloading works in Elm. Similar experience.
It helps a lot more when you're working on the UI layer.
But again, just not having to think as much about, am I looking at the correct version of my application?
Did it refresh? Did I refresh the page?
It helps a lot just with the process of building an application. So you can look at your code, rate the changes, switch back to the browser.
And because of how fast Vite is, typically it's already done by the time you get back.
You're not alt-tabbing fast enough. That's it.
I need to work on my alt-tab game. That's what I need to do.
Why might someone consider alternatives to Vite? Maybe you're all in on Vite, but I'm curious.
Do you think there are use cases where someone would want to not use Vite with Elm?
That's a good question. I don't. So I will fully admit I am all in on Vite.
I came from the Vue ecosystem. Evan, you created this. I really appreciate his work and I just kind of stick with it.
The fact that the rest of the JavaScript ecosystem has gone in this direction as well gives me added comfort in that.
That said, I think it's perfectly reasonable to take a different course if that is what feels more comfortable.
And I think that's what it comes down to, is comfort.
So, for example, there's Parcel, which you don't even have to use a plugin to get Elm working. Elm just works.
There's tools like ES Build, or please don't use Webpack, but there's Webpack too.
If you really want to go in that other direction and not down this path,
I think there's some kinds of developers that really prefer that configuration of all the nitty gritty over having something that's pre-configured that they can update.
The other big one is if you want to use ES Build and you don't want all the other benefits of Vite, you could just use ES Build, for example.
I know plenty of projects and companies that are doing just that if they want to use Vite.
Whether it's Elm or not, ES Build is a wonderful tool on its own. It's still in development, but it's pretty good.
So if you didn't want all the extra goodies that come with Vite, they don't benefit you for whatever reason.
It's perfectly reasonable to just go with something more lower level and just get a bundler.
I think those would be the main reasons I would go with something else.
The only one that I knew about and used sometimes was Parcel.
Just because it worked without any configuration or so.
And the alternative at the time was Webpack.
There's one that works without a configuration and the other one that doesn't work with a lot of configuration.
So the choice was quite easy.
I wouldn't be able to tell what the difference is between Vite and Parcel.
I think in many ways that's a good thing.
If they do the same thing, yeah.
They do pretty much the same thing for what you need.
And so it comes down to a preference, which one feels more comfortable to use.
I think for me, the big reason I lean towards Vite besides I came from the Vue ecosystem is
I'm jumping between other frameworks a lot of the time for other side projects.
And being able to, for example, in a Nuxt application, Nuxt.js, it's a server-side rendering framework for Vue.
Because it's using Vite, I can just import the Elm plugin.
And now I have the power of Nuxt on the backend and the components if I really want to.
And then for anything that feels like it should be in Elm, I can just render that in Elm instead.
And I can just mix and match a whole lot easier.
And similarly, going the other direction, if I'm a Vue developer or I'm a React developer
and I want to try out Elm, or I want to try out Svelte, or I want to try out whatever, but we're an Elm podcast.
If you want to try out something else and you're using Vite already, it's as simple as just getting started.
If you're using Elm, just plug it in.
If you're wanting to use Vue, just plug it in.
And then you can start using that thing, whatever it is.
And the fact that so much of the ecosystem is already built on that, you've got Nuxt, you've got Astro, you've got SvelteKit, SolidStart.
There's just so much that's built into that.
I really like the ability to jump between the different tools and the different frameworks and the different applications.
And it's all the same configuration underneath.
And I know what I'm getting into.
So that's when I'm evaluating a new project such as Elm Pages v3, I'm looking to see what is it using under the hood.
And, oh, it's Vite. Awesome. I'm probably going to use this.
Yeah. I was adding something to the docs site for Elm Pages for basically like, I think I even saw some GDPR thing about some litigation because people were loading Google fonts directly.
And it was violating people's privacy because it was unnecessarily pinging Google servers when people loaded a page just to load a font.
And I'm like, wow, that's been like this standard way I do things, just load a Google font.
And for a long time, people said, well, it's a good practice because you cache the font.
So if somebody else has loaded it with Google fonts, it will be in their cache.
But actually, that no longer happens on browsers because they realized that that was like a fingerprinting vector that could let people identify a user based on which assets had been cached on their site.
So caches are sandboxed by domain.
And so you no longer have even that benefit.
Plus, when you have to connect to another domain, you have to do the whole handshake and open up that connection to another domain.
So it's faster if you load the asset from the site that is being hosted at.
And so how do you how do you do that?
Install a single Vite plugin, tell it the Google fonts you have, and then it just knows how to do it.
And it's so easy.
And I can do that now in the Elm pages doc site, which, of course, if it was not written in Elm pages, that would be embarrassing.
It's written in Elm pages.
But I just open up the Vite config and then add that plugin to for downloading Google fonts.
And it's so easy to use.
So it's pretty cool being able to hook into this like vibrant ecosystem, which like everybody has decided is a good idea right now.
So when do you get tracked now?
Is it when you build the application?
Google knows when you run your build.
Okay, good.
We wouldn't want to take that away from them, right?
As long as they can't shut it down, that's fine.
Google knows if you've been bad or good, so be good for goodness sake.
Oh, no.
I mean, it's true, but oh, no.
On Vite real quick and the integrations, I just pulled up an article written by Patak, who's one of the core developers on Vite at this point as well.
And there's just so many integrations that Vite has that I didn't even remember as we were talking about it.
So, for example, there's plugins for Ruby.
So if you've got Ruby on the back end, typically you're going to be using something like Webpacker to bundle your JavaScript as the default.
So there's a way to replace that with Vite, and you can just have a really nice tool for JavaScript on the front end.
And same for Laravel.
There's a plugin for Laravel that lets you just use Vite instead of its equivalent of Webpack.
And there's also tools like Cypress or Storybook that are more development tools.
You've either got your end-to-end testing or your component library or something.
But it can all use Vite across the stack.
You don't have to worry about these separate configurations.
You don't have to worry about which environment to make in and trying to get the tools to talk to each other.
It's just a streamlined way to use the same configuration and the same expectation of how the code works across your entire code base.
Just so I don't misunderstand it, Ruby doesn't get compiled to ESM, right?
Correct. That's just Ruby on Rails doing Ruby on Rails things.
Okay. So Vite is not only compiling for or bundling for a front end, but it's also able to run servers, right?
Is my understanding correct now? Because Laravel is a backend technology as far as I understand it.
This is more an integration so that as you're working with Ruby or Laravel, rather than having to compile your JavaScript via Webpack or via something else or not at all, you're integrating Vite into that experience.
Okay. Right. Which means if you try to download hello.scss, or when you render your HTML in Ruby, I'm assuming it has to post-process that with Vite.
So it's some setup that runs on the server to make sure it has those assets, I'm assuming.
Yep. Even in production?
In production, it's using a bundle at that point.
That said, because you mentioned Vite on the server, there is Vite Node or Node Vite. I think it's Vite Node.
And that's what Vitest uses in order to run Vite on a Node environment for running unit tests.
But it's not really intended, at least for the time being, as a solution to write a server in.
So nobody's going to be writing blog posts on how to set up Express using Vite Node.
Are you sure about that?
They might now, but...
I mean, if there's one thing I know about JavaScript developers, it's if it's possible to do it, they're going to do it.
And that's fair.
For better or worse. Sometimes it's better.
Sometimes it is.
All right. Well, Lindsay, do you know any good resources for getting started?
Do you have any recommendations for things to watch or listen to or read if somebody wants to learn more about Vite and get started?
I think that's going to be a nuanced answer depending on what you're looking for.
If you just want to get familiar with Vite, I would just go to the website,, and kind of just read through, find what's interesting for you.
There's the awesome Vite repository that has an amazing list of resources that are using Vite.
A lot of it's going to be about Vue and React, but there's a little bit of Elm in there.
And I think the third thing I would suggest is on YouTube, last year in October was ViteConf 2022, where I gave a talk on the same thing about using Vite and Elm together.
And I will admit that talk was directed more at people who aren't familiar with Elm than people who aren't familiar with Vite.
But you can check that talk out as well as all of the other talks.
It was a 24-hour conference.
There was a lot of content in there about all the different frameworks, all of the different tooling, and how this ecosystem is really shaping up and coming together.
So those would be the resources I would point at first, and then just kind of explore and see what feels good.
Amazing. And where can people follow you?
I am on Twitter at lindsaykwardell.
I am also on Mastodon at lindsaykwardell at
Fabulous. All right.
Well, Lindsay, it was a pleasure having you on. Thanks so much for coming on the show.
Thanks for inviting me. This was great.
And you're in. Until next time.
Until next time.