How to create a reusable Web Component with Stencil and use it in your Ionic 4 application with Angular


Ionic 4 is finally here and ready for production. And I'm sure you've all heard about their heavy use of reusable Web Components to make Ionic framework agnostic (and many other reasons). That's why I specified "using Angular" in the title. The Component you will make in this tutorial will be able to be used in many web frameworks.

To make our lives easier, the Ionic team created Stencil, a reusable web component compiler. And that's what we're going to use in this article.

In this article, you will learn to:

  • Make a simple reusable Web Component using Stencil.
  • Publish this Web Component on NPM.
  • Import it and use it in your Ionic 4 application.

Enough talk, more code. Let's make this bad boy.

My first reusable Web Component

We're going to create a simple video playing in the background of the screen. Like the one you can see on many landing pages. I actually made another article explaining how to achieve this:

Making a Web Component inside our own project and making a maintained and reusable one available on NPM is not the same story. There's a lot of configuration to do to be sure everything is OK. Fortunately for us, the Stencil team made a starter project with everything you need.

So let's clone this repository and make it ours:

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

Install the node modules:

npm install

Here is how the project structure looks like:

What we're interested in is the components folder. That's where you'll add your components. So, yes, this project is already made to publish multiple components at once. Pretty neat, right?

If you look at the my-component folder, you will understand quite quickly how everything works. If you've already done some Angular Components, it all should look familiar to you. Once you've played around with this simple component, you can just delete it. And also delete the utils folder, you won't need it.

I will not cover testing your component in this tutorial. If you want more information about writing Unit Tests and End to End Tests, please refer to the official Stencil documentation.

Let's create a my-background-video-component. Create the folder in components. Create the .tsx file and the .css file.

Let's keep it simple. We want 3 public properties for our video element:

  • The video source : src
  • The fallback image : poster
  • The mute state of the video : muted

Here is the my-background-video-component.tsx file:

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

@Component({
  tag: 'my-background-video-component',
  styleUrl: 'my-background-video-component.css',
  shadow: true
})
export class MyBackgroundVideoComponent {
  /**
   * The video src
   */
  @Prop() src: string;

  /**
   * The image shown while the video is loading
   */
  @Prop() poster: string;

  /**
   * To mute the video
   */
  @Prop() muted: boolean = true;

  @Element() private videoElement: HTMLVideoElement; // pointer to the created video element

  componentDidLoad() {
    // sometimes, as the component is loaded dynamically, the video starts with sound, even if muted property is set to true
    if (this.muted) {
      this.videoElement.muted = true;
    }
  }

  render() {
    return <video autoplay loop playsinline preload="auto" muted={this.muted} src={this.src} poster={this.poster}></video>;
  }
}

Okay, I've added some complexity here, but it's just to show you a little more of what we can do with this component. I created an @Element()pointer to the html video element to be able to mute it when it shows (in the componentDidLoad function). But you're still with me, right?

I won't dive in the details of the css. It just places the video at the right place in the background. Here is the my-background-video-component.css file:

video {
  position: fixed;
  top: 50%;
  left: 50%;
  min-width: 100%;
  min-height: 100%;
  width: auto;
  height: auto;
  transform: translateX(-50%) translateY(-50%);
}

We only need to specify the video tag in this css, as the css will ONLY be applied to this component, because we use the Shadow DOM, which makes the css scoped only to this component. More information on the official Stencil documentation.

That's it! your Component is done and ready to be used!

Use your awesome Component

Okay, that's great, but how do I test this component to see if it works correctly?

First, we need to build our Web Component. And for that, we need a last file to edit to have the right name for our built Component. Modify the namespace value in stencil.config.ts like this:

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

export const config: Config = {
  namespace: 'my-background-video-component',
  outputTargets:[
    { type: 'dist' },
    { type: 'docs' },
    {
      type: 'www',
      serviceWorker: null // disable service workers
    }
  ]
};

Now run this to build your component:

npm run build

The Web Component will be built in a dist folder. Which is the folder that will be available in your NPM package once published.

Let's modify the index.html file to call our new component and make it play a sample video:

