If you spend a lot of time on Hacker News, it’s easy to get taken by the allure of building a web app without a framework. There are a bunch of potential advantages (no bloat! bespoke to your project!) and being able to say you built something with minimal dependencies gets you Engineer Points.
That is, if you can pull it off.
I started a new side project recently. It’s a web-based graphics editor, so it needs to be a single page app. My time spent profiling SongRender Make your audio stand out on social media SongRender lets you create music visualizers, podcast clips and more to help grow your audience online. songrender.com for performance issues has made me a little wary of React for building interfaces that update at 60 frames per second, so I decided to avoid it. I’d go (mostly) vanilla and see how far that took me.
I installed lit-html Lit Simple. Fast. Web Components. lit.dev and got to work. “Components” were simply functions that returned lit-html template results. A big singleton at the top of the tree held onto all the application state, stored as a global variable within that module.
The first hurdle came when a component needed local state. I could have lifted it to the singleton, but that would have broken the component’s encapsulation. I noticed that lit-html directives can keep state, so I decided to use them to build a tiny component library — ignoring a warning from the lit-html developers that this wasn’t a supported use case.
My home-brewed library worked great… until I needed to run some code when a component appeared on the screen. I started digging through lit-html documentation and issues looking for a way to detect a directive’s lifecycle, but it became clear to me that going down that path would be painful.
Fair enough. Let’s avoid that trap. What about a small framework? I’d heard a lot of good things about Svelte, and although I was slightly worried about the size of the Svelte community I figured it would be fine.
My migration attempt quickly ground to a halt when I wanted a parent component to apply some styles to a child component. In React, I’d pass a class name in from the parent as a
className prop. In Svelte, that’s considered a workaround, and the actual feature is the subject of an RFC File not found · sveltejs/rfcs RFCs for changes to Svelte. Contribute to sveltejs/rfcs development by creating an account on GitHub. github.com/sveltejs/rfcs/blob/master/text/0000-style-properties.md .1
Maybe this is an example of dangling by a trivial feature Dangling by a Trivial Feature prog21.dadgum.com/160.html . But it’s so basic a capability that I’m surprised Svelte is on version three without an officially blessed way to do it. I ran into this limitation when I created my second component. Way before I got to try out any of the cool reactivity that earns Svelte all that buzz.
So I changed my mind and went with React. After an hour or so, I’d finished moving everything over — and my anxiety had vanished. I stopped worrying about having most efficient component system, and picked up work on the thing I wanted to build in the first place.
No, React isn’t perfect. It’s optimized for apps that make network requests and then display lists of things (i.e. most apps) which isn’t really what I’m doing here2. The performance is fine now, although I expect I’ll have to optimize as my project gets more complex.
But React lets me stop thinking about the framework. React gets out of my way. React is boring Choose Boring Technology mcfunley.com/choose-boring-technology . React is actively developed. React has a giant ecosystem and a giant community. React is battle-tested on some of the most visited websites in the world.
I’m sure there are technically better ways to build a highly interactive interface on the web, but life is too short for me to spend hours trying to figure them out. There are things I want to create, and the only way I can actually create them is to stop spending so much time on tooling.
For now, I’m choosing React.
The solution they seem to have landed on — letting the child expose CSS custom properties as props — is actually pretty cool, though I don’t like that Svelte will silently wrap your component with an extra
Although Dan, if you read this, the “animation pass” mode danabramov.bsky.social on Twitter “@0xca0a @sebmarkbage had an idea about a special animation pass mode that is limited in what you can do (can’t change tree structure) but that would be very fast. Might explore later.” twitter.com/dan_abramov/status/1133341485133438982 you’ve mentioned offhandedly sounds very relevant! ↩