How I created SmileToUnlock with StencilJS - Part 1/2


A week ago, in a flourish of creativity, I put together a Web Component called http://smiletounlock.com. Want to give away free content on your site? How about asking for a smile in return 😁.

I used a tool called StencilJS, a number of HTML5 APIs and the Azure Emotive API to detect happiness. In this two-part article, I’m going to specify how I created it.

In Part 1 of the article, I’m going to talk about Web Components and how to build them using StencilJS. In Part 2 I’ll go deeper and explain how I created Smile to Unlock using HTML5 APIs and the Azure Emotive API.

What are Web Components?

Web components allow you to create your own custom HTML elements outside of any framework, so they work with every framework! You can use Web Components anywhere: Angular, Vue, React or Wordpress, it doesn’t matter it works on the browser level.

For example, to add smiletounlock to your own site, simply drop a script tag in your HTML file:

<script src="https://unpkg.com/[email protected]/dist/smiletounlock.js"></script>

and then add some HTML to your file:

<smile-to-unlock api-key="<YOUR_API_KEY_HERE>"></smile-to-unlock>

Now those of you, who have been working on a modern JS framework like React, Angular or Vue, will recognize the above pattern. We’ve added a piece of HTML, a custom tag called smile-to-unlock. This is a component that encapsulates the display and behavior of our smile to unlock widget.

We added a custom component but we never included a framework like React, Angular or Vue.

Tip

The first rule of web components is that they don’t rely on any framework, they are part of the HTML spec. And now browsers are slowly starting to support them. Chrome and Safari support them natively and the most of the other browsers support them with a polyfill.

What is StencilJS?

You can create Web Components natively in the browser, without needing to include any other script file. They just work. But there are frameworks out there that help you in creating web components, and the most famous is http://polymer.io. They handle things like the boiler plate code, loading the browser pollyfills, etc…​

StencilJS is the new kid on the block but it differs from the other frameworks by not being a framework, it’s a build tool.

The output of StencilJS is just raw native web component code. You don’t need to include the StencilJS library to run a StencilJS generated web component. You just need StencilJS to build the Web Component. The output of the build process is a native web component you can use on its own.

Tip

Think of StencilJS as gulp for Web Components.

It’s made by the Ionic team, the people behind the open source mobile development framework Ionic Framework. Historically Ionic has been built using Angular but with the advent of Progressive Web Apps they needed something more lightweight. Something their users could use to build fast loading mobile optimised sites. And Web Components appear to be the answer.

It’s currently in beta but the Ionic team is dogfooding it at the moment. They are building Ionic 4 using StencilJS and the tool is improving daily.

How to use StencilJS?

To begin with, lets create a stenciljs project

git clone https://github.com/ionic-team/stencil-app-starter my-app
cd my-app
git remote rm origin
npm install

This creates a folder called my-app and inside there you will find your stencil project.

.
├── LICENSE
├── package-lock.json
├── package.json
├── readme.md
├── src               // <-- This is where all the soruce code goes
├── stencil.config.js // <-- This is your stencil configuration file
├── tsconfig.json     // <-- This is your typescript configuration file
└── www               // <-- This is your output folder with the built stencil web component

The npm package.json file has some scripts you can run. They can help with development. The first is

npm run dev

This starts the local build tool running, watches files for changes and opens up the browser.

Note

Remember, StencilJS is a build tool. We write our components in tsx files and stencil compiles them down into native web components.

When you are ready to release, you can run npm run build. This creates a dist folder with the web component compiled into a distributable package.

Your first StencilJS component

We write stencil components in a special dialect of Typescript called tsx

Note

JSX is an extension to the Javascript specification. You can find details here: https://facebook.github.io/jsx/. It allows you to write HTML inside your Javascript files.

TSX is the Typescript implementation of JSX https://www.typescriptlang.org/docs/handbook/jsx.html, so you can write HTML inside Typescript files.

This allows us to use JSX syntax, the part of the code below that looks like HTML, in Typescript.

A basic StencilJS component might look like this:

import { Component } from '@stencil/core'; (1)

@Component({ (2)
  tag: 'cc-simple' (3)
})
export class Simple {
  render() { (4)
    return (
      <p>
        Simple
      </p>
    );
  }
}
1 We include the required entities from @stencil/core
2 We decorate an ES6 class with the @Component decorator.
3 We specify the custom tag that this class is to be assocaited with, the tag name must contain a - character.
4 We have a render function where we return a p tag with the word Simple using the special JSX syntax. +

The React developers out there will get used to concepts like JSX and the render function. The Angular developers out there will get used to the idea of decorating classes with something like a @Component decorator. Again, Stencil is not using Angular or React, it’s just borrowing ideas from both frameworks.

