Stencil: I Think I Found My Frontend Home
Ben Lovy
・6 min read
Getting Real
I've just started exploring
stencil and am already nursing a crush.
I will be the first to admit that I don't particularly enjoy building frontends. It's the reason my own personal website is, well, sad. It's been incomplete and neglected since the day I forced it out in the open, and largely untouched from that pathetic, half-finished state. This means it does not reflect any recent work I have to show off, at all, or accurately demonstrate what I'm capable of in any significant way. Useful, right? Why even have one, at this point.
I built that website as a side-effect of a DIY static site generator exploratory project in Rust. The fun part of that project for me was building the static site generator and web server. To see if it all worked I needed some test stuff to feed it so I figured I may as well build a personal website. Poor thing never stood a chance - talk about falling short of even the bare minimum.
One of my goals for 2020 is to completely rebuild it into something that isn't embarrassing, and hopefully hone a useful skill and learn more about the domain along the way.
Runners Up
Choosing a stack in the JavaScript world is daunting, especially when you don't actually know what you're doing. Before settling on
stencil I considered the following options:
My "aesthetic" for tooling choices is barebones and simple. I prefer to build my own abstractions, at least when I'm learning a domain, before trying to choose someone else's, and also think that for this website even vanilla JS is probably overkill, everything I want to present can be presented statically and simply. All I really need is HTML and CSS. I decided not to go this route because I do want to open myself up to more flexible growth paths in the future, and this is the most labor-intensive route if I go beyond the absolute basics. I also find it extremely boring to do, which means I'd be less likely to finish.
The framework I've spent the most time with is React, so Gatsby was a natural choice for the next level of organization. I liked it a lot, and developed a healthy appreciation for GraphQL, but ultimately found it was like "React+" - you're really on your own in terms of ecosystem, and there is a ton of complexity going on to achieve what (for me) is a simple end goal, even after I got my head around the basic model (both React itself and then Gatsby over it). I still think highly of Gatsby in general and would revisit for a larger, more complicated project.
I finally looked at Svelte, because I like the idea of an AOT compiler handling this complexity rather than a bunch of complex runtime logic. I didn't spend a lot of time with it, though, because it looked like a whole new set of stuff to learn, as well designed as it is, and that's precisely what I want to avoid. The Sapper repo looks a little dead, too, but that's not always a valid indicator. I'm keeping my eye on this as well, though.
Why Stencil Wins
I want to be crystal clear: I am not saying that
stencil "wins". I am only saying it wins for me, for this project. I know I have a tendency to prod at controversial questions on DEV to see what happens, but this is not one of those posts.
Stencil is (mostly) just "Web". It's actually not a "framework" at all. Instead, it's a generator and compiler for Web Components, with some extra niceness built-in to streamline the process.
The Web is an inherently complicated platform, and frontend is not my primary target domain, so I do want some help taming that complexity. What I don't want is for that help to consist of a whole new set of specifics to learn which may or may not apply to anything else, and which has no guarantee of lasting relevance. That's been my experience with JQuery, Knockout, Ember, Meteor, React, Vue, Svelte, whatever else I've touched - even just listing stuff I've test driven makes my head spin and it's all new layers on top of an already complicated topic.
Now, it's a valid point that Web Components may very well represent yet another step along this chain. There's a phenomenal discussion here:
My favorite quote from that discussion came from @josepot, who noted that "web-components are just leaky abstractions built on top of other leaky abstractions". That's hard to argue with. Still, though, what isn't?
I like how Stencil is focused first and foremost on compliance with existing standards. To write my app, I need to learn TypeScript, not some specific other tool. I also love how easy it's made it for me to piece together my app. The
stencil generate tool can generate just your TSX or include a CSS stylesheet, a Jest
component.spec.ts file, and a Puppeteer
component.e2e.ts file. I'm forcing myself to use all of these for every component as an educational exercise, E2E testing in specific is something I've never experimented with, but it's great that it's all opt-in and you could keep it straightforward as long as you want.
Within minutes of loading up the template app I had added a new component and hooked it up to a new route with the handy built-in
stencil-router component, purely based on what I already knew about HTML templates going in and the example provided. Sold!
Example Component
For a simple example, here's a component I put together to display a United States postal address:
import { Component, Prop, h } from '@stencil/core'; import { Address } from '../../cvdata'; @Component({ tag: 'app-cv-address', styleUrl: 'cv-address.css', shadow: true }) export class CvAddress { @Prop() address: Address; render() { if (this.address) { return ( <p itemscope <span itemprop="streetAddress">{this.address.street}</span><br /> <span itemprop="addressLocality">{this.address.locality.name}</span>, <abbr title={this.address.locality.state.fullName}{this.address.locality.state.abbreviation}</abbr> <span itemprop="postalCode">{this.address.locality.postalCode}</span><br /> <span itemprop="addressCountry">{this.address.locality.state.country}</span> </p> ); } } }
It's just TypeScript, or an ES6 class, with some metadata stored in a decorator. JSX is a polarizing tool, but I think it's a good fit for the problem it solves. I've never used Angular but I think the "metadata-in-a-decorator" pattern is not entirely dissimilar.
This is the over-engineered
Address TS interface I defined to pass in as a prop:
interface AddressRegion { fullName: string, abbreviation: string, country: string, } interface Locality { name: string, state: AddressRegion, postalCode: string, } export interface Address { street: string, locality: Locality }
This is precisely why I like using TypeScript over JavaScript - familiarity. I'm used to tools that let me define the shape of my data up front, and get frustrated when debugging problems in vanilla JS that would have been caught by a typechecker. By setting this up before implementing, it's easy for me to dig through the object passed in as a prop. I know it's not technically any different than doing so without a defined interface, but I like it, dammit. Having my editor know what I'm doing is a huge help - I like my red squigglies showing me when I'm being stupid. Typechecked props by default is just awesome.
This was easy to pop in the larger context:
render() { return ( <div> {/* other stuff... */} <app-cv-address address={this.data.address}></app-cv-address> </div> ); }
No need to import anything, just use your new component! It couldn't be easier, and I'm off to the races.
Beyond the perceived technical merits of this tool, beginning to build this simple application with
stencil is pretty much the only time I've ever actually enjoyed the process of writing a frontend application. I thought Gatsby was incredibly cool tech, and Svelte was a solid implementation of an elegant idea, but "fun" is a little more elusive for me in this space.
Why exactly this tool got me there when so many relatively similar others haven't is tricky for me to pinpoint, but I'm not fighting it. It's simple to get started with, based around core Web technologies that I know will continue to be relevant, and will allow complexity to grow as needed as opposed to including it up-front in a massive convoluted "starter app" template. I'm hoping this tool stays around for a long time and see no reason to hop around, at least for personal use, for the foreseeable future.
Photo by Karim MANJRA on Unsplash
Design Patterns in Java
I thought it would be a fun to write a series of blog posts looking at differen...
I also landed on Stencil after trying all of the other big frameworks and built my site with it. It just feels right. It seems like other front-end libraries are "hacking" the web to get it to do their bidding, while Stencil embraces it and provides the same convenience as those frameworks. It is hard to justify the investment of learning a front-end framework that is not compatible with anything else and may eventually be replaced by the next big thing in 3-5 years. My only complaint so far is using JSX with it, I think I would prefer a template approach instead, but everything else has worked well.
My first impression was the same as yours, I would have rather like to use template, but now? I would like to use TSX everywhere 🤣
Cherry on top, I think, it could help developers with prior React experience to easily step in Stencil and likewise, for developers without React experience, it could help to step in React.
How tricky would this be to make work work with the tools as-is? I know the
stenciltool does operate on a set of assumptions but at a glance this seems like the sort of thing you could do.
I think it can be done. I set up lit-html in Stencil and it seemed to work, but was kind of weird because I had to create a dummy element to attach lit-html to and call
render() { render( document.querySelector("element") ) }. I'm sure that probably negated any compiler optimizations in Stencil. There might be a better way to do it, but I haven't explored it too much.
It's nice to hear of somebody else who doesn't particularly enjoy building frontends. It seems sometimes that everyone is so passionate about web frontend development. I think building web frontends is a mess and no fun, but because it's 2020 I can't get away from it. It makes me feel like I'm the only one that feels that way! Good to know I'm not alone!
I think some of that might be perceived bias - DEV in particular is pretty frontend focused but it really is just a subset of all the engineering going on - thankfully so!
Great article Ben, thx for the share 👍
If I understand correctly what the Stencil team is currently cooking and also understand correctly that you like Rust, I might not be wrong saying that you might even gonna love more Stencil soon 😉
You just made my month.
I've noticed that you have written some Rust articles, I know Rust/wasm is not production ready yet (that is of course always debatable), but did you take a look on that?, how far from ready is it?,
I'm working in a personal project with Actix-web and soon I'll start dealing with the client side, I haven't started a heavy research on the client side yet but from what I've heard I'm considering: Svelte, Stencil or something in Rust, maybe Seed or Percy, but there seems to be very uncertain yet.
Did you considered some wasp alternative?, any interesting discovery on that front? or is too new/experimental yet?
Yep - My old site is in
actix-web, and I plan to continue writing servers in
hyper. Part of my goal here is to get more familiar with a more mainstream tech, so TypeScript was a primary driver.
As far as frontends go, I've tried both Yew and hand-rolling with web-sys - I wrote a quick write-up of the latter here:
Reactive Canvas with Rust/WebAssembly and web-sys
Ben Lovy ・ Jun 14 '19 ・ 11 min read
I think "uncertain" is exactly the word. Not a lot of "1.0" level crates, so you have to expect breaking API changes. I think this should be revisited after everything has had more time to bake, but is an exciting tech to watch.
For simple frontend sites, though, Rust is overkill. JS/TS is the right tool for a lot of cases. If you're doing all your work server-side, then it's a little more robust. I really like
hyper.
For frontends, I've wanted to try
seedfor a while, would be curious to hear how it goes if you do. For rust web servers, I'm most excited about tide but again, we're a little early for it to be ready for use.
As for whether Rust is "production-ready" in a general sense, I think there are domains where it absolutely is, but the big thing that needs to happen is a larger set of "1.0"-stable crates.
I guess we'll have to wait and see on the client side, in the server side I'm really happy so far with Actix-web, it was kinda hard for me at the start but once you start to get in the groove you even start to enjoy the API docs :D
PS: If you find yourself in the curious mode, take a look to SQLx (is a Rust SQL toolkit fully async with async-std and has forbidden non-safe code 8-o ) I just found it and look very promising, far from 1.0, but looks great.
I was also thrilled with actix-web 0.7, but the 1.0 breaking change threw me for a lurch and I haven't spent the time to sit down and learn the new API - I really should.
I saw a reddit post about that - looks very cool. The Rust ecosystem is full of cool stuff. Today I'm playing with cxx, a safer way to interface C++<->Rust than
bindgen. It's like getting cool new toys every day.
I liked Stencil at first but found event dispatching and handling to be unreliable and buggy. Haven't given it a second try in a while. Did you find handling events simple and reliable?
I use many events and handlers and never had trouble related to Stencil, even the contrary, I found easy to implement and reliable. Events are a core mechanism of our editor.
Only difficulty I once had was with the selection API but that's not related to Stencil but rather with the platforms and the compatibility of such API with Shadow Dom.
Interesting - that's good to know. So far in this app I've only handled
onLoadand
onClickand haven't needed to dispatch anything myself, so it's way too early to tell. How long ago did you try it?
I think around August. A lot of the event handlers would fire only once or not at all.
Yikes. Will definitely be on the lookout for that, but so far so good.
Angular is pretty similar syntactically to Stencil, for what I have seen. Also, Angular has a way to create web components called Angular Elements. I haven't looked in to it much, but it's seems cool
Looks like Angular and React had a baby
Haha funny but true
Hah! Pretty much :)
The big issue for me with stencil, besides looking like angular, which I didn’t like, is the state of web components.
I don’t believe in a thick client. Web components are great in concept, but they are still a thick client solution. You are forcing a lot of things into JavaScript that don’t have to be there. You are also not ready for prime time. There are issues across the board with web components.
It’s wonderful if you love the developer experience that stencil gives you. I think it’s fine that we can disagree on that.
I think it’s really bad for the output of that to be a experience that has gaps in browser support without a lot of polyfills, and moves things into a thick client so the developer experience could be what you had.
This is why I don’t care for react as well.
Svelte is the closest I found to something that gives me the output I want. But only when paired with static site generation. And even then, you end up with things that end up as templates in h Th e JS bundle that should remain as CSS and JS.
I also don’t think we should of moved routing to the client over the server. I have unpopular opinions, but I have been doing this for 20 years.
I do think it's odd to not prefer Stencil because of the way it looks, especially given how esoteric and non-standards-like Svelte is. But that's just me.
As far as browser support and output: Stencil is building on an actual standard. The output is standards-compliant JS plus a couple of helpful utils. Every JS library does this, almost by definition. And Stencil's impact is small: smaller than react, angulary, vue, litelement, or svelt. ionicframework.com/blog/announcing...
I mean there are many more reasons. I just don’t care for how Angular looks. But my comments were more about thick clients and the state of web components.
Web components are a standard. That is true. I am 100 percent on board with standards. But we are not ready for them. And the standard we ended up with is much worse than the standard we were suppose to get (thanks Safari).
Nothing I have seen has shown the output as smaller than Svelte. One of the reasons we ruled it out was because of browser support, and output size. But I may be miss remembering.
Either way, I will jump on web components, when it makes sense for the work that I do, and if the adoption curve gets better by the community.
In general, building them without a framework does not appeal to me. Having them, even if they are standard, does not appeal to me for numerous reasons. And using a framework to improve the developer experience, cool. But then I should be using something that builds something I actually want. Hence Svelte.
I am not hating on Stencil. It’s fine. I just don’t think I want this thick client web we make every site out of. And the current standard of web components does little to mitigate that, if we put css and html in JS.
Why do you say that? I definitely think we're ready for them. Apple, Amazon, and Google are all shipping them to prod.
Open the article I posted earlier and check for yourself.
Stencil is the opposite of Thick UI. 0 framework, 0 esoteric syntax as in Svelte's case, just pure web-based development with a small vdom.
pianomanfrazier.com/post/comparing...
This is just one example. But yeah, it’s larger.
Web Components still require polyfills. The argument that these big companies ship them is fine, if you have the same user base as those companies and can alienate the same set of browser users.
I still have to support IE 11 for example. caniuse.com/#search=Web%20components
I mean, if they work for you, and your happy with the framework. Congrats. I don’t need to keep going against every point you want to try and dismiss. It’s simple. Moving CSS and HTML into JS is thick client still. Might be lighter weight than a virtual dom, but it has ramifications. I have to support a wider range of users than you, and hence can’t use the features without polyfills.
So like I said before, I don’t feel we are ready for mass adoptions. Most websites can’t abandon IE11 or Edge. And I won’t use a collection of polyfills just so I can develop with a framework. Why punish the user. They don’t care that your using web components. They just want the site to be fast and work. So use the technology that does that the best. For me, that is Svelte or just native HTML, CSS and JS.
This has generally been my leaning too, and why I like writing web servers a lot more than web clients - you're right, this is primarily a developer experience choice, but also partially educational. At least near me, TypeScript is a commonly seen job skill, and part of my motivation is working on it. I guess there's nothing stopping me from doing this in Node, which I hadn't really considered.
In general, all of these concerns are more than valid, and it's important to make sure there's continued development in that direction too. However, it is a reality, and becoming versed in it is important.
When you refer to static site generation, do you mean Sapper specifically?
Yeah. Sapper is not ready for prime time as a live server imho.
I use sapper for my personal blog and for my company. The work I do for my company is a heavily modified version that solved a number of pain points. We opened PR for all of them to contribute back, but they have been siting since October.
But we made a decision to only use it for static gen.
Stencil syntax is v.similar to Angular.
Angular also provides a CLI tool to generate your components etc.
I think it's a great way to take a certain idea and take it to other areas of development, especially by making use of decorators.
If you were to go look at Nest.js which is a Backend Framework for node written in TS, I reckon you'd be able to understand what's going from the shared knowledge of what you've learned using Stencil.