WEBVTT

00:00.000 --> 00:11.480
Oh, yeah.

00:11.480 --> 00:12.480
Hi!

00:12.480 --> 00:19.460
I kind of feel like I'm a bit of an imposter in this room today because I'm not really

00:19.460 --> 00:25.960
here to talk about brows development and I'm not necessarily here to talk about the

00:25.960 --> 00:30.760
platform from a standard perspective.

00:30.760 --> 00:34.320
But here I'm talking about web components

00:34.320 --> 00:37.000
from a JavaScript framework author's perspective.

00:37.000 --> 00:40.160
And that may be putting me at a bit of a disadvantage

00:40.160 --> 00:42.680
in this room.

00:42.680 --> 00:45.400
So yeah, hi, my name is Haley Thompson.

00:45.400 --> 00:49.600
You can find me on blue sky at haley.dev.

00:49.600 --> 00:52.880
I am on the core team for this really friendly programming

00:52.880 --> 00:55.120
language called gleam.

00:55.120 --> 00:56.480
It compiles to JavaScript.

00:56.480 --> 00:59.080
It looks like compiles to Erlang.

00:59.080 --> 01:02.240
And I'm the author of a front-hand framework

01:02.240 --> 01:04.240
in that language called luster.

01:07.600 --> 01:12.840
And like I said, I'm here to talk about web components

01:12.840 --> 01:15.200
often when we talk about web components

01:15.200 --> 01:18.680
as framework authors that's maybe a bit negative.

01:18.680 --> 01:20.480
So here, I want to talk about ways

01:20.480 --> 01:23.680
that we can make components web components work

01:23.680 --> 01:24.720
as a framework author.

01:28.720 --> 01:30.920
Often when we talk about web components

01:30.920 --> 01:33.840
and we're getting positive feedback,

01:33.840 --> 01:35.440
these are coming from people that maybe

01:35.440 --> 01:39.760
want to avoid frameworks or avoiderating lots of JavaScript

01:39.760 --> 01:42.920
or lock-in.

01:42.920 --> 01:46.400
And on the flip side, it's framework authors

01:46.400 --> 01:49.440
that are often the most critical of the technology.

01:49.440 --> 01:57.680
And I think if we want to understand how we can make

01:57.680 --> 02:00.400
web components be a really like successful foundation

02:00.400 --> 02:02.480
to build a framework on, we first need

02:02.480 --> 02:04.600
to understand what those criticisms are

02:04.600 --> 02:05.840
and why they're being made.

02:08.360 --> 02:11.200
And I think a lot of that stems from this quote

02:11.200 --> 02:14.480
from the author of solid JS.

02:14.480 --> 02:17.760
The fundamental problem with web components

02:17.760 --> 02:21.440
is that they're built on custom elements.

02:21.440 --> 02:25.520
And elements are not components.

02:25.520 --> 02:28.200
That's their observation.

02:28.200 --> 02:30.280
And then, kind of a lot of consequences

02:30.280 --> 02:32.080
for wow from this.

02:32.080 --> 02:36.400
So framework components are not tied to the DOM

02:36.400 --> 02:38.760
in the same way the custom elements are.

02:38.760 --> 02:42.360
And that means they can often do more.

02:42.360 --> 02:45.920
If you think about, say, a react application,

02:45.920 --> 02:48.920
I mean, there's going to be hundreds, maybe thousands

02:48.920 --> 02:51.520
of components in an application.

02:51.520 --> 02:53.200
And maybe only a handful of them

02:53.200 --> 02:55.600
are actually rendering something to the DOM.

02:55.600 --> 02:58.480
And the rest of them are composing in different and interesting

02:58.480 --> 02:58.880
ways.

03:02.800 --> 03:07.040
Similarly, web components affect DOM structure.

03:07.040 --> 03:08.240
They have to live in the DOM.

03:08.240 --> 03:10.640
And so that means if I have a web component

03:10.640 --> 03:14.600
that renders a list item, and then I try and render

03:14.600 --> 03:17.560
that web component inside a list.

03:17.560 --> 03:19.760
Well, I've broken the structure of the DOM.

03:19.760 --> 03:24.080
I've introduced some antique problems.

03:24.080 --> 03:25.280
And we can fix those.

03:25.280 --> 03:30.240
We can fix those with array attributes, for example.

03:30.240 --> 03:31.760
But from a framework's perspective,

