Suggestion: perhaps call it Vanilla++? With the list of tools many (including me) would argue that it's not really 'vanilla js', but rather 'frameworkless npm'.
In my very humble opinion, vanilla js is esmodules, html, fetch and good old plain css. Basically a no-node environment, static hosting and no transpilation.
Prime doesn't necessarily need to denote anything specific, but often means 'different, but related'. It is common to use prime notation to reflect a derivative function in calculus, but like most things it depends on context.
I was pondering if "vanilla" should be part of the name as well. In the end I felt I got sufficiently close to the vanilla flavor you describe, and the name conveys the "as little as possible" approach. Hope it's not too misleading!
I do like the "plus" idea, now that you mention it! Will think about it :)
Vanilla JS has also been a long-time joke about how modern javascript has advanced far enough not to need libraries for a lot of stuff people still reach for: http://vanilla-js.com/
I'm not a professional web developer, but I do write web apps and WASM stuff. With JS being decent since ES6, do you really need all this crap? And if you do, are you going to use someone's side project that they refer to as "Dark Souls of web frameworks"?
Original author here. No, you don't need all of it; everything is optional (as stated in the article). It's just a boilerplate with some tools and conventions, so "using it" has little implications, I think. Other than that, I'm just trying to have fun out here :)
I don't get it. What is the point in this "boilerplate"? As I see it's just moving your node modules local, I mean I can do that without anything here. I'm missing something?
Looks like it's already suffered from 'issue bloat' of people suggesting small changes in the issues and the maintainer not being tough enough to reject the changes
The (vanilla) web was poorly designed with layers of accidental complexity accumulated though the years. It's funny that some people look at it as some sort of glorious past to strive to. You are simplifying nothing by removing tools/frameworks and shifting complexity to the application. It has to be somewhere.
I find "web platform poorly designed, therefore frameworks with 100s of dependencies" a bit too easy, especially when the user experience suffers in the end. I also believe that not every complexity needs to be solved with code. Isn't it a worthwhile exercise to figure out if something can be solved with (significantly) less code?
> It has to be somewhere.
I tend to agree; I think I'm just trying to not shift it to the user device's runtime.
One method of a simpler web is, ironically, porting traditional applications to the web. I mean compiling C or C++ apps to WASM and then running them.
Typically, then you get extremely rich and complex user interfaces with less work. The problem is though that this is still janky. It's not a smooth, or even viable, experience sometimes.
But it goes to show that making applications via the Web IS unnecessarily complex. Because the tools, being HTML, JS, and CSS were never intended for applications but rather documents. Personally, I think it's a sliding-scale type thing between documents and applications. Anyway, the standards have been expanded to try to address the application use cases but it's all "bolted on" stuff. And the document stuff also has to stay because that's still a primary use case.
Like, I would rather shoot myself than try to replicate photoshop in the Web. I'd much rather use Qt for an application like that.
Not having 100s of dependencies to bundle from is great too. In the jQuery era, you had jQuery, a few plugins, and the main js files. Now we’ve pushed backend’s logic in the frontend without the pragmatism of the tooling.
I assumed most of this was already standard practice. Although boilerplate is useful to have on hand. Two thoughts:
1. Vue seems to follow most of these paradigms, including "pass data down, bubble events up" and directory structure.
2. This is a stupid complaint, but putting *.ts files in a directory named "js" bothers me. Of course, it makes more sense if one doesn't like typescript and just uses ES6+ as I am wont to do.
In all honesty, I've never got to properly look into Vue, but it has been mentioned multiple times in the comments. Time to study it I guess :)
The "src/js" directory was initially called "src/scripts" but it kind of clashes with the top-level "scripts" for the shell scripts. Agreed it's not ideal though.
Packing your basic web site with 50,000+ SLOC worth of run time dependencies is quite a different story. When you use a framework like react, or vue, or svelte, you're bringing in a massive dependency tree, and you're also changing the semantics of the application code from web standard code to framework specific code. Instead of being an expert on the standards, you have to become an expert in the framework. There are compromises being made either way.
I think that one important thing missing from this project is a templating engine of some kind. Questionable addition though, since you're then going to stop writing HTML and start writing whatever template language. And it's a greased path toward going with a framework.
It struck me as weird too. If the author isn't trying to avoid build tools altogether then I'm not clear why they wouldn't just install Vite and simplify their life.
Looking at the package definition, there are no runtime dependencies, the only dependencies are development tools such as linting, testing, etc. I can definitely see how this can be misleading though.
Prettier, eslint, and the typescript compiler are great. The issue is the npm package gallore that exists currently. No one wants to write code anymore, instead using mini libraries for a function or two, resulting in ~8000 packages for a react application I had to work on.
these are all dev-time dependencies. The part that's contradictory is
"A simpler, more sustainable way of web development"
vs.
"Vanilla Prime is not meant to be easy"
I think it's a little naive to believe the JS build scene is somehow divorced from the JS runtime scene, when there's just as much complexity, churn, deep dependency chains and rapid obsolescence.
Not sure who said it but I was aiming for the "Simple Ain't Easy" trope here ;)
I agree that the JS build scene can be churning a lot as well (e.g. ESLint's recent config changes), that's why I tried to make everything as orthogonal and disposable as possible.
I appreciate this effort significantly. I am curious, though, if it can be extended towards backend development as well. As far as I can see, this is for static websites. The inclusion of NodeJS is strictly for running the other tools.
Original author here, thanks for the comment! I also think that backend would be an interesting subject to try and reduce, if only to not constantly wade through 100s of Dependabot pull requests... It's a very different scenario where you will want to optimize different things (code size usually does not matter as much, for example) and you have various deployment options (serverless, containerized, edge, ...) etc. so quite the Pandora's box, I think. Interesting nonetheless!
Can you explain a bit more about what you mean by “…extended toward the backend”?
IMO all the extra flavoring on the front end comes from everyone having their own approach to modeling UIs and managing state for UIs. I don’t see anything similar on the backend - HTTP server packages seem mostly similar, data access packages seem to be a fairly small/thin layer.
I’m not deep into the node ecosystem, help me see how Ben & Jerry have contributed to the backend :-D
I've found the space to currently watch for low-dependency "as modern vanilla as we can" backend stacks is the Deno ecosystem, including and maybe especially the growing JSR repository. There are some very nice low-dependency ESM backend frameworks and libraries growing "Deno-first" to keep an eye on. Many of them still run on Node and/or Bun, too.
I'm not following it. It starts great (the idea) but the toolset used is a big nonsense? Make it run in the browser with no external tool needed otherwise it is just the same old bloated approach.
I like to look at it as a spectrum from "truly vanilla" to "same old bloated". I've previously looked at the dogmatic vanilla approach here [0] and wanted to try something else this time, on a different location on that spectrum.
- tests test the same thing twice: once with a unit test, and once with an integration test
But those are minor pet peeves. My main one is: what is this, exactly? Neither the project itself nor the proposed combination of tools and directory structures show that this is somehow better or more sustainable than any other approach.
It's purely demo code. I personally subscribe to the "modules should be deep" school of thought (Ousterhout).
> tests test the same thing twice: once with a unit test, and once with an integration test
Again, purely a demonstration that Playwright handles E2E, integration, and unit tests equally well, not an endorsement of testing the same thing twice. Note the absence of jest, mocha, etc.
> somehow better
Hopefully, you can achieve similar gains to [0], e.g. 50% less time to load and 95% less bandwidth in that case, by avoiding frameworks and other runtime dependencies.
> more sustainable
Hopefully, code produced this way is less susceptible to framework churn and other changes in the environment. Also, knowledge gained by following the approach will be about the standard web platform, not about a particular framework (and version thereof).
I feel the author needs to be more specific about their definition and understanding of "vanilla".
This is a nice project, but the comments here show that the author is in a different world if they consider it a "no frameworks, no bundlers, no required dependencies. Just web standards, patterns, a unix-style toolset, and some elbow grease" or "simple" or "more sustainable".
It's got like 7 dotfiles and a 3500 line package-lock.json.
Isn’t this just reinventing Vite, but badly? Literally anything achieved in this project will be handled out of the box, faster, with less configuration necessary, and more documentation available, by just using Vite…
I don’t mean to dismiss the authors efforts here, but if you actually value your time, go with standard tooling and don’t overdo it with the runtime dependencies. There, saved you an afternoon.
FWIW Vite would indeed handle the responsibilities of sass, typescript (partly), s4d, terser, cbst, vendoring (but not eslint, stylelint, prettier, playwright, c8, exdom). It's certainly a good option!
One thing to consider is that Vite promotes a plug-in ecosystem, of which many plug-ins are maintained by individuals. I think this increases the chance for incompatibilities over time significantly.
Also, having more documentation is not necessarily good. Each tool in Vanilla Prime has its own documentation, but they are self-sufficient. Vite combines lots of things in the background, which already have documentation, then adds their own documentation, and often requires additional plug-ins that take care of integration.
Maybe Vanilla Prime could be the car you can repair yourself, whereas Vite is the modern car that is so tightly engineered that repairing without external support becomes difficult.
This is where I find bundlers to still be useful and friendly. Rather than bundling everything, just spot bundle individual dependencies as necessary. The great thing is that individual dependency vendoring is especially where bundlers stop being massive balls of configuration and start being script friendly one-liners again.
A direct example I've used in several places is bundling all of the (top "barrelled") RxJS library into a single vendored ESM file with esbuild:
It's also a useful counterpoint to the request for more library authors to publish to package managers single big ESM files, by taking that power back and doing it proactively with available modern bundlers in a nice-to-have middle ground between bundlers being too common and top-level and "no bundlers".
> Can routing be done reasonably well with vanilla means?
I think routing has a bad reputation for being more complex than it is, especially from a vanilla perspective. There's basically three pieces to routing: Event Handling, Data Structures, and often Regex. Individually all three are easy to do, it is often only in combining the three into the same problem/library that a lot of the unintentional complexity creeps in.
1. Event Handling:
If all you want is classic SPA hash-routing (using just the #fragment to route by), you only need to worry about the hashchange event [1]. It is simple and high-level. The classics are classics for a reason and a lot of SPAs frown on hash-routing, but it's still a great, simple option and should not be overlooked even if it is currently "unfashionable".
If you want to go down the road of fancier SPA navigation with fake but nice-looking URLs (https://myapp/about instead of https://myapp/#/about, for instance) there's only one Vanilla JS API to learn, the History API [2]. It's got a small learning curve and the popstate event could be a bit smarter, but it's still relatively easy to work with.
(One source of complexity in "modern routers" is that they often include or are prollyfills for the even higher-level, and still Experimental, Navigation API [3]. It is subtly nicer to work with than the History API, though yet another level of complexity/learning-curve, but for Vanilla/light-weight/dependency-free stuff learning the History API is "good enough" for today.)
2. Data Structures
You want a way to "register" routes and then iterate from them. Routing libraries will offer complex plugin systems and nested trees of routes, but you can always start simpler with hand edited Records or Lists.
3. Regex
You need a way to match a URL's path and capture variables from it, and Regex is ultimately the tool to do that. I'd argue there's nothing more Vanilla in JS than crafting your own Regexes. (And Typescript now has better than ever compile time checks for them.) But I also understand why a lot of people wish to avoid writing Regexes directly in routing. There are small dependency-free/light libraries just for that part of routing such as path-to-regexp [4]. (TIL there's also an Experimental attempt to provide such a library directly in the browser called the URL Pattern API [5].)
But you also might not need Regexes at all. If you don't have a lot of aliases and you don't need to capture variables, sometimes all you need is "just" a switch with string matching.
In my very humble opinion, vanilla js is esmodules, html, fetch and good old plain css. Basically a no-node environment, static hosting and no transpilation.