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 !