Rolling my own front-end framework pt. 1/?

typesincode

Jay Landrum

Posted on November 9, 2023

Rolling my own front-end framework pt. 1/?

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({}));
Enter fullscreen mode Exit fullscreen mode

Produces the following markup:

<hello-world>
    <div>Hello world</div>
</hello-world>
Enter fullscreen mode Exit fullscreen mode

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({}));
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Give it a shot: Code Sandbox. Next post will describe how to bind to data.

💖 💪 🙅 🚩
typesincode
Jay Landrum

Posted on November 9, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related