Creating a Web Component in Stencil

February 23rd 2018 Stencil JS Web Components

Stencil is a new lightweight framework for creating web components from the authors of Ionic. It's going to form the basis of components in Ionic 4, which will make them framework agnostic and hence allow writing Ionic applications in frameworks other than Angular. Although Stencil is still in early stages, I decided to give it a try and see what the experience is like.

There's no CLI yet, so the best way to create a new project is by cloning the starter project.

git clone https://github.com/ionic-team/stencil-component-starter.git my-component
cd .\my-component\
git remote rm origin

We can now run the project to see the sample component it contains:

npm install
npm start

This will start a development server on http://localhost:3333 with file watcher enabled to trigger rebuild as we edit the files. Although Stencil components will run in all current browsers, development build is ECMAScript 6 only by default, so you'll need to use a compatible browser, e.g. Chrome.

My component will use the GitHub API to retrieve some users. I chose that to try out as many Stencil concepts as possible, including HTTP calls to external APIs.

For starters, I took care of rendering the list. I replaced the component source code with the following:

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.scss',
  shadow: true
})
export class MyComponent {

  @State() users: Array<any>;

  render() {
   return (
      <ul>
        {this.users.map(user =>
          <li><a href={user.html_url}>{user.login}</a></li>
        )}
      </ul>
    );
  }
}

As you can see, the code looks like a mixture of Angular and React. The class structure and the decorators look very Angular-like, while the render function uses the React XML-like syntax - TSX. Even if you're familiar with both, you might wonder what the @State attribute does. Well, it indicates that the property is a part of the component state. Because of that, render() will be invoked whenever its value changes and re-render the component.

To call the GitHub API, I will use the new Fetch API. Stencil will polyfill it in browsers that don't support it yet:

componentWillLoad() {
  let url = 'https://api.github.com/users';
  fetch(url).then(response => {
    response.json().then(json => {
      this.users = json;
    });
  });
}

The componentWillLoad() function is a part of Stencil's component life-cycle. It will be run only once while the component is initializing.

As the last feature of my component, I'll implement the option to customize the number of users to retrieve.

@Prop() count = 20;

componentWillLoad() {
  let url = `https://api.github.com/users?per_page=${this.count}`;
  fetch(url).then(response => {
    response.json().then(json => {
      this.users = json;
    });
  });
}

The @Prop decorator exposes the class property as an element attribute so that it can be set when the element is used in a page. In the project, this page is index.html and that's where we can set a different value:

<body>
  <my-component count="10"></my-component>
</body>

The change in index.html wasn't automatically picked up by the browser even with file watch enabled. I had to restart the development server and refresh the browser for the change to take effect.

Of course, not everything works perfectly yet. For example, the starter project includes a setup for unit tests which fail for me even in a freshly cloned starter project. Still, the framework looks promising and could become a great tool for developing web components.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License