03:31.760 --> 03:34.240
these are problems that never existed in the first place.

03:34.240 --> 03:36.800
And now, how do you think about ways to solve them?

03:40.480 --> 03:42.720
And then, of course, there's a performance

03:42.720 --> 03:44.800
conversation to be had here.

03:44.800 --> 03:46.320
When you're developing a framework,

03:46.320 --> 03:48.200
you really want to be tightly in control

03:48.200 --> 03:51.960
that is much of the runtime as possible.

03:51.960 --> 03:53.360
And when you go through web components,

03:53.360 --> 03:56.720
you're kind of forced into a very strict life cycle

03:56.720 --> 03:59.080
and a strict way of thinking about components that maybe

03:59.080 --> 04:02.560
doesn't map to how your framework wants to work,

04:02.560 --> 04:03.760
how your runtime wants to work.

04:08.600 --> 04:11.840
Now, none of these talking points are particularly new.

04:11.840 --> 04:16.400
I think maybe that solid JS quote comes from,

04:16.400 --> 04:19.480
I don't know, 2020.

04:19.480 --> 04:22.320
I think I've seen blog posts date back from 2015

04:22.320 --> 04:24.720
talking about web components, and the problems

04:24.720 --> 04:27.320
that they have as frameworks.

04:27.320 --> 04:29.840
So do we have any answers as a community?

04:33.280 --> 04:34.920
Well, kind of not really.

04:37.720 --> 04:41.400
As a community, we've kind of collectively agreed

04:41.400 --> 04:44.680
that web components are not framework components.

04:44.680 --> 04:46.200
And that's OK.

04:46.200 --> 04:48.680
Maybe framework components, that's, sorry,

04:48.680 --> 04:51.760
maybe frameworks don't even need to build on web components at all.

04:54.760 --> 04:58.680
And Leo Veru has this really excellent blog post

04:58.680 --> 05:01.880
kind of going into this idea.

05:01.880 --> 05:06.040
And she suggests that web components are at their best

05:06.040 --> 05:09.080
when they're these kind of generalizable elements.

05:09.080 --> 05:13.040
They extend what HTML can do.

05:13.040 --> 05:15.960
And they're used in the same way as kind of native HTML

05:15.960 --> 05:18.520
elements, meaning that they should be applicable

05:18.520 --> 05:21.080
to a wide range of diverse projects.

05:24.840 --> 05:28.360
Maybe another way to think about that is how many people

05:28.360 --> 05:32.880
have application authors do expect to use that component.

05:32.880 --> 05:36.840
And if you care about reaching as many users as possible,

05:36.840 --> 05:39.880
then you might want to reach for web components,

05:39.880 --> 05:43.960
the big story there, being, of course, interoperability.

05:43.960 --> 05:47.080
But the more projects specific you get,

05:47.080 --> 05:51.080
then the scale start tipping towards framework components.

05:51.080 --> 05:55.800
And if you're already in a React or a View application,

05:55.800 --> 06:00.440
then the cost of putting in or developing a web component

06:00.440 --> 06:03.000
is really outweighed by the convenience of just leaning

06:03.000 --> 06:05.720
onto the framework that you're already using.

06:09.320 --> 06:13.680
I mean, I think that's a fine place to be.

06:13.680 --> 06:16.440
But that kind of original promise,

06:16.440 --> 06:18.960
the allure of using the platform is still really

06:18.960 --> 06:19.800
very strong for me.

06:23.400 --> 06:27.960
And fundamentally, these are both still components.

06:27.960 --> 06:30.880
I mean, we've seen, or we've discussed maybe,

06:30.880 --> 06:33.000
the elements and framework components,

06:33.000 --> 06:34.480
they're slightly different.

06:34.480 --> 06:38.320
But fundamentally, a component is about an encapsulation

06:38.320 --> 06:42.640
of state, behavior, render logic.

06:42.640 --> 06:45.280
And web components do that just as well as framework components.

06:51.120 --> 06:54.160
So framework authors often frame this conversation

06:54.160 --> 06:59.040
as a technical, custom elements, web components.

06:59.040 --> 07:02.480
They have technical requirements or technical limitations

07:02.480 --> 07:05.520
that don't work in the context of a framework.

07:08.400 --> 07:10.760
But we've also seen maybe the difference

