Skip to main content Accessibility Feedback

Creating web app components with vanilla JavaScript

I’m getting ready to launch my latest pocket guide, “Vanilla JS Web Apps.”

This week, we’ve looked at how to render elements, add state to an object (and what “state” even is), and how to automatically re-render elements when state changes—all without using a framework or library.

Today, we’re going to talk about the final piece of the puzzle: how to programmatically turn any template into a component.

Quick heads up: if you haven’t yet, you should definitely read the rest of the articles in this series or some of this won’t make sense.

A component() method #

We’re going to use a component() method to handle all off the stuff we did manually in the previous articles.

Let me show you the whole thing, then we’ll talk through it.

var component = function (template, props, elem) {

    // Add properties to our template
    Object.defineProperties(template, {

        // Set the element to render into
        elem: {
            value: elem,
            writable: true
        },

        // Add state
        state: {
            value: props,
            writable: true
        },

        // Add the `setState()` method
        setState: {
            value: function (props) {

                // Shallow merge new properties into state object
                for (var key in props) {
                    if (props.hasOwnProperty(key)) {
                        template.state[key] = props[key];
                    }
                }

                // Render the element
                render(template, template.elem);

                // Return the elem for use elsewhere
                return template.elem;

            }
        }

    });

    // Return the template so you can assign it to a variable if desired
    return template;

};

There are two ways to use our component() method. In each case, we’ll pass in the props argument to access our template’s state.

Example 1: Pass in an existing template #

To use it, you can pass in an existing template and assign your initial state.

var todoList = function (props) {

    // Setup our template
    var template = '';

    // Loop through the todos
    for (var i = 0; i < props.todos.length; i++) {
        var todo = props.todos[i];

        // Check if it's completed
        var checked = todo.completed ? 'checked' : '';

        // Create the todo item
        template += 
            '<label>' + 
                '<input type="checkbox" value="' + todo.item + '" ' + checked + '>' +
                todo.item +
            '</label>';
    }

    // Return completed template
    return template;

};

component(todoList, {
    todos: [
        {
            item: 'Eat',
            completed: false
        },
        {
            item: 'Take a nap',
            completed: true
        },
        {
            item: 'Eat again',
            completed: false
        }
    ]
}, document.querySelector('#todo-list'));

Our todoList() template is now a component with state, and calling todoList.setState() will cause the associated element to re-render.

Example 2: Create the template with the component() method #

You can alternatively set up a template for the first time with the component() method.

var todoList = component(function (props) {

    // Setup our template
    var template = '';

    // Loop through the todos
    for (var i = 0; i < props.todos.length; i++) {
        var todo = props.todos[i];

        // Check if it's completed
        var checked = todo.completed ? 'checked' : '';

        // Create the todo item
        template += 
            '<label>' + 
                '<input type="checkbox" value="' + todo.item + '" ' + checked + '>' +
                todo.item +
            '</label>';
    }

    // Return completed template
    return template;

}, {
    todos: [
        {
            item: 'Eat',
            completed: false
        },
        {
            item: 'Take a nap',
            completed: true
        },
        {
            item: 'Eat again',
            completed: false
        }
    ]
}, document.querySelector('#todo-list'));

And just like the previous example, calling todoList.setState() will cause a re-render.

How this works #

The Object.defineProperties() method is used to assign properties to a JavaScript object (and in JavaScript, confusingly, everything is an object, not just actual objects like {}).

What makes it nicer than just doing object.property = 'something' is that you can also set whether or not people should be able to modify it, if it should show up in loops, and so on.

With our component() method, we’re defining the template’s elem and state properties. We’re also assigning a function to setState(), and automatically passing in in the template’s state property.


🚀 Make 2018 the year you master JavaScript! My pocket guides and mini courses are short, focused, and made for beginners. You can do this!

Have any questions or comments about this post? Email me at chris@gomakethings.com or contact me on Twitter at @ChrisFerdinandi.

Get Daily Developer Tips