Tip

StencilJS seems to be the best of React and Angular merged together.

Questions

Well, you’ve got a basic grasp of StencilJS now and I think the best way to explain the rest, is by asking and answering a bunch of questions.

How do you do data binding?

If you have a state variable called, say, foo, how do you render the value of foo?

import { Component } from '@stencil/core';

@Component({
  tag: 'cc-simple'
})
export class Simple {

  foo: number; (1)

  constructor() {
      this.foo = 10; (2)
  }

  render() {
    return (
      <p>
        {this.foo} (3)
      </p>
    );
  }
}
1 Define a property on our class which we want to hold some state.
2 We set a value to our foo property.
3 We output the value of our foo property in the template.

How do we call the render function when something changes?

When the foo variable changes, we will want to call the render function to update our UI. To help with this pattern StencilJS comes with a @State decorator which you can use like this:

import { Component, State } from '@stencil/core';

@Component({
  tag: 'cc-simple'
})
export class Simple {

  @State() foo: number; (1)

  constructor() {
    this.foo = 10;
    setInterval(() => {
      this.foo += 1; (2)
    }, 1000)
  }

  render() {
    return (
      <p>
        {this.foo}
      </p>
    );
  }
}
1 We decorate our foo property with @State.
2 Now every time foo changes, the render function is called automatically.

How do we pass parameters into our Web Component?

We can pass parameters into our component from the HTML:

<cc-simple first="asim" last="hussain"></cc-simple>

To capture those values in our component class we use the @Prop decorator:

import { Component, Prop } from '@stencil/core'; (1)

@Component({
  tag: 'cc-simple'
})
export class Simple {

  @Prop() first; (2)
  @Prop() last;

  render() {
    return (
      <p>
        {this.first}, {this.last} (3)
      </p>
    );
  }
}
1 The decorator is a function that we need to import from the stencil library.
2 We decorate a property in our class with the @Prop decorator.
3 We can use and bind to this property in the JSX, just like before.

How do you call a method on the component?

If we have a component like this:

<cc-simple></cc-simple>

we can expose a method on our component class so it can be called externally:

var component = document.querySelector('cc-simple');
component.some_method();

As you might have guessed, we do this by using another decorator, called @Method:

import { Component, Method } from '@stencil/core'; (1)

@Component({
  tag: 'cc-simple'
})
export class Simple {

  @Method() some_method() { (2)
    // Add code here...
  }

  render() {
    return (
      <p>
        Simple
      </p>
    );
  }
}
1 We import the @Method decotator.
2 Afterwards any class method we decorate will be callable externally.

How does the component send events?

To communicate externally we use custom events:

<cc-simple></cc-simple>
<script>
(function() {
  var component = document.querySelector('cc-simple');
  component.addEventListener("myEvent", function (ev) {
    // ev.detail contains the data passed out from the component
    // Handle event here...
  });
})();
</script>

Again we implement this by using the @Event decorator and an EventEmitter.

import { Component, EventEmitter, Event } from '@stencil/core';  (1)

@Component({
  tag: 'cc-simple'
})
export class Simple {

  @Event() myEvent: EventEmitter; (2)

  constructor() {
      setTimeout(() => {
        this.myEvent.emit({foo: 'bar'}) (3)
      }, 1000)
  }

  render() {
    return (
      <p>
        Simple
      </p>
    );
  }
}
1 We import the @Event decorator and the EventEmitter class.
2 We create a property with the name of the event we want to send, we decorate it with @Event and we give it a type of EventEmitter.
3 To send an event we call emit on the EventEmitter instance and we pass it (to) the data which will be set on the detail property of the event object.

How do we get a reference to the Dom Element?

We may want to do some manipulation of our component dom from our component class itself. To do that we need a reference to the dom which we can get by using the @Element decorator:

import { Component, Element, Method } from '@stencil/core'; (1)

@Component({
  tag: 'cc-simple'
})
export class Simple {

  @Element() el: HTMLElement; (2)

  @Method()
  hide() {
    this.el.querySelector("p").style.display = 'none' (3)
  }

  render() {
    return (
      <p>
        Simple
      </p>
    );
  }
}
1 We import the required @Element decorator.
2 We declare a property on our component called el (it can be named anything) decorate it with @Element and give it a type of HTMLElement
3 Now we can refer to the root dom node of our component with this.el and perform any dom manipulation we want.

Summary

In this article I’ve explained what are web components, what is StencilJS, and the basics of how we can build web components using StencilJS. There are still a number of other features we haven’t covered yet, like routing and testing, but the above is enough to prepare you for the next article, where I’ll explain how I create the smile to unlock web component using the Azure Emotive API.