07:10.760 --> 07:13.480
between web components and framework components

07:13.480 --> 07:15.680
is in terms of audience or in terms of reach.

07:18.480 --> 07:21.560
But I think there's actually a more fundamental difference

07:21.560 --> 07:24.320
between the two.

07:24.320 --> 07:27.000
And it comes down for their world view.

07:27.000 --> 07:29.840
It comes down to how frameworks

07:29.840 --> 07:34.040
see the nature of applications versus web components

07:34.040 --> 07:35.400
or the platform in general.

07:38.400 --> 07:41.760
And I think that can be summed up as frameworks

07:41.760 --> 07:44.920
want to compose components.

07:44.920 --> 07:47.600
The main building block in a React application

07:47.600 --> 07:49.040
is the React component.

07:52.480 --> 07:55.800
The elements are web components are slightly different.

07:55.800 --> 07:57.920
And they want to be composing elements, right?

07:57.920 --> 08:00.960
They want to be composing real HTML elements, don't know.

08:05.680 --> 08:08.760
And to kind of exemplify that, I've taken some snippets

08:08.760 --> 08:11.600
from the React homepage.

08:11.600 --> 08:14.600
I think using these is a really good example

08:14.600 --> 08:17.400
because that homepage has to sell you

08:17.400 --> 08:19.880
on the idea of the framework and convince you

08:19.880 --> 08:23.760
about what the frameworks meant for model on world view is.

08:23.760 --> 08:27.400
And so we have a video component here.

08:27.400 --> 08:28.920
It's rendering some markup.

08:28.920 --> 08:31.800
And it's also rendering some other components

08:31.800 --> 08:33.000
that we don't know about right now.

08:36.080 --> 08:39.240
Next, they show us a video list.

08:39.240 --> 08:41.480
Here we're seeing the templating language

08:41.480 --> 08:42.800
can do looping or whatever.

08:46.960 --> 08:48.920
And then maybe the most interesting part

08:48.920 --> 08:53.360
is when we introduce this searchable video list component.

08:53.360 --> 08:57.080
And now we are starting to talk about state management.

08:57.080 --> 09:01.720
So we have this string of state search text.

09:01.720 --> 09:04.480
An interestingly, of course, this isn't rendering any dominoes

09:04.480 --> 09:04.960
directly.

09:10.480 --> 09:13.040
Now, if you're counting that's six components,

09:13.040 --> 09:14.560
just in this really small example.

09:18.720 --> 09:23.280
But if we think about what we said a component was,

09:23.280 --> 09:26.920
I mean, really, only one of these fits the bill.

09:26.920 --> 09:29.880
And that's that searchable video list.

09:29.880 --> 09:32.760
And so I think if we were working in the context of web

09:32.760 --> 09:36.680
components, we might not be thinking about that thumbnail

09:36.680 --> 09:39.720
or that like button as a web component.

09:39.720 --> 09:43.520
We might just be thinking about this as a reusable piece

09:43.520 --> 09:45.040
of the dom.

09:45.040 --> 09:52.640
Now, because framework components that

09:52.640 --> 09:57.480
these core compositional unit from the ground up frameworks

09:57.480 --> 10:00.960
have to work, not just have to embrace this idea

10:00.960 --> 10:04.320
that you might have an application with many, many hundreds

10:04.320 --> 10:08.200
of stateful components, each with their own lifetimes

10:08.200 --> 10:08.920
and render loops.

10:08.920 --> 10:15.640
But the frameworks have to work this way.

10:15.640 --> 10:18.920
I mean, we can kind of see that there is this philosophical

10:18.920 --> 10:22.360
divide between how web components want to be used,

10:22.360 --> 10:25.040
or see the world, and how frameworks want to be used,

10:25.040 --> 10:27.200
and see the world.

10:27.200 --> 10:29.800
But does it have to be like that?

10:29.800 --> 10:33.640
Well, when I think about Luster, the framework that I

10:33.640 --> 10:37.160
developed, I like to think of it like this,

10:37.160 --> 10:39.960
and that the framework really wants to be composing elements,

10:39.960 --> 10:42.440
and it really doesn't want to be composing components at all.

10:46.440 --> 10:53.160
And to maybe exemplify, I ported those react snippets over to Luster.

10:53.160 --> 10:54.920
So this is written in Gleam.

10:54.920 --> 10:57.120
I'm not going to go too deep into the language.