<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
  <title>Stencil Component Starter</title>
  <script src="/build/my-background-video-component.js"></script> <!-- import the component -->

</head>
<body>

  <my-background-video-component
    src="https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4"
    poster="https://sample-videos.com/img/Sample-png-image-500kb.png">
  </my-background-video-component>

</body>
</html>

The Stencil template projet already has a lightweight web server available to try your component.

To start this web server, just run:

npm run start

And voila! Your component is working in a web page!

You'll see the fallback image first, and then the video will play. Note that the video will need to load a bit before playing.

Publish you component on NPM

No need for you to publish this sample component to NPM. I've already done that, because this Component is a real one, that I really use in my projects. See the Background Video Component Github

Of course, if you want to publish your package, you'll need to change the package.json file to name and describe it like you want.

{
  "name": "my-background-video-component",
  "version": "0.0.1",
  "description": "A Stencil Component for a fullscreen video playing in the background of a web page",
  "module": "dist/esm/index.js",
  "main": "dist/index.js",
  "unpkg": "dist/ionic.js",
  "types": "dist/types/components.d.ts",
  "collection": "dist/collection/collection-manifest.json",
  "files": [
    "dist/"
  ],
  "scripts": {
    "build": "stencil build --docs",
    "start": "stencil build --dev --watch --serve",
    "test": "stencil test --spec --e2e",
    "test.watch": "stencil test --spec --e2e --watchAll"
  },
  "dependencies": {},
  "devDependencies": {
    "@stencil/core": "~0.16.2"
  },
  "license": "MIT"
}

And don't forget to edit the readme.md file if you want developers to understand how to use your package.

Let's publish this awesome Web Component on NPM:

npm publish

If this is your first time publishing a package to NPM, you'll need to create a user. I'll let you read the official NPM documentation for that.

That's it, your component is now available on NPM !

How to use it in your Angular Ionic 4 project

For this part, I'll assume you've already have an Ionic 4 app. If you don't, just follow the Ionic documentation.

Install the node package we've published

we'll get the Web Component package from our Geek Learning NPM package Background Video Component.

Go into your Ionic 4 application and run:

npm install --save gl-ionic-background-video

Import the component

Import it in the module of the page which will use it (home.module.ts for example):

import 'gl-ionic-background-video';

Include the Custom Element Schema

You also need to tell your page's module to allow non-angular elements, otherwise, you will get an error. Add the CUSTOM_ELEMENTS_SCHEMA to the list of schemas of you module:

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    RouterModule.forChild([
      {
        path: '',
        component: HomePage
      }
    ])
  ],
  declarations: [HomePage],
  schemas: [CUSTOM_ELEMENTS_SCHEMA] // here is the schema declaration to add
})
export class HomePageModule {}

Call defineCustomElements

Call the defineCustomElements function from the Stencil component collection in your main.ts like this:

import { defineCustomElements } from 'gl-ionic-background-video/dist/loader'; // add this line

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.log(err)); // This is already in main.ts, it's just to show you where to put everything.
  
defineCustomElements(window); // call the function here

Use the Web Component in your html

Then add the gl-background-video html element in the ion-content of your home page html:

<ion-content force-overscroll="false">
  <gl-background-video src="https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4" poster="https://sample-videos.com/img/Sample-png-image-500kb.png"></gl-background-video>
  <div class="page-centered-element">
    <div class="content">
      <h1>Welcome</h1>
      <h5>This is a sample app</h5>
      <div class="buttons-container">
        <ion-button>Login</ion-button>
      </div>
    </div>
  </div>
</ion-content>

And that's it! You can finally use your awesome Web Component in your Ionic 4 application!

Conclusion

The Web is evolving quickly. A new era of generic, framework agnostic, Web Components is coming. So let's embrace it, and start making awesome reusable Web Components with Stencil!

Don't hesitate to give feedbacks and ask questions in the comments section below !

Mitch

Mitch

I'm a freelance full stack developer working in Paris. I love everything that involves websites and mobile apps. That includes startups, UX design, growth hacking, productivity and many more.

Read More