Rolling Your Own Frontend JS Framework
For fun and (no) profit
(TL;DR I built a framework just for fun, please do not use, check it out here!) There is no shortage of frontend JS frameworks, and they seemlingly never keep coming. Trying to keep up with every new framework on the block for the last decade or so has proven to be an exercise in futility, resulting in what can be best described as framework fatigue. But instead of trying to fight about which of the frameworks released this week is the best (and why everyone should make the switch), it could be a fun experience to approach the subject from a different angle by building your own framework. Let's dive in.
I found building a simple JS framework is good for several reasons:
- You will learn how a framework actually works (because magic is not real)
- You will understand some of the tradeoffs that frameworks have, why they exist, and how to deal with them
- You will learn that building a simple framework might actually be easier than you think!
After you built your own thing, you should have a good understanding of how a framework gets the job done, and what design decisions need to be made when building one - and this should also answer the question older than time itself: Why are there so many frameworks, and why are they all sort of the same but all a little different??
So a framework has several building blocks and concepts. But what we generally need is:
- Templating - so that there will be HTML to show on the page.
- Reactivity/data binding - so that when we interact with the page, the framework picks up what we do and can update itself accordingly.
- Some general rules/guidelines around structure - Such as: What is a component, how do components interact, how is CSS handled, etc.
The ideal framework
Different frameworks take different approaches to all this. For example, Angular has templates in separate files and offers two-way binding. React on the other hand, has the component and the template in the same file, and uses one-way binding. Also, Angular uses HTML with some special "template language" syntax thrown in (such as
So how would we like the ideal framework to behave? And this is where you mileage may vary! But in my case, I think it would be great if we could have:
- Native technologies as far as possible, but flexibility when required: Plain HTML, plain JS, plain CSS should all work. Generally, if you know the web, you're good to go.
- Minimal tooling. Webpack and time consuming builds should not be needed.
- Ease of use. No weird JS trickeries, no new template syntax to learn. Especially, updates to the state should "just work" and not require the developer to understand new concepts (yes, I'm looking at you React, with your
useEffectand other hooks)
- Good performance.
- Lightweight and small.
So that would be ideal in my world.
How it turned out
As I pretty much expected, I immediately ran into problems when I tried the above.
- Avoiding special templating syntax turned out to be tricky. We could just render the HTML per component as a plain string, and on each change, replace the HTML. I found that this would lead to problems such as child components being re-rendered and inputs loosing focus in re-render. By using a basic template syntax, we can make sure to re-render only what is needed.
So I ended up with this:
- A component is simply a web component - the "native" idea of a component that both a browser, as well as a developer, can understand.
- Each components extends an abstract base class and needs to define a state model, that can be accessed using
- A simple templating syntax that is inspired by KnockoutJS. The basic idea is that we simply use the attribute
data-bindon any element, and then set the property, just like you would in the DOM. For example, if your state model contains a property called name, then you display it in a spanby: <span data-bind="innerText: name"></span>
Using the framework, I then set up a page that explains how the framework works, and also contains a few demos: KnockoffJS.
If this sounds a bit like Lit, then - yes, there are certainly similarities!
When it comes to reacting to changes, I opted for using
For good measure, I added a simple service locator, a registry that can be used to share styles, and a (very) simple router. This all weighs in at less than 5kb (gzipped, non-minimized).
Can I use it?
I want to point out once more that this was not much more than an exercise, and while the framework could be used for building something, I would not recommend it. It has several obvious flaws that I don't plan to address at the moment. Here are some examples:
- Every time something in the state is updated, any child components rendered in for-loops are destroyed and re-rendered, which might be slow and also the state of each child might get lost.
- Updates are not batched, so changing the state several times e.g. in a loop will re-render on each change.
- Basic functionality is missing, such as some sort of
ifconstruct in the template syntax. Instead, elements must be shown/hidden by CSS.
Now, this is hardly a framework I would recommend for use in production. But as a learning experience, I found it quite inspiring. And if you try something similar, maybe you have other priorities than I did? Maybe you would rather use React-style hooks, such as