Oh, yeah.
Hi!
I kind of feel like I'm a bit of an imposter in this room today because I'm not really
here to talk about brows development and I'm not necessarily here to talk about the
platform from a standard perspective.
But here I'm talking about web components
from a JavaScript framework author's perspective.
And that may be putting me at a bit of a disadvantage
in this room.
So yeah, hi, my name is Haley Thompson.
You can find me on blue sky at haley.dev.
I am on the core team for this really friendly programming
language called gleam.
It compiles to JavaScript.
It looks like compiles to Erlang.
And I'm the author of a front-hand framework
in that language called luster.
And like I said, I'm here to talk about web components
often when we talk about web components
as framework authors that's maybe a bit negative.
So here, I want to talk about ways
that we can make components web components work
as a framework author.
Often when we talk about web components
and we're getting positive feedback,
these are coming from people that maybe
want to avoid frameworks or avoiderating lots of JavaScript
or lock-in.
And on the flip side, it's framework authors
that are often the most critical of the technology.
And I think if we want to understand how we can make
web components be a really like successful foundation
to build a framework on, we first need
to understand what those criticisms are
and why they're being made.
And I think a lot of that stems from this quote
from the author of solid JS.
The fundamental problem with web components
is that they're built on custom elements.
And elements are not components.
That's their observation.
And then, kind of a lot of consequences
for wow from this.
So framework components are not tied to the DOM
in the same way the custom elements are.
And that means they can often do more.
If you think about, say, a react application,
I mean, there's going to be hundreds, maybe thousands
of components in an application.
And maybe only a handful of them
are actually rendering something to the DOM.
And the rest of them are composing in different and interesting
ways.
Similarly, web components affect DOM structure.
They have to live in the DOM.
And so that means if I have a web component
that renders a list item, and then I try and render
that web component inside a list.
Well, I've broken the structure of the DOM.
I've introduced some antique problems.
And we can fix those.
We can fix those with array attributes, for example.
But from a framework's perspective,
these are problems that never existed in the first place.
And now, how do you think about ways to solve them?
And then, of course, there's a performance
conversation to be had here.
When you're developing a framework,
you really want to be tightly in control
that is much of the runtime as possible.
And when you go through web components,
you're kind of forced into a very strict life cycle
and a strict way of thinking about components that maybe
doesn't map to how your framework wants to work,
how your runtime wants to work.
Now, none of these talking points are particularly new.
I think maybe that solid JS quote comes from,
I don't know, 2020.
I think I've seen blog posts date back from 2015
talking about web components, and the problems
that they have as frameworks.
So do we have any answers as a community?
Well, kind of not really.
As a community, we've kind of collectively agreed
that web components are not framework components.
And that's OK.
Maybe framework components, that's, sorry,
maybe frameworks don't even need to build on web components at all.
And Leo Veru has this really excellent blog post
kind of going into this idea.
And she suggests that web components are at their best
when they're these kind of generalizable elements.
They extend what HTML can do.
And they're used in the same way as kind of native HTML
elements, meaning that they should be applicable
to a wide range of diverse projects.
Maybe another way to think about that is how many people
have application authors do expect to use that component.
And if you care about reaching as many users as possible,
then you might want to reach for web components,
the big story there, being, of course, interoperability.
But the more projects specific you get,
then the scale start tipping towards framework components.
And if you're already in a React or a View application,
then the cost of putting in or developing a web component
is really outweighed by the convenience of just leaning
onto the framework that you're already using.
I mean, I think that's a fine place to be.
But that kind of original promise,
the allure of using the platform is still really
very strong for me.
And fundamentally, these are both still components.
I mean, we've seen, or we've discussed maybe,
the elements and framework components,
they're slightly different.
But fundamentally, a component is about an encapsulation
of state, behavior, render logic.
And web components do that just as well as framework components.
So framework authors often frame this conversation
as a technical, custom elements, web components.
They have technical requirements or technical limitations
that don't work in the context of a framework.
But we've also seen maybe the difference
between web components and framework components
is in terms of audience or in terms of reach.
But I think there's actually a more fundamental difference
between the two.
And it comes down for their world view.
It comes down to how frameworks
see the nature of applications versus web components
or the platform in general.
And I think that can be summed up as frameworks
want to compose components.
The main building block in a React application
is the React component.
The elements are web components are slightly different.
And they want to be composing elements, right?
They want to be composing real HTML elements, don't know.
And to kind of exemplify that, I've taken some snippets
from the React homepage.
I think using these is a really good example
because that homepage has to sell you
on the idea of the framework and convince you
about what the frameworks meant for model on world view is.
And so we have a video component here.
It's rendering some markup.
And it's also rendering some other components
that we don't know about right now.
Next, they show us a video list.
Here we're seeing the templating language
can do looping or whatever.
And then maybe the most interesting part
is when we introduce this searchable video list component.
And now we are starting to talk about state management.
So we have this string of state search text.
An interestingly, of course, this isn't rendering any dominoes
directly.
Now, if you're counting that's six components,
just in this really small example.
But if we think about what we said a component was,
I mean, really, only one of these fits the bill.
And that's that searchable video list.
And so I think if we were working in the context of web
components, we might not be thinking about that thumbnail
or that like button as a web component.
We might just be thinking about this as a reusable piece
of the dom.
Now, because framework components that
these core compositional unit from the ground up frameworks
have to work, not just have to embrace this idea
that you might have an application with many, many hundreds
of stateful components, each with their own lifetimes
and render loops.
But the frameworks have to work this way.
I mean, we can kind of see that there is this philosophical
divide between how web components want to be used,
or see the world, and how frameworks want to be used,
and see the world.
But does it have to be like that?
Well, when I think about Luster, the framework that I
developed, I like to think of it like this,
and that the framework really wants to be composing elements,
and it really doesn't want to be composing components at all.
And to maybe exemplify, I ported those react snippets over to Luster.
So this is written in Gleam.
I'm not going to go too deep into the language.
I think if you understand a tiny bit of JavaScript,
you can probably grow up this.
And so again, we have this video element
to rendering some marker, it's calling some other functions.
And again, the video list looks pretty similar to you.
Where things change or get a little bit more interesting
is when we think about that searchable video list element.
In React, we just introduced some state here.
We wanted to handle the search input, what
the user had typed, and so we just pull in use state.
And now this is a stateful component.
But if Luster isn't about composing components,
if it's about composing elements, then we have to do something
if we want reactive state.
And that's achieved in what maybe appears
a bit of a cop out by simply moving that state somewhere else.
Now of course, you could do this in React 2.
The differences you don't actually have a choice in Luster
is no way to introduce component state in the same way.
I was reading a blog post recently
from an organization that had just ported their view
application to Luster.
And I pulled out this quote because I actually
just think it's really perfect for what I'm talking about here.
It's this idea that components that hold state in frameworks
like React or View, et cetera.
They make it super easy.
In fact, super attractive to model your state hierarchically
in the same way that you model your view.
But for Luster, we can have taken this approach.
That state should be modeled separately
from your view code.
And actually, I think you see a convergence
on framework communities of this idea, too.
I mean, first we had like Redux.
Now we have all sorts of store libraries.
Trying to move state away from our components.
In Luster, that is achieved through an application architecture
that all applications have to follow.
And it's called the Model View Update architecture.
It's also known as the NOM architecture.
And that means all applications kind of follow this blueprint.
We start off with a model of our entire application state.
So here we have that search query string.
We also have the video metadata that we've loaded.
And we have an init function to produce the first version
of that state when the application starts.
Then we have a message type that describes all the events
that we want to react to.
So maybe that's user interaction or maybe that's HTTP
requests, et cetera.
And an update function that takes incoming event
and the current state of the application
and returns a new one.
So here every time the user types something,
we'll get a user updated search message
with the new value that they've typed,
and we store that in the model.
And the final piece of that puzzle then is a view function,
which takes the current application state
to any given point in time.
I'm renders that as HTML elements.
And then we package these things up together in an application.
And we start it.
This is pretty standard of fair for a framework.
So we're mounting onto some element with an ID
called app and a way we go.
If we start talking about state management
and frameworks, though, especially declarative frameworks,
which most front end frameworks are these days,
then there's actually a dirty secret about we have to come front.
And that frameworks like to pretend
the own all of the state.
So we had that search input.
We own the text, the user is typed, every time they type.
We get a message, we have to stay.
We re-sync the DOM with whatever that string is.
But of course, DOM nodes are stateful objects themselves.
And there's a whole bunch of state that often exists
in the DOM that is not modeled in our framework state.
And the most framework's, I feel like developers,
in general, are quite happy to live with this convenient
lie and often it doesn't really get addressed
or even spoken about.
But if you kind of accept that this is what's going on,
then you kind of open to the idea that web components
let us play along with that lie.
If we're already kind of OK with the idea that some
nodes have in terms of state that we're not
going to manage from our framework, then web components
are a way to tap into that potential, perhaps.
So I want to revisit that idea of what web components
are perhaps applicable for.
And instead of talking about it in terms of audience,
how many users do I want my component to be accessible to?
I actually think the split is a bit more specific than that.
And it's about presentation logic or view state versus business
logic.
You know, if you have a lot of presentation logic
in a component and not much business logic,
then that's this idea that the component isn't really
tied to your application.
This might be more generalized in kind of layers framework.
And as you introduce more and more business logic,
then this is where we typically would feel
like a framework component would come into its own.
Now once you're ready to kind of participate in this idea
that web components let us play along with the lie whilst
also getting some benefits out of state encapsulation,
then let's make that really easy.
Instead of starting the application as a single page app,
mounting it onto a dom node, we can just swap out
what we do with that app.
And so instead of starting it, we register it as a custom element.
And I think this is one of the key parts
about what makes component successful in Luster
is that we kind of introduce the platform incrementally.
I like to build on the idea that the platform is actually good.
We don't really need to abstract too much away from it.
But often there's a lot of upfront learning that has to be done,
especially when it comes to web components.
Now you have to suddenly learn three specs.
You have to learn how to deal with accessibility,
you have to learn about what the shadow dom is,
how styling works, how slots work, et cetera, et cetera.
And so making it really easy to just take an application
and turn it into a web component.
That's kind of this first step towards having a really tight integration.
And of course, Luster isn't the first framework to have done this.
Svel and view have done this in the past.
But I really like this idea that the components
they build on your knowledge rather than replacing it.
And so you can get comfortable with Luster's approach
to application architecture, which you kind of have to do
if you want to buy into this kind of framework.
And then once you're really comfortable with that,
you can slowly start playing with components,
first by just taking your application and turning it
into a custom element.
But then, for example, changing the application constructor.
So before we were hauling Luster.application,
now we're calling Luster.component.
And that gives us access to some new options.
So for example, we can start listening for attribute changes.
Maybe we learn about form association.
We want our components to participate in native HTML forms.
Maybe we learn about focus delegation, particularly
if we want to start distributing this component
and context that we don't own.
And then finally, especially if we're going to start talking
about distributing this component outside of a Luster application.
There's this option here called adopt styles
and we can turn that off.
Now the other options, these were part of the spec, right?
If you know about web components, you've probably heard about listening
for attribute changes or form association.
But this idea of adopting styles, well, this is new.
And this is one of the big watts that Luster kind of has to
contend with if it wants to present components
as a viable option for application authors.
And that's fundamentally the styling still remains really tricky
when you're working with web components, especially when you're
working with the shadowed on, which is what
all Luster components work with.
And so what we do there is we have this kind of
awful hack to crawl the document, find all of the style tags,
all of the link tags, and they kind of get cloned into the component.
And so if you're an application developer, 90% of the time
this just works.
But 10% of the time is really awful.
And I'm hoping that maybe if we can start thinking about
web components as viable targets for applications,
that maybe we can start having a conversation
about splitting the shadowed arm from style isolation.
So to summarise, we kind of agree that elements are not components.
But maybe frameworks don't need components as much as they think.
And when they do want components, maybe work components are ready
and waiting.
Thank you for listening.