Lorenz Weiß Hi, I'm Lorenz, a frontend-focused web developer. I'm in love with the internet and its people and interested in everything related to it.

Building Jamstack-friendly components with Tonic

4 min read 1391

Tonic

The world of frontend web development changes quickly, thanks to new frameworks mixing up the market and new ideas and approaches evolving rapidly.

React and Vue, for example, were one of the first component-based frameworks, and they are currently the most widely used.

With the trend of Jamstack websites moving back to statically rendered websites instead of dynamically rendered websites on the client side, new frameworks built on top of React or Vue are gaining popularity, such as Next.JS, Gatsby, and Nuxt.

In addition, new frameworks like Svelte are being adopted by developers because they make it even easier to create statically rendered websites without losing the modern feel of dynamic frameworks like React.

What is Tonic?

Tonic is a component framework that takes this minimal yet elegant approach and works perfectly with Jamstack, as it doesn’t use virtual DOM and is server-side rendered by default. It’s based on web components and can be used out of the box without a build tool. It is super small (5KB!) and blazingly fast.

This article explains how Tonic works and why you should use it when building a Jamstack website. I will also show you how to set up a project and build your first components using Tonic.

How Tonic works

Tonic uses native web components, a concept introduced in 2011 that is now part of all modern browsers by default.

One of the most famous websites powered by web components is YouTube. If you look at the source code, you’ll quickly discover many components in the DOM:

Components in the DOM

Although using web components natively is possible, it can be complex and difficult to understand.

We made a custom demo for .
No really. Click here to check it out.

Because of this, Tonic has added some abstraction on top of web components that gives it a similar look to frameworks like React and Vue, but, aside from using virtual DOM to render elements, it actually uses the web components natively built into the browser.

Why use Tonic?

So, why use it when there are so many frameworks out there? Here are some great reasons you should use Tonic to create a Jamstack website:

  • It’s super small — basically non-existent. It’s only 350 lines of code and it’s 5KB in size, so your website will load quickly
  • You don’t need a build tool. Have you ever gotten lost in a webpack configuration or waited for your frontend build to finish? With Tonic, the code works out of the box, no build tools required
  • You can debug your production code as the web components appear in the HTML document of your production website, and you can see the names of your components in the DOM of your website
  • The developer experience is the same as other modern frameworks, so it’s plain-old fun to write your components in it

How to use Tonic

Now that we’ve seen the benefits of Tonic, let’s bootstrap a project using it. Our goal is to have a page that contains a counter button and a number next to it that increments every time we click the button.

Because we don’t need a build tool, we can create a simple HTML file that contains:

<!doctype html>  
<html>  
<head>  
    <title>Website with Counter</title>  
      <script src="https://unpkg.com/@optoolco/[email protected]/dist/index.js" ></script>  
    <script type="module" src="index.js" ></script>  
    <script src="https://unpkg.com/@optoolco/[email protected]/dist/index.js" ></script>  
    <link rel="stylesheet" type="text/css" href="index.css" media="screen" />  
</head>  
<body>
    <p>Hello</p>
</body>  
</html>

Let’s go through the parts we have in the head tag. We have the title of our website, and we need a script tag to import the code from Tonic. We use the unpkg tool to import it with a script tag.

The next script tag is for importing our custom JavaScript file we will create with no content for now. We also want some styles, so we’ll create a style.css file and link it as a stylesheet.

The body part will contain our custom counter element. We’ll leave it empty for now and fill it in when we write the actual component. Now, we should have the following classic web development files:

-- index.css
-- index.js
-- index.html

And that’s it! You don’t need any other files or a node modules folder. However, if you want to run it on a development server, you can initialize it as an npm package and use serve to run your website on localhost by adding the following package.json file:

// package.json
{  
     "name": "tonic-proj",  
     "version": "1.0.0",  
     "description": "",  
     "main": "index.js",  
     "scripts": {  
         "start": "serve"  
     },  
     "keywords": [],  
     "author": "",  
     "license": "ISC",  
     "dependencies": {  
         "serve": "^13.0.2"  
     }  
}

Then, after running npm i and npm start, the project should open on localhost:3000. This should be a pretty static hello message.

Creating the first Tonic component

So far, there’s nothing special here. But to add the actual counter, we need to write our first Tonic component.

We have to first get the Tonic class from the imported package. At the top of the index.js file, let’s add the following:

const components = window.components;  
const Tonic = components.Tonic;

Then, we can create our first component that extends the Tonic class:

// ...

class CounterElement extends Tonic {  
    state = { counter: 0 };

    click(e) {
        const trigger = Tonic.match(e.target, "button");
        if (trigger) {
            this.state.counter++;
            this.reRender();      
        }
    }

    render() {  
        if (typeof this.state.counter === "undefined") {  
            this.state.counter = 0;  
        }
        return this.html`
            <div>
                 <button>+1</button>
                <p>Count: ${this.state.counter.toString()}</p>
            </div>
        `;  
    }
}

As you can see, it looks similar to React code and the JSX pattern. Similar to React, you need a render function in your class that returns the return value of the inherited function this.html. This function accepts a string containing the HTML elements you want your component to render.

If you want to render dynamic values, you can simply add them with a template string. The component also accepts a set of event methods that are fired when the HTML element events are triggered.

In our example, we use the click event to listen for when someone clicks on our component. Because we want the count to fire only when we have actually clicked on the button, and not on the entire component, we add a matching check for this in the click method. This is because of Tonic’s decision to use event delegation instead of multiple event listeners.

To wrap up the JavaScript piece, we also need to add this component to Tonic by adding the following to the end of our JavaScript file:

// all the above code...

Tonic.add(CounterElement)

Don’t panic that nothing is displayed on your website yet. We need to add the element to the HTML file by adding it as a web component that Tonic automatically creates for you.

...
<body>
    <counter-element></counter-element>
</body>

Now, you should be able to see your counter element in the DOM, and you should even be able to see it when you inspect the DOM of the site.

Counter Element

Here, you can see a fully functional example of the component we just created:

See the Pen
Untitled
by lowe1111 (@lowe1111)
on CodePen.

Conclusion

Writing components in Tonic feels pretty similar to writing components in other frameworks. In particular, the class-based approach is very similar to writing class components in React. You also have lifecycle methods that are automatically triggered when the component is updated or rendered, and you return HTML elements in a render function.

The major difference is that Tonic uses your exact written code, and the DOM structure matches exactly what you have on the server and what the user sees in their browser.

This means that you can create Jamstack components without having a build tool or rendering engine installed on the server side. It uses the power of web components.

And, although the developer experience is pretty similar to React or other modern frameworks, using Tonic is much more streamlined, the debugging feels simpler, thanks to the lack of build tools, and because of static rendering, Tonic also loads and renders lightning fast. Happy hacking!

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

.
Lorenz Weiß Hi, I'm Lorenz, a frontend-focused web developer. I'm in love with the internet and its people and interested in everything related to it.

Leave a Reply