Andy Desmarais

A code ninja and all around tech geek

StencilJs - Part 2 - @Component decorator deep dive

2019-11-24 Andy DesmaraisWebComponents

Cover photo credit: Robin Glauser

This article assumes you have a working knowledge of web components. If you don’t, please check out my earlier series on web components.

It’s also building on a previous article, so please check that out too.

@Component decorator

Stencil, like almost all modern front-end frameworks, revolves around creating components. The syntax for creating a component in Stencil is:

@Component({
  tag: 'my-custom-element'
})
export class MyCustomElement {}

This is the simplest version of a component that can be created with Stencil.

But we’re not here for simple. Today we’re going to talk about the more complex use cases Stencil’s @Component decorator helps us solve.

We’ll cover:

Scoped styles

This will only be pertinent if you are NOT using the shadow DOM. This flag will cause Stencil to scope styles for you. With scoped styles you can still write simple selectors like h1 {} without having to worry about affecting the whole document.

Here’s an example usage:

@Component({
  tag: 'my-custom-element',
  scoped: true // Defaults to false
})
export class MyCustomElement {}

Shadow DOM flag

There’s a simple flag for enabling the shadow DOM on a Stencil component. Not a whole lot of explanation needed for this one.

Here’s an example usage:

@Component({
  tag: 'my-custom-element',
  shadow: true // Defaults to false
})
export class MyCustomElement {}

StyleUrl

This is a property that allows you to load an external style sheet from a relative path into the dom/shadow DOM depending on the shadow property. Typically this process is a bit more tricky. Stencil makes it really easy to load any .css files style. They also offer a plugin to load sass ([@stencil/sass](https://www.npmjs.com/package/@stencil/sass)).

Here’s an example usage:

@Component({
  tag: 'my-custom-element',
  styleUrl: 'my-custom-element.css'
})
export class MyCustomElement {}

StyleUrls

This one took some digging to figure out. The information in the Stencil docs says:

Similar as styleUrl but allows to specify different stylesheets for different modes.

That really didn’t give me enough information to understand what the heck was going on. So, after consulting The Oracle (aka Google Search), I found that this is meant for developing components for non-web cross platform use.

Ionic is a platform for developing applications that work on Android, IOS, and the Windows. This property is meant to give a CSS target for each of those build targets.

NOTE: You should still provide a styleUrl property to handle the web. The styleUrls property is meant for non-web build targets.

Here’s an example usage:

@Component({
  tag: 'my-custom-element',
  styleUrls: {
    ios: 'my-custom-element.ios.css', // Obvious :-p
    md: 'my-custom-element.md.css', // Android
    wp: 'my-custom-element.wp.css' // Windows
  }
})
export class MyCustomElement {}

Styles

This is meant to allow inline styling. It cannot support Sass or Less, it must be native CSS. In my opinion this is a property that requires a balancing act. Using it for everything can make component files HUGE and unruly. In general I have found that keeping a project consistent is important. For that reason I generally choose to avoid using this property, and instead use the styleUrl property with a separate file.

However, my opinion is not for everyone! So here’s an example usage:

@Component({
  tag: 'my-custom-element',
  styles: `
  h1 {
    color: green;
  }
  `
})
export class MyCustomElement {
  render() {
    return <h1>Super Green!</h1>;
  }
}

AssetsDirs

When you need to load a local asset in a component it can complicate the build. Stencil tries to keep this a bit more seamless with the use of assetsDirs. This property allows you to specify local folder that contains assets needed for this specific component. The stencil build will make sure these assets are brought along for publishing.

The path can be any relative path to a directory from the component file.

In your component you will use the getAssetPath() function to resolve the run time location of the static asset you’re trying to load.

Here’s an example usage:

@Component({
  tag: 'my-custom-element',
  assetsDirs: [
      '../../assets'
  ]
})
export class MyCustomElement {
  render() {
    return <img src={getAssetPath('../../assets/things.jpg')} />;
  }
}