Stencil Starter Project

A great resource for starting to build a Stencil component (particularly if you want to build a standalone component), is the Stencil Component Starter. Navigate to the directory where you want your project to live and follow the instructions.



git clone

cd side-menu

git remote rm origin

npm install // Installing the Stencil App Startergit clone https://github.com/ionic-team/stencil-component-starter.git side-menucd side-menugit remote rm originnpm install

They give you a great framework to start with and your directory structure should look a bit like this.

my-component.tsx and the file structure

Our first goal here is to rename all of our components to side-menu, from MyComponent to SideMenu (side-menu). Don’t count this out as a simple step. There are four files you need to modify after your rename all of your files and folders from my-component to side-menu.

// side-menu.tsx

@Component({

tag: 'side-menu',

styleUrl: 'side-menu.css',

shadow: true

})

export class SideMenu {

... // index.html

<!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/side-menu.js"></script>

</head>

<body>

<side-menu></side-menu>

</body>

</html> // stencil.config.js

exports.config = {

namespace: 'side-menu',

generateDistribution: true

};

exports.devServer = {

root: 'www',

watchGlob: '**/**'

} // package.json

"name": "side-menu",

"version": "0.0.1",

"description": "Stencil Component Starter",

"main": "dist/side-menu.js",

"types": "dist/types/index.d.ts","collection": "dist/collection/collection-manifest.json",

"files": [

"dist/"

],

"browser": "dist/side-menu.js",

...

Your final file structure should look something like this.

File structure for side-menu

Now if we ‘run npm’ start we actually get something out!

Your first component!

Definitely something to improve would be an easier way to make a new component in the right directory, but for a beta product (Stencil is in beta) I can see what they are doing so far.

Making it an actual side menu

Well there isn’t much here but now you are all setup to start actually building your component. There are two main properties of a side menu, first, that it overlaps on top of the current view, and second that it has a greyed out background (on a mobile device). Let's create that view in the render() method of our side-menu.tsx

return (

<div class="side-menu">

<div class="menu-background">

</div>

<div class="actual-menu">

This is my main menu!

</div>

</div>

);

Now we have our side menu, and our background. To make everything nice and pretty, add these styles to your side-menu.css

.side-menu {

position: absolute;

top: 0px;

left: 0px;

height: 100%;

width: 100%;

z-index: 20;

background-color: transparent !important;

padding: 0;

} .actual-menu {

position: absolute;

top: 0px;

left: 0px;

width: 250px;

min-height: 100%;

background-color: white;

z-index: 30;

} .menu-button {

width: 100%;

margin: 0px;

padding: 10px;

} .menu-background {

position: absolute;

background-color: rgba(38,38,38, 0.2);

top: 0px;

left: 0px;

width: 100%;

min-height: 100%;

z-index: 5;

}

This will allow us to have a side menu that automatically displays, and that looks something like this.

Your First Side Menu

Adding Events To Your Component

Now that we have a custom component that looks like a side menu, we will also need it to function like a side menu. If you see with our current menu, there is no way to exit. Normally, on a mobile device if you click in the grey background area it will remove the menu and take you back to the content you are looking at, so we will implement that functionality.

To do this, we will need two pieces of functionality. First, we will need our component to emit a custom event, that I am calling “backgroundToggle”. In order to do that, we will import both Event and EventEmitter from @stencil/core and attach it to an onclick event for our menu-background div.

// side-menu.tsx

@Event() backgroundToggle: EventEmitter; menuToggle(e) {

console.log('Background toggled menu', e);

this.backgroundToggle.emit(e);

} render() {

return (

<div class="side-menu">

<div class="menu-background" onClick={(e) => this.menuToggle(e)}>

</div>

<div class="actual-menu">

This is my side menu!

</div>

</div>

);

}

This allows Stencil to bind our event menuToggle to the native onClick event from the div menu-backgroud.

Listening To Events

Since we are building a standalone component, we do not want to assume we know what the user wants to do when someone clicks on the background. Instead, we should implement a way to listen for this event in our main index.html page, and use that to hide our side menu. There are two ways to do this, the first way is to implement the Listen tag that you can use if you have a transpiler like typescript.

// index.html with transpiler

@Listen('backgroundToggle')

function toggleBackground(e) {

console.log('recieved event', e);

document.getElementsByTagName('side-menu')[0].style.display = 'none';

}

Unfortunately for us, the index.html file that Stencil has provided is not transpiled in anyway, so we need to make sure to use es5 features to be able to listen to our emitted event. This would also be considered best practice because we want to test our component in a way that an end user might implement, and we cannot count on the fact that they might be using TypeScript.

To implement a listener that works, we use our good ole fashioned document.addEventListener function!

//index.html

...

<side-menu>

</side-menu>

</body>

<script>

document.addEventListener('toggleBackground', function hideMenu(e) {

console.log('Event Recieved');

document.getElementsByTagName('side-menu')[0].style.display = 'none';

});

Now we can listen to our emitted event on the main page, and react accordingly when someone clicks on the background of our menu!

Allowing User Customization With <slot />

If we really are building a component that could be used in any project, we have no idea how someone would want to structure their menu options. There could be a user auth portion, a simple list of menu items, or a information dialogue about what someone is looking at.

<side-menu>

<div class="example-button">This is a nav butt!</div>

</side-menu>

To enable this kind of customization, we need to allow the user to pass in HTML to the side menu as the menus content, and to do this we have the handy <slot /> tag that is incredibly powerful. When we add a <slot /> tag to our component, whatever html that a developer adds inside of our custom tag will be added in place of the <slot />. For us, we want that to be added inside of the “actual-menu” (naming isn’t my forte).

<div class="actual-menu">

</slot>

</div>

I can’t reinforce how powerful this slot tag can be. Particularly for a side menu, you will want the user to be able to add navigation buttons, and customized content that would not be possible without it.

In Summary

The Stencil component compiler is an incredibly powerful tool that will enable great cross framework components to be built. The ability to standardize components and deliver these in React, Vue, Angular, or whatever your cup of tea is makes it a very useful tool for anyone (particularly ionic) who wants to develop and share web components across teams, projects, and businesses without worrying about the next hot front-end framework. I personally am very excited to start using and open sourcing some custom components for the community to improve upon.