10:57.120 --> 10:59.680
I think if you understand a tiny bit of JavaScript,

10:59.680 --> 11:02.760
you can probably grow up this.

11:02.760 --> 11:06.040
And so again, we have this video element

11:06.040 --> 11:10.120
to rendering some marker, it's calling some other functions.

11:10.120 --> 11:14.560
And again, the video list looks pretty similar to you.

11:14.560 --> 11:17.440
Where things change or get a little bit more interesting

11:17.440 --> 11:21.560
is when we think about that searchable video list element.

11:21.560 --> 11:24.800
In React, we just introduced some state here.

11:24.800 --> 11:28.160
We wanted to handle the search input, what

11:28.160 --> 11:31.440
the user had typed, and so we just pull in use state.

11:31.440 --> 11:34.760
And now this is a stateful component.

11:34.760 --> 11:39.080
But if Luster isn't about composing components,

11:39.080 --> 11:42.320
if it's about composing elements, then we have to do something

11:42.320 --> 11:43.720
if we want reactive state.

11:46.600 --> 11:49.080
And that's achieved in what maybe appears

11:49.080 --> 11:53.880
a bit of a cop out by simply moving that state somewhere else.

11:53.880 --> 11:56.600
Now of course, you could do this in React 2.

11:56.600 --> 11:59.960
The differences you don't actually have a choice in Luster

11:59.960 --> 12:03.640
is no way to introduce component state in the same way.

12:07.560 --> 12:09.760
I was reading a blog post recently

12:09.760 --> 12:13.760
from an organization that had just ported their view

12:13.760 --> 12:17.040
application to Luster.

12:17.040 --> 12:18.600
And I pulled out this quote because I actually

12:18.600 --> 12:22.160
just think it's really perfect for what I'm talking about here.

12:22.160 --> 12:27.560
It's this idea that components that hold state in frameworks

12:27.560 --> 12:29.920
like React or View, et cetera.

12:29.920 --> 12:31.400
They make it super easy.

12:31.400 --> 12:35.800
In fact, super attractive to model your state hierarchically

12:35.800 --> 12:39.200
in the same way that you model your view.

12:39.200 --> 12:43.280
But for Luster, we can have taken this approach.

12:43.280 --> 12:45.600
That state should be modeled separately

12:45.600 --> 12:47.320
from your view code.

12:47.320 --> 12:49.840
And actually, I think you see a convergence

12:49.840 --> 12:53.640
on framework communities of this idea, too.

12:53.640 --> 12:56.120
I mean, first we had like Redux.

12:56.120 --> 12:58.640
Now we have all sorts of store libraries.

12:58.640 --> 13:02.040
Trying to move state away from our components.

13:06.240 --> 13:10.760
In Luster, that is achieved through an application architecture

13:10.760 --> 13:13.240
that all applications have to follow.

13:13.240 --> 13:16.640
And it's called the Model View Update architecture.

13:16.640 --> 13:20.360
It's also known as the NOM architecture.

13:20.360 --> 13:26.080
And that means all applications kind of follow this blueprint.

13:26.240 --> 13:31.800
We start off with a model of our entire application state.

13:31.800 --> 13:35.160
So here we have that search query string.

13:35.160 --> 13:39.360
We also have the video metadata that we've loaded.

13:39.360 --> 13:42.640
And we have an init function to produce the first version

13:42.640 --> 13:44.440
of that state when the application starts.

13:47.840 --> 13:50.480
Then we have a message type that describes all the events

13:50.480 --> 13:52.080
that we want to react to.

13:52.080 --> 13:56.040
So maybe that's user interaction or maybe that's HTTP

13:56.080 --> 13:58.760
requests, et cetera.

13:58.760 --> 14:02.760
And an update function that takes incoming event

14:02.760 --> 14:06.280
and the current state of the application

14:06.280 --> 14:07.800
and returns a new one.

14:07.800 --> 14:11.080
So here every time the user types something,

14:11.080 --> 14:13.560
we'll get a user updated search message

14:13.560 --> 14:15.160
with the new value that they've typed,

14:15.160 --> 14:17.440
and we store that in the model.

14:17.440 --> 14:22.680
And the final piece of that puzzle then is a view function,

14:22.680 --> 14:25.680
which takes the current application state

14:25.680 --> 14:27.800
to any given point in time.

14:27.800 --> 14:30.680
I'm renders that as HTML elements.

14:35.320 --> 14:39.640
And then we package these things up together in an application.

14:39.640 --> 14:42.600
And we start it.

14:42.600 --> 14:44.840
This is pretty standard of fair for a framework.

14:44.840 --> 14:48.120
So we're mounting onto some element with an ID

14:48.120 --> 14:50.000
called app and a way we go.

14:50.000 --> 14:57.280
If we start talking about state management

14:57.280 --> 15:00.680
and frameworks, though, especially declarative frameworks,

15:00.680 --> 15:04.480
which most front end frameworks are these days,

15:04.480 --> 15:08.840
then there's actually a dirty secret about we have to come front.

15:14.080 --> 15:16.280
And that frameworks like to pretend

15:16.280 --> 15:17.720
the own all of the state.

15:20.240 --> 15:25.280
So we had that search input.

15:25.280 --> 15:29.520
We own the text, the user is typed, every time they type.

15:29.520 --> 15:31.320
We get a message, we have to stay.

15:31.320 --> 15:35.360
We re-sync the DOM with whatever that string is.

15:35.360 --> 15:39.120
But of course, DOM nodes are stateful objects themselves.

15:39.120 --> 15:41.400
And there's a whole bunch of state that often exists

15:41.400 --> 15:44.640
in the DOM that is not modeled in our framework state.

15:44.640 --> 15:50.320
And the most framework's, I feel like developers,

15:50.320 --> 15:54.040
in general, are quite happy to live with this convenient

15:54.040 --> 15:56.000
lie and often it doesn't really get addressed

15:56.000 --> 15:58.680
or even spoken about.

15:58.680 --> 16:03.920
But if you kind of accept that this is what's going on,

16:03.920 --> 16:06.240
then you kind of open to the idea that web components

16:06.240 --> 16:09.480
let us play along with that lie.

16:09.480 --> 16:13.320
If we're already kind of OK with the idea that some

16:13.320 --> 16:15.520
nodes have in terms of state that we're not

16:15.520 --> 16:19.480
going to manage from our framework, then web components

16:19.480 --> 16:23.000
are a way to tap into that potential, perhaps.

16:26.120 --> 16:30.200
So I want to revisit that idea of what web components

16:30.200 --> 16:33.000
are perhaps applicable for.

16:33.000 --> 16:35.880
And instead of talking about it in terms of audience,

16:35.880 --> 16:42.320
how many users do I want my component to be accessible to?

16:42.320 --> 16:45.360
I actually think the split is a bit more specific than that.

16:45.360 --> 16:50.560
And it's about presentation logic or view state versus business

16:50.560 --> 16:53.280
logic.

16:53.280 --> 16:55.840
You know, if you have a lot of presentation logic

16:55.840 --> 16:58.600
in a component and not much business logic,

16:58.600 --> 17:00.880
then that's this idea that the component isn't really

17:00.880 --> 17:04.520
tied to your application.

17:04.520 --> 17:10.480
This might be more generalized in kind of layers framework.

17:10.480 --> 17:12.640
And as you introduce more and more business logic,

17:12.640 --> 17:14.960
then this is where we typically would feel

17:14.960 --> 17:17.280
like a framework component would come into its own.

17:22.240 --> 17:26.280
Now once you're ready to kind of participate in this idea

17:26.280 --> 17:29.440
that web components let us play along with the lie whilst

17:29.440 --> 17:32.800
also getting some benefits out of state encapsulation,

17:32.800 --> 17:35.440
then let's make that really easy.

17:35.440 --> 17:38.800
Instead of starting the application as a single page app,

17:38.800 --> 17:42.240
mounting it onto a dom node, we can just swap out

17:42.240 --> 17:43.520
what we do with that app.

17:43.520 --> 17:47.440
And so instead of starting it, we register it as a custom element.

17:51.040 --> 17:53.200
And I think this is one of the key parts

17:53.200 --> 17:55.760
about what makes component successful in Luster

17:55.760 --> 17:59.120
is that we kind of introduce the platform incrementally.

18:01.920 --> 18:05.920
I like to build on the idea that the platform is actually good.

18:05.920 --> 18:09.440
We don't really need to abstract too much away from it.

18:09.440 --> 18:12.240
But often there's a lot of upfront learning that has to be done,

18:12.240 --> 18:15.440
especially when it comes to web components.

18:15.440 --> 18:18.080
Now you have to suddenly learn three specs.

18:18.080 --> 18:20.640
You have to learn how to deal with accessibility,

18:20.640 --> 18:22.320
you have to learn about what the shadow dom is,

18:22.320 --> 18:26.240
how styling works, how slots work, et cetera, et cetera.

18:26.240 --> 18:29.520
And so making it really easy to just take an application

18:29.520 --> 18:32.240
and turn it into a web component.

18:32.320 --> 18:36.880
That's kind of this first step towards having a really tight integration.

18:36.880 --> 18:40.160
And of course, Luster isn't the first framework to have done this.

18:40.160 --> 18:42.160
Svel and view have done this in the past.

18:46.320 --> 18:48.480
But I really like this idea that the components

18:48.480 --> 18:52.000
they build on your knowledge rather than replacing it.

18:52.000 --> 18:55.360
And so you can get comfortable with Luster's approach

18:55.360 --> 18:59.360
to application architecture, which you kind of have to do

18:59.360 --> 19:03.040
if you want to buy into this kind of framework.

19:03.040 --> 19:05.360
And then once you're really comfortable with that,

19:05.360 --> 19:08.800
you can slowly start playing with components,

19:08.800 --> 19:11.280
first by just taking your application and turning it

19:11.280 --> 19:13.280
into a custom element.

19:13.280 --> 19:16.960
But then, for example, changing the application constructor.

19:16.960 --> 19:20.240
So before we were hauling Luster.application,

19:20.240 --> 19:22.640
now we're calling Luster.component.

19:22.640 --> 19:26.800
And that gives us access to some new options.

19:26.800 --> 19:30.480
So for example, we can start listening for attribute changes.

19:34.240 --> 19:37.360
Maybe we learn about form association.

19:37.360 --> 19:41.120
We want our components to participate in native HTML forms.

19:44.560 --> 19:47.120
Maybe we learn about focus delegation, particularly

19:47.120 --> 19:49.920
if we want to start distributing this component

19:49.920 --> 19:51.840
and context that we don't own.

19:51.840 --> 19:57.840
And then finally, especially if we're going to start talking

19:57.840 --> 20:01.760
about distributing this component outside of a Luster application.

20:01.760 --> 20:04.560
There's this option here called adopt styles

20:04.560 --> 20:08.000
and we can turn that off.

20:08.000 --> 20:11.600
Now the other options, these were part of the spec, right?

20:11.600 --> 20:15.200
If you know about web components, you've probably heard about listening

20:15.200 --> 20:18.720
for attribute changes or form association.

20:18.720 --> 20:22.000
But this idea of adopting styles, well, this is new.

20:22.000 --> 20:24.960
And this is one of the big watts that Luster kind of has to

20:24.960 --> 20:28.720
contend with if it wants to present components

20:28.720 --> 20:33.120
as a viable option for application authors.

20:33.120 --> 20:35.840
And that's fundamentally the styling still remains really tricky

20:35.840 --> 20:38.560
when you're working with web components, especially when you're

20:38.560 --> 20:41.760
working with the shadowed on, which is what

20:41.760 --> 20:44.800
all Luster components work with.

20:44.800 --> 20:48.400
And so what we do there is we have this kind of

20:48.400 --> 20:54.800
awful hack to crawl the document, find all of the style tags,

20:54.800 --> 20:58.640
all of the link tags, and they kind of get cloned into the component.

20:58.640 --> 21:03.840
And so if you're an application developer, 90% of the time

21:03.840 --> 21:05.680
this just works.

21:05.680 --> 21:09.040
But 10% of the time is really awful.

21:09.040 --> 21:12.880
And I'm hoping that maybe if we can start thinking about

21:12.880 --> 21:16.320
web components as viable targets for applications,

21:16.320 --> 21:18.560
that maybe we can start having a conversation

21:18.560 --> 21:23.440
about splitting the shadowed arm from style isolation.

21:27.040 --> 21:35.520
So to summarise, we kind of agree that elements are not components.

21:35.520 --> 21:40.320
But maybe frameworks don't need components as much as they think.

21:40.320 --> 21:43.600
And when they do want components, maybe work components are ready

21:43.600 --> 21:46.080
and waiting.

21:46.080 --> 21:56.080
Thank you for listening.

