Painless Web Components: Naming is (not too) Hard
Tags: javascript, webdev, webcomponents, tutorial
Published: 2023-02-20 17:51:25

This post is the first in a series focused on reducing the perceived pain of creating, using, and maintaining web components. The main focus will be web standards and "vanilla" methods, but we will look at how comparable libraries or frameworks are doing things too.

## Two Types of Custom Elements

Naming isn't really the first decision in the process of creating web components. The first task is to choose which of the two types of custom elements you are making. The options are: 
* **Autonomous custom element**<sup>[1](#one)</sup> - the standalone types of elements (e.g. - `<my-cool-element>`, `<useful-tool>`, `<fly-away>`)
* **Customized built-in element**<sup>[2](#two)</sup> - the elements that inherit and extend the semantic functionality of existing elements (e.g. - `<button is="bouncing-button">`, `<strong is="bold-and-beautiful">`).

The difference lies in whether or not you are extending an existing element of HTML in your constructor (e.g. - `HTMLButtonElement`,`HTMLInputElement`,). In my experience, when people are talking or thinking about web components they're usually talking about the former, but the naming conventions for either one are the same.

## Two Name Considerations

When building a web component there are at least two items to name: the custom element (tag name), and the constructor (usually a JavaScript `class`). Both deserve appropriately thoughtful names as they are the required parameters to register a custom element with the browser using the `customElements.define()` function:
```
customElements.define('your-tag-name', YourConstructor);
```
OR 
```
customElements.define('your-tag-name', YourConstructor, { extends: "input" });
```
in the case of a customized built-in (in this case extending an `HTMLInputElement`)

The **constructor** is probably the easiest of the two to name, because you will utilize the same conventions you have for any other class names (you **are** defining a standard naming convention, right?). In most cases, I will make an attempt to match the constructor's name to the tag name, but it isn't required other than for lowering cognitive load. I am also a little more liberal on the use of upper-case abbreviations, but not for any justifiable reason other than I like how it looks next to `HTMLElement`. Some examples:
* CPXCookie <=> cpx-cookie
* DPSearch <=> dp-search
* MyMatchMeetBookOverflowPlusSong <=> mmmbop-song<sup>[0](#zero)</sup>

There is a nice "Note" on the Custom Elements living standard<sup>[3](#three)</sup> that lays out the standard for valid **custom element names**. Most of these are to accommodate existing HTML parser rules. In my simplified words:
* Starts with a lower-case letter
* No upper-case letters allowed
* One or more hyphens
* Not one of a small list of existing tag names that include a hyphen that it is unlikely you'd use anyway (unless you're dying to make an `annotation-xml` or `missing-glyph` component, in which case you're going to be really let down)

Interestingly, the wide constraints on this means you can have some fun with the tag names. Emojis, latin, and any valid characters in the list of potential custom element name characters<sup>[4](#four)</sup> are valid (even if I wouldn't recommended doing so because it makes actually typing the element name a little harder).

## Namespacing and Organization

Element names can also be used to namespace or organize your components. Namespacing with a prefix can help denote ownership or library membership. A few examples: 
* `cpx-` (my Chapeaux components, which we'll look at more as the series progresses)
* `pf-` ([Patternfly Elements](https://patternflyelements.com/) from Red Hat<sup>[5](#five)</sup>)
* `sp-` ([Spectrum components](https://opensource.adobe.com/spectrum-web-components/) from Adobe<sup>[6](#six)</sup>)
* `bp-` ([Carbon components](https://web-components.carbondesignsystem.com/?path=/story/introduction-welcome--page) from IBM<sup>[7](#seven)</sup>)
* `fast-` ([Fast components](https://www.fast.design/) from Microsoft<sup>[8](#eight)</sup>)
* `lion-` ([Lion components](https://lion-web.netlify.app/) from ING<sup>[9](#nine)</sup>)

After the namespace prefix, you can also leverage the hyphenated nature of the names to do natural grouping for related components. Variants or parent-child components can be grouped with a consistent name to denote their relationships.

## Referencing the custom element name

How the tag name gets into your code can vary based on the method you are using to write your components. If you load up a few of the templates over on [WebComponents.dev](https://webcomponents.dev/) you'll see that many examples just use a string value typed into the define function directly.
```
customElements.define('my-tag-name', MyTagName);
```

 In some templates, like Lit and Stencil for example, they leverage decorators that handle passing that string value along. 

```
// Lit
@customElement('my-tag-name')
// Stencil
@Component({ tag: 'my-tag-name' })
```

In my vanilla components, I like having this little blurb near the top of the defining class to set a class-level value:

```
static get tag() { return 'my-tag-name'; }
```

When I register the element it looks something like this:

```
customElements.define(MyTagName.tag, MyTagName);
```

## Registering your element

Once you have your two names, you are able add your element to the `CustomElementRegistry` by using `customElements.define()`. Putting it all together, it might look something like this:

```
class MyTagName extends HTMLElement {
  static get tag() { return 'my-tag-name'; }
}

customElements.define(MyTagName.tag, MyTagName);
```

```
<script src="mytagname.js"></script>
<my-tag-name></my-tag-name>
```

Your custom element is now registered, but doesn't really do much of anything at all except exist. Next up we will look at the various features that are made available to developers beyond component registration.

---

<a name="zero">[0]</a> - An out-dated web component overview repo used for a talk - Luke Dary - https://github.com/KamiQuasi/wc-overview/blob/master/index.html ([YouTube recording of talk](https://www.youtube.com/watch?v=5b5O5cclPbk))

<a name="one">[1]</a> - "4.13.1.1 Creating an autonomous custom element" - HTML Living Standard - https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-autonomous-example

<a name="two">[2]</a> - "4.13.1.4 Creating a customized built-in element" - HTML Living Standard - https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-customized-builtin-example

<a name="three">[3]</a> - "4.13 Custom elements" - HTML Living Standard - https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements

<a name="four">[4]</a> - "4.13.3 Core concepts" - HTML Living Standard - https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name

<a name="five">[5]</a> - "PatternFly Elements" - https://patternflyelements.com/

<a name="six">[6]</a> - "Spectrum Web Components" - https://opensource.adobe.com/spectrum-web-components/

<a name="seven">[7]</a> - "carbon-web-components" - Introduction / Welcome - Page ⋅ Storybook - https://web-components.carbondesignsystem.com/?path=/story/introduction-welcome--page

<a name="eight">[8]</a> - "Adaptive UI System, Utilities, and Tools" - https://www.fast.design/

<a name="nine">[9]</a> - "Fundamental white label web components for building your design system" - https://lion-web.netlify.app/