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 element1 - the standalone types of elements (e.g. -
<my-cool-element>
,<useful-tool>
,<fly-away>
) -
Customized built-in element2 - 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-song0
There is a nice "Note" on the Custom Elements living standard3 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
ormissing-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 characters4 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 from Red Hat5) -
sp-
(Spectrum components from Adobe6) -
bp-
(Carbon components from IBM7) -
fast-
(Fast components from Microsoft8) -
lion-
(Lion components from ING9)
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 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.
[0] - 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)
[1] - "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
[2] - "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
[3] - "4.13 Custom elements" - HTML Living Standard - https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements
[4] - "4.13.3 Core concepts" - HTML Living Standard - https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
[5] - "PatternFly Elements" - https://patternflyelements.com/
[6] - "Spectrum Web Components" - https://opensource.adobe.com/spectrum-web-components/
[7] - "carbon-web-components" - Introduction / Welcome - Page ⋅ Storybook - https://web-components.carbondesignsystem.com/?path=/story/introduction-welcome--page
[8] - "Adaptive UI System, Utilities, and Tools" - https://www.fast.design/
[9] - "Fundamental white label web components for building your design system" - https://lion-web.netlify.app/
Top comments (1)
Love (and also do) your approach to
static get tag() { return 'my-tag-name'; }
so that it has a suggested tag name shipping w/ it (which can still be overloaded).Looking forward to where this goes as a series :)
naming as a large critique has some work coming to overcome name conflicting too:
Another approach to name scoping that OpenWC also tends to write your class name into a different file from your
customElement.define
call so that you could alter the name based on other implementations if needed.