Web Development

Getting Started With Stencil – A Web Component Generator


DemoCode

Stencil is a web component generator / compiler which is developed by the Ionic team. The project’s website can be found at https://stenciljs.com/.

Stencil lets us author standard compliant web component features which are known from popular web frameworks like Angular and React.

Web components are re-usable user interface components which are do not require any additional framework. Web components can be included in any HTML page by using custom HTML tags like <my-web-component></my-web-component>.

The technology to support web components is build into modern web browsers. The concept of web component is build on top of the following browser technologies:

  • Custom Elements
  • HTML Templates
  • Shadow DOM
  • HTML Imports

In this tutorial we’ll cover the basics of Stencil and by following a practical example you’ll learn how to implement your first web component with Stencil.

Installing Stencil

You need to clone the Git repository of stencil into a new directory. The following command clones the repository into the new folder my-stencil-app:

$ git clone https://github.com/ionic-team/stencil-app-starter my-stencil-app

Next, change into the newly created folder:

$ cd my-stencil-app

Next make sure to remove the remote repository destination:

$ git remote rm origin

Next we need to make sure that app dependencies of the Stencil project are installed by executing the following command:

$ npm install

Having completed the installation successfully we’re able to test if everything is working correctly by starting up the live-reload development web server:

$ npm start

You should be able to see the following output on the console:

This is the confirmation that the server started up successfully and that it is now listing for changes. The browser window is also opening up automatically and shows the output which is generated by the default Stencil project:

This output is generated by a web component which is part of the standard project. Let’s take a look at the initial Stencil project structure and see where we can find the corresponding code.

Within the src folder you can find the complete source code of the application and that’s the location where we’ll be working in when extending the project in the next section.

The main HTML file is index.html. If you take a look into that file you’ll find the following code in the body section:

<body>
  <my-name first="Stencil" last="JS"></my-name>
</body>

The <my-name> element is a custom web component which is implemented as part of this project. The corresponding implementation can be found in the folder src/components/my-name. First, let’s take a look into file my-name.tsx:

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

@Component({
  tag: 'my-name',
  styleUrl: 'my-name.scss'
})
export class MyName {
  @Prop() first: string;
  @Prop() last: string;
  render() {
    return (
      <div>
        Hello, my name is {this.first} {this.last}
      </div>
    );
  }
}

As you can see the implementation of the web component contains many elements which are familiar if you have been working with Angular or React before.

Stencil uses decorators to add component metadata (@Component) and to define component properties (@Prop). This concept is known from Angular. The @Component decorator contains two properties. The first property is named tag and contains the tag name of our component as a string. The value which is used here is my-name and that’s the reason why this component can be included in index.html by using the element <my-name>.
The second property is named styleUrl and contains the path to the file which is containing the SCSS code which should be used for this component.

The component input properties are defined by using the @Props() decorator within the class MyName. As you can see the example contains two properties: first and last. TypeScript syntax is used to declare that both properties must be of type string.

JSX code is used to include the template code of the component which is used to output the resulting HTML code. The template code is returned by a method called render which is added to the component path. This concept is known from the React framework.

Finally, take a look into file stencil.config.js:

exports.config = {
  bundles: [
    { components: ['my-name'] }
  ],
  collections: [
    { name: '@stencil/router' }
  ]
};
exports.devServer = {
  root: 'www',
  watchGlob: '**/**'
}

Important to note is that each custom web component which is implemented in our project needs to be added to a bundle. Bundles are used to group together components, so that each group can be lazy loaded when needed. By default the my-name component is already added here.

Implementing A Custom Web Component With Stencil

Now that you have a basic understanding how Stencil is working and what’s included in the default project let’s extend this project and implement our own custom web component with Stencil.

The component which we’re going to implement will output a list of learning resources. The visibility of this list can be toggled by clicking on a button.

To create the new component we start with creating a new subfolder my-first-component within src/components. Next, create the following two new files within that new folder:

  • my-first-component.tsx
  • my-first-component.scss

We start the implementation in file src/components/my-first-component/my-first-component.tsx. Insert the following code:

import { Component, Event, EventEmitter, State } from '@stencil/core';
@Component({
    tag: 'my-first-component',
    styleUrl: 'my-first-component.scss'
})
export class MyFirstComponent {
    public list: Array<any> = [
        {
            name: 'Udemy',
            description: 'Udemy is an online learning platform where anyone can create and upload courses. There are over 50,000 courses on the platform covering all trending topics for web developers. ', 
            url: 'https://codingthesmartway.com/udemy',
            imageUrl: '/assets/udemy.jpg'
        },
        {
            name: 'Treehouse',
            description: 'Treehouse is focusing mainly on web design & coding. Here you can choose from 1,000s of hours of content from JavaScript to Python to iOS.',
            url: 'https://codingthesmartway.com/treehouse',
            imageUrl: '/assets/treehouse.png'
        },
        {
            name: 'Coursera',
            description: 'Coursera offers online courses taught by actual college professors from Stanford, University of Michigan, Yale University and many more. Here you can earn your master`s degree fully online',
            url: 'https://codingthesmartway.com/coursera',
            imageUrl: '/assets/coursera.png'
        },
        {
            name: 'Pluralsight',
            description: 'Pluralsight offers thousands of courses authored by leading experts. Every course contains in-depth content that goes beond the fundamentals and teaches you practical skills you can apply immediately.',
            url: 'https://codingthesmartway.com/pluralsight',
            imageUrl: '/assets/pluralsight.png'
        }
    ];
    @State() toggle: boolean = false;
    @Event() onToggle: EventEmitter;
    toggleComponent(): void {
        this.toggle = !this.toggle;
        this.onToggle.emit({visible: this.toggle})
    }
    render() {
        return (
            <div>
                <div class="jumbotron">
                    <center>
                        <h1 class="display-3">Welcome!</h1>
                        <p class="lead">This is a Stencil sample application - Demonstrating the power of pure web components!</p>
                        <button class="btn btn-primary" onClick={() => this.toggleComponent()}>Learning Resources for Web Developers</button>
                    </center>
                </div>
                
                <div class={ this.toggle ? 'active' : 'inactive'}>
                    <div class="row">
                    {
                        this.list.map(entry => 
                            <div class="col-lg-3 col-md-6 col-sd-12">
                            <div class="card">
                                <a href={ entry.url } target="_blank"><img class="card-img-top" src={ entry.imageUrl }/></a>
                                <div class="card-body">
                                    <h4 class="card-title">{ entry.name }</h4>
                                    <p class="card-text">{ entry.description }</p>
                                    <a href={ entry.url } class="btn btn-success" target="_blank">Go to { entry.name }</a>
                                </div>
                            </div>
                            </div>
                        )
                    }
                    </div>
                </div>
            </div>
        );
    }
}

The implementation is similar to what we saw before. The component class MyFirstComponent is decorated with the @Component decorator. Within that decorator the file path to the corresponding SCSS file and the name of the element is set.

As we’d like to output a list of learning resources the class contains the list property is is an array and contains the data we’d like to output.

Furthermore the Stencil @State decorator is used to define the toggle state. By default toggle is set to false which means that the list should not be visible. The user is able to change this state by clicking on a button and invoking the executing of method toggleComponent which changes the value of toggle to the opposite.

Defining state on component level is a core concept of Stencil. If the value of any state changes the component will re-render itself.

As seen before the render method is returning the JSX code which is used to generate the HTML output for the browser. Note, that we’re setting assigning the class active or inactive to the div element which is surrounding the list output depending on the value of the toggle state.

The JSX template code makes use of various Bootstrap classes, so that we need to make sure that the Bootstrap framework is included in our project later on.

The output is generated by iterating over the list array by using the JavaScript map function.

Let’s add the need CSS code into file my-first-component.scss:

.active {
    display: block;
  }
  
.inactive {
    display: none;
}
.card {
    margin-bottom: 10px;
}

In order to make the element visible and invisible the display property is used in classes active and inactive. Furthermore we’re assigning a margin of 10 pixels at the bottom to every card element.

Now we need to add our new component my-first-component to a bundle in stencil.config.js:

 bundles: [
    { components: ['my-first-component'] }
  ],

Here we’ve been removing the my-name component and instead inserted the string my-first-component.

Next step is to include

<my-first-component></my-first-component>

in the body section of index.html:

<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
  <meta charset="utf-8">
  <title>Stencil Starter App</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
  <meta name="theme-color" content="#16161d">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <script src="/build/app.js"></script>
  <link rel="apple-touch-icon" href="/assets/icon/icon.png">
  <link rel="icon" type="image/x-icon" href="/assets/icon/favicon.ico">
  <link rel="manifest" href="/manifest.json">
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
</head>
<body>
  <div class="container">
      <br/>
      <a href="https://codingthesmartway.com/" target="_blank"><img src="/assets/logo.png" width="300" /></a>
      <hr>
      <my-first-component></my-first-component>
 
  </div>
  
  <!-- Optional JavaScript -->
  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>
</body>
</html>

Furthermore <link> and <script> elements for including the Bootstrap library have been added here.

The final result should now look like the following in the browser:

If you click on the button the area displaying the learning resources becomes visible:

Deploying Your Stencil Application

Finally, if you want to deploy your Stencil application you need to execute the following command first:

$ npm run build

This command is preparing our application for production and adding the corresponding files into folder www of your project directory.

From this location you can then deploy your application to any hosting provider of your choice.

React 16+ - The Complete Guide (incl. Redux)

Dive in and learn React.js from scratch! Learn Reactjs, Redux, React Routing, Animations, Next.js basics and way more!

React 16+ – The Complete Guide

  • Build powerful, fast, user-friendly and reactive web apps
  • Apply for high-paid jobs or work as a freelancer in one the most-demanded sectors you can find in web dev right now
  • Provide amazing user experiences by leveraging the power of JavaScript with ease
Go To Course

Using and writing about best practices and latest technologies in web design & development is my passion.

    View Comments
    There are currently no comments.

    *