Rolling my own front-end framework pt. 1/?
Jay Landrum
Posted on November 9, 2023
For a long time I've iterated on my own small front-end framework fueled by ongoing developer gripes with popular libraries. My goal here is to write a series of posts as a retrospective on that work, summarize design goals and document the problems I faced and how I chose to solve them.
Repo link: j-templates
The framework was used to make Qwiqwit, a multi-player web-game I built with my friend, Paul.
Some of the initial goals were:
- No transpile step besides TypeScript
- Fully typed in the editor, no bindings in string or .html templates
- Sensibly reactive, no need to self-manage change detection
My goals were reactions to pitfalls present in early React/Angular development. This was 6+ years ago so a lot of these complaints have been addressed since.
Hello World
import { Component } from 'j-templates';
import { div } from 'j-templates/DOM';
// Create the Component class
class HelloWorld extends Component {
// Override the Template function
Template() {
return div({}, () => "Hello world");
}
}
// Convert the class to an element function
const helloWorld = Component.ToFunction("hello-world", null, HelloWorld);
// Append a new hello-world element to the page
Component.Attach(document.body, helloWorld({}));
Produces the following markup:
<hello-world>
<div>Hello world</div>
</hello-world>
All elements are represented as functions that take a configuration object. The HelloWorld
component class is converted to a function using the Component.ToFunction(...)
method. Calling one of these function returns a NodeDefinition
object which is the framework's virtual representation of the DOM.
Element functions (i.e. div, span, p) take a second parameter to define children. This function can return a string to create a text node or one/many NodeDefinition
objects. This is all you need to start building static views.
import { Component } from 'j-templates';
import { div, span, h1, b } from 'j-templates/DOM';
class MessageHeader extends Component {
Template() {
return h1({}, () => "Incoming Message");
}
}
const messageHeader = Component.ToFunction("message-header", null, MessageHeader);
class HelloWorld extends Component {
Template() {
return [
messageHeader({}),
div({}, () => [
b({}, () => "Message Body: "),
span({}, () => "Hello world")
])
];
}
}
const helloWorld = Component.ToFunction("hello-world", null, HelloWorld);
Component.Attach(document.body, helloWorld({}));
Produces the following markup:
<hello-world>
<message-header><h1>Incoming Message</h1></message-header>
<div><b>Message Body: </b><span>Hello world</span></div>
</hello-world>
Give it a shot: Code Sandbox. Next post will describe how to bind to data.
Posted on November 9, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.