top | item 34825676

Writing JavaScript without a build system

334 points| panic | 3 years ago |jvns.ca | reply

177 comments

order
[+] spankalee|3 years ago|reply
If anyone's looking for something like a framework that works with buildless workflows you might check out the project I work on: Lit ( https://lit.dev )

Lit gives you reactive components, declarative HTML templates, runtime encapsulated DOM and styles, interoperability with frameworks and HTML via web components, and a lot more - with no required build tools at all.

We take great care to make sure our libraries are usable without build tools. We support plain JS (in addition to TypeScript). Our libraries work straight from CDNs like Unpkg. When installed locally you can use a dev server that rewrites bare module specifiers or use an 8-line import map to make all of our core packages importable (we carefully keep all of our import specifiers compatible with the web and compact import maps). Components are inspectable with regular DevTools. You can even write components right in DevTools, or in a script tag in plain HTML.

I honestly wish this weren't a unique selling point of Lit, but as far as I can tell, of the "major" frameworks or component libraries out there (including React, Vue, Angular, Solid, Ember, Svelte, Stencil, Preact, and more) we're the only ones that fully work with plain JS within our regular mainstream usage patterns.

[+] mtlynch|3 years ago|reply
I've been looking for a modern JS library that doesn't require a build step, and I've always skipped over Lit because I thought it depended on a build until this comment.

Is there any documentation about using it without a build? All the documentation and tutorials seem to assume a build step. I've found several different "getting started" entrypoints on the site, and they all begin with "npm install" and "import {LitElement, html} from 'lit';" which won't work without a build step, correct?

[+] swazzles0|3 years ago|reply
We used Lit for a project a while ago and I definitely vouch for it.

We were embedding the component into a legacy application and needed to use a build tool for older browser compatibility but it was a breeze to work with even with the extra complication our build setup added.

Getting back to basics and targeting what most modern browsers support out of the box is a huge plus.

[+] herpdyderp|3 years ago|reply
I've been doing web dev for 20 years and Lit has finally allowed me to achieve my dream of what I've imagined web dev should've been like this entire time. Thank you for your work, please keep it up!
[+] no_wizard|3 years ago|reply
I just want to point out there is a great alternative from Microsoft called `fast-design`[0] which provides a more fleshed out experience than Lit does, in terms of out of the box components.

It does have lower level packages if you don't want ready made components too

[0]: https://www.fast.design/

[+] tymscar|3 years ago|reply
First of all, thank you for the work you have done on this, I love lit, use it all the time at work.

One thing I struggle with from time to time is that if I have a web component created in lit, and somewhere inside I have a dependency that has some CSS, that CSS doesn’t seem to work when the web component is consumed by JS.

The only workaround I found was to override the behaviour and instead of returning a shadow dom, return the component on its own, but that has its own downsides.

[+] sugarkjube|3 years ago|reply
Thanks for the reference, interesting, I will have a look.

vuejs can work without build system though. it's mentioned in the article, and I used to wwork that way. Eventually i made my own (minimal) build system though using quickjs and esbuild. Precompiling templates and minimising can be useful.

[+] olafura|3 years ago|reply
I really like Lit. It makes you in most cases write better reusable components, though you should still check all the properties to see if they are too reliant on some property.

We started using it because we knew it could be embedded into any other framework, we are using two others in the company. But are starting to use it for SPA also. Glad to see it growing quickly.

Thinking about possibly reworking our SPA to use Phoenix LiveView most of the abstractions are there already.

[+] iamcreasy|3 years ago|reply
How much do I have to know about web components before using Lit?

But thank you for your response. Did not know that I was looking for Lit all these time. Every time I want to dabble into web dev, I get frustrated by multitude of tools need to setup first. All I wanted was to import a library using script tag and start building.

[+] tgv|3 years ago|reply
> ... we're the only ones that fully work with plain JS

Vue (v2, don't know about v3) can also work without a build script, either with a render function, or a "template" (an string that mixes HTML with expressions).

[+] LAC-Tech|3 years ago|reply
Hey, long time lit-html fan here. Had always held off on using Lit itself because from the documentation it felt like build tools were required. Great to see that's not the case! Definitely going to check this out.
[+] Existenceblinks|3 years ago|reply
Most devs have no idea what lit-html + DOM event + sub 100 lines glue code (mvc) is capable of. No useThis useThat non-sense, just call render() manually, really, it's not a big deal like at all.
[+] insin|3 years ago|reply
If you use Visual Studio Code and stick a jsconfig.json in the root of your project, you can use JSDoc comments (and .d.ts files if you want) to get build-free type checking using its internal version of TypeScript:

    {
      "compilerOptions": {
        "checkJs": true,
        "target": "es2020"
      }
    }
[+] bitwize|3 years ago|reply
The appeal of JavaScript when it was invented was its immediacy. No longer did you have to go through an edit-compile-debug loop, as with Java, or even an edit-upload-debug loop as with a Perl script, to see the changes you made to your web-based application. You could just mash reload on your browser and Bob's your uncle!

The JavaScript community, in all its wisdom, reinvented edit-compile-debug loops for its immediate, dynamic language and I'm still assmad about it. So assmad that I, too, forgo all that shit when working on personal projects.

(The enterprise fucking loves React and Vue. Can't avoid it at work.)

[+] dgb23|3 years ago|reply
A thing that I liked when I started out was the fact that you could actually read all of the JavaScript on web pages. You found something interesting? Just fetch the source or open your dev tools (firebug!) and look at the code.

There's probably merit in minification and compressing code though. But for the types of projects OP is talking about it's going to be single digit KB of JS anyways.

[+] theteapot|3 years ago|reply
> The appeal of JavaScript when it was invented was its immediacy.

You can still do that. Nothing has changed. It's probably gotten even easier. Have fun implementing a complex UI without some sort of component Framework though.

[+] apitman|3 years ago|reply
Skipping a build system is my default for new JavaScript these days. The main suggestion I would add is using git submodules[0] for dependencies. It's a bit unwieldy and I always have to look up the syntax, but works pretty well.

You can have something like a `lib` directory in your project and place submodules for other projects in there. The dependencies need to provide some sort of a prebuilt artifact in the repo that you can import (or natively support ES Modules), but I've had good luck so far.

I'll also shout out jsdelivr[1] which is great for pulling dependencies directly from github.

[0]: https://git-scm.com/book/en/v2/Git-Tools-Submodules

[1]: https://www.jsdelivr.com/

[+] zapt02|3 years ago|reply
Getting older projects to run was a huge pain for me when I started working with JavaScript, but I have found two easy steps to get around this:

1. Use nvm + an .nvmrc file in your project to pin the major version of Node.js you are using. I recently got a five year old Node 8 project up and running with no issues using this method.

2. Try to avoid packages that has binary dependencies (like node-sass). 99% of binary issues are fixed when using the correct Node version, the only other problem that can occur is if you switch architecture (eg. x86 => ARM).

[+] TimTheTinker|3 years ago|reply
We recently solved this problem org-wide with Volta. It allows pinning both the Node and package manager (npm, yarn, etc.) version in each project's package.json file.

Even better, Volta automatically uses whatever node and package manager versions are declared in package.json -- no explicit call required. In other words, `node -v` and `npm -v` always return the version specified in package.json. It's been helpful for working with hundreds of repos and keeping CI and local dev synced on which versions to use automatically.

[+] doodlesdev|3 years ago|reply
Why rely on nvm to pin the NodeJS version you are using? That's natively supported as a field in your package.json [0], assumimg you're using npm/yarn/pnpm (you are).

Example:

  {
    "engines": {
      "node": ">=0.10.3 <15"
    }
  }
Also, god forbid you are actually running anything using Node 8 and whatever dependencies you have there, that's a recipe for security disaster.

The truth is that to use the NodeJS ecosystem we must have the discipline to keep projects up-to-date. I personally have a policy to update all my NodeJS projects at least yearly, even if they are abandoned, this includes all dependencies and the NodeJS version. Because I do it often most of the times there are not many problems to keep it working, and consequently updating is fast, but if I leave it for a few years and then come back I'll be in for a (bad) surprise.

[0]: https://docs.npmjs.com/cli/v9/configuring-npm/package-json?v...

[+] nicoburns|3 years ago|reply
If you use a `.node-version` file it will work with both nodenv and asdf
[+] eurasiantiger|3 years ago|reply
nvm is slow when changing directories, fnm is a fast drop-in replacement
[+] andai|3 years ago|reply
When I was learning HTML and JS in the early 2000s, most web pages had hand-written source code, and most JS I encountered was hand-crafted and unminified. So I learned programming by just reading the code of the pages I was on.

That's the main reason for me not to use a build system. You lose that "transparency" and accessibility of the code. With "raw" HTML/JS, the user can just copy paste your HTML/JS (often it was just 1 file for both combined!) into notepad, save as HTML and have their own website/"app"!

At least, that's how I felt until I got comfortable with TypeScript, and now I will refuse to use raw JS even for small projects... Alas! (Would be nice if V8 could strip type annotations and just run the plain JS hidden underneath... would also help with pasting TS snippets into the dev console!)

[+] handsclean|3 years ago|reply
I’ve had good luck splitting the difference by writing custom, dependency-free build scripts. You never get this accelerated code rot when you depend only on a mainstream language, it’s pretty trivial to implement a primitive version of bundling, inlining, macros, etc, and being primitive is far less limiting when you’re developing your build script hand-in-hand with your code.

Some things can’t be done like this, like TypeScript, for which I keep it to a minimum, look for stability, and often include a full copy in revision control.

[+] TimTheTinker|3 years ago|reply
AMD was a great module system precisely because the build step was optional. Running the build helped on large projects (for bundling, minifying, etc.), but you could literally deploy your frontend's `src` directory to production and expect it to work as-is. A nice side-effect was that local development only required a simple file http service.

I'm hoping ES module tooling, import maps, etc. get us back to that point.

[+] girvo|3 years ago|reply
Indeed. I miss AMD and tooling from that time, it was all pretty straightforward and browser focused. Combined with PHP and you could do some pretty neat things, too.
[+] dgb23|3 years ago|reply
> I’d love more tips for no-build-system javascript

1. MDN has a comprehensive guide on JavaScript modules [0]

2. A build system free way to build interactive websites could be to combine libraries like htmx[1] and or lit[2] or just the sub package lit-html[3]. Or just go with native web components and a bit of AJAX.

[0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid...

[1] https://github.com/bigskysoftware/htmx

[2] https://github.com/lit/lit

[3] https://github.com/lit/lit/tree/main/packages/lit-html

[+] kyle-rb|3 years ago|reply
+1 for Lit. It seems like they're mostly recommending that you use TypeScript and a build system, but you can 100% still just pull the library from a CDN with native JS modules [0].

It leans into a lot of the web components spec so the library is pretty small, and the templating system is just ES6 tagged template literals parsed by the browser's DOM parser and any embedded variables are updated dynamically [1].

[0] https://lit.dev/docs/getting-started/#use-bundles

[1] https://lit.dev/docs/templates/overview/

[+] tarkin2|3 years ago|reply
Use npm to install libraries one time only.

Try to use as few dependencies as possible.

You don't need a framework--components are achievable without vue and react etc.

Server side rendering and VDom are really not needed for most projects.

Think of Programming in JavaScript, CSS and F5 rather than Typescript and SASS as the equivalent of Marcus Aurelius' Stoicism.

[+] nonethewiser|3 years ago|reply
JavaScript and CSS are to Stoicism as Typescript and SASS are to _____?
[+] sebazzz|3 years ago|reply
> npm > Try to use as few dependencies as possible.

Hahaha. You joke.

[+] shaftoe444|3 years ago|reply
Great she mentioned https://unpkg.com/ which is a great place to start.

Import maps are also worth exploring. Documentation isn't great but I've managed to ditch npm on some side projects and use import maps and CDNs instead. Worth it for small projects without a doubt.

[+] crabmusket|3 years ago|reply
I agree with calling out esbuild as a great, minimal, stable tool.

I recently had the chance to start a small greenfield project. As we have a lot of "tribal knowledge" of Vue I decided to keep it, but go with a "minimum viable tooling" approach. This means esbuild with no plugins, .tsx instead of .vue files (because esbuild natively supports TSX), no hot module reloading (just esbuild's simple refresh-the-page-on-build), and absolutely no CSS-in-JS, just a single style.css using BEM conventions.

The same approach would probably work with any other frontend framework that can use JSX or plain JS files, even if you do run it through esbuild before it gets to the browser.

In my experience, most of the complexity that creeps into JS projects is fancy build tools. Using fewer, better tools which focus on standards-compliant content is the way forward.

[+] swsieber|3 years ago|reply
Hey, congrats. I have a similar setup, except using react.

And I'm torn about styles. I've inlined some and done plain css for others. Though it's still just plain tsx to esbuild.

I've loved it. You can add timestamps to log output by piping a command through the ts Linux command. And I have never seen the second change (it doesn't do sub second timestamps) between the start of a rebuild and the rebuild being done. It's amazing.

[+] jefftk|3 years ago|reply
I make a lot of small simple websites, I have approximately 0 maintenance energy for any of them, and I change them very infrequently.

I do this too, and no-build is definitely the way to go for them.

To make this even less effort, I log directly into the web server and edit them live. Once I'm happy I check in the changes.

[+] nathell|3 years ago|reply
One of my favourite hacks I did recently was in 2020 when soup.io was unexpectedly discontinued with short notice; to promote my scraping framework, Skyscraper, I quickly whipped up a content downloader. It produced an archive of assets, and I also added a rudimentary browser.

The beauty of it was that it was a single static HTML file, copied verbatim to the archive. The only moving part, apart from the actual images/posts/videos, was a file called soup.js, which was just `window.soup=` plus the content metadata as JSON.

I didn’t even bother to have a separate JS file for the actual code, much less a build system. I put the JS right into a `<script>` tag in the HTML. I used ES modules, HyperApp as a minimalistic React/Redux workalike, and mini.css for some sane default styling.

I was amazed at how far I could get with such a rudimentary tooling. 117 SLOCs of HTML+JS, working equally well served from a server and from a local filesystem, complete with pagination, permalinks, and a jump-to-page dialog.

Here it is in action, serving a friend’s soup archive: https://soup.tomash.eu/archive (warning: some content can be touchy and/or NSFW). View source for a glimpse of how it works.

[+] logicalmonster|3 years ago|reply
> I was amazed at how far I could get with such a rudimentary tooling.

Are you just 1 person working on a project?

A lot of "best practices" aren't particularly needed when you're flying solo and know your code and mental processes inside and out.

Where this starts to get hairy is when you have multiple developers all trying to understand one person's mental model of how the code is structured, all in one file that they have to work in.

[+] throwingtoofar|3 years ago|reply
For simple Typescript without node:

1. Typescript in VSCode.

2. Simple script to watch a project directory for changes to .ts files that runs...

3. ...'deno cache <file>' to transpile them to JS. Then copy them from the cache directory.

deno also has a bunch of options for formatting, bundling etc.

[+] pjmlp|3 years ago|reply
On personal projects that is my approach, vintage Web development, just like I was doing 20+ years ago.
[+] Waterluvian|3 years ago|reply
A lightning rod of an idea I’m sure, but would it help if typescript was accepted by browsers but ignored? Then I can write my typings in-line rather than in comments, and check them, but not have to transpile.

I guess “enum” as a special snowflake would fail this.

Where I’m coming from is that I can live without most build steps for a small project… but I can’t live without typescript.

[+] shadowgovt|3 years ago|reply
Python typing kind of went in that direction.
[+] textninja|3 years ago|reply
I can happily live without TypeScript (ka-chow) but at the same time I don’t see why not.
[+] mythz|3 years ago|reply
Vue.js seems to be the natural choice for progressive JavaScript Apps without a build system.

We've also removed npm build systems from our newest .NET Project templates which uses JavaScript, Import Modules to progressively enhance Razor Pages Apps, which I've also written about in:

"Simple, Modern JavaScript"

https://vue-mjs.web-templates.io/blog/javascript

As you have access to full Vue 3 you're still highly productive being able to use Vue 3's Composition API and Plugins & Components which are able to be loaded directly in the browser.

We're still using TypeScript definitions for enabling static analysis benefits for external libraries but have switched to JSDoc type annotations for App code to avoid any runtime transpilation.