Distributing and Browser Support
Publishing to npm
Section titled “Publishing to npm”A web component is just a JavaScript module. Publish it the same way you would any ES module package. A minimal package.json:
{ "name": "my-counter", "version": "1.0.0", "type": "module", "main": "./dist/my-counter.js", "module": "./dist/my-counter.js", "exports": { ".": "./dist/my-counter.js" }, "files": ["dist"], "keywords": ["web-components", "custom-element"]}Consumers install it with npm install my-counter and import it once (a side-effect import is enough to register the tag):
import 'my-counter';// <my-counter> is now usable anywhere in the documentThe Custom Elements Manifest
Section titled “The Custom Elements Manifest”The Custom Elements Manifest (custom-elements.json) is a machine-readable JSON file that documents every custom element in a package — its tag name, properties, attributes, events, and CSS custom properties. Tools like Storybook, IDEs, and design-system dashboards consume it for auto-complete and documentation.
Generate one with the @custom-elements-manifest/analyzer CLI:
npm install --save-dev @custom-elements-manifest/analyzernpx cem analyze --globs "src/**/*.js"A fragment of what it produces:
{ "schemaVersion": "1.0.0", "modules": [{ "kind": "javascript-module", "path": "src/my-counter.js", "declarations": [{ "kind": "class", "name": "MyCounter", "customElement": true, "tagName": "my-counter", "attributes": [ { "name": "label", "type": { "text": "string" } } ], "members": [ { "kind": "field", "name": "count", "type": { "text": "number" } } ] }] }]}Versioning
Section titled “Versioning”Follow semantic versioning. Breaking changes in a custom element are:
- Removing or renaming an attribute, property, or event.
- Changing the element’s tag name.
- Changing the shape of a custom event’s
detailobject.
Non-breaking changes include adding new optional attributes, new CSS custom properties, or new public methods.
Browser support and baseline
Section titled “Browser support and baseline”As of 2025, Custom Elements v1, Shadow DOM v1, and HTML Templates are supported in every major browser — Chrome, Firefox, Safari, and Edge (all current and most previous versions). The “Baseline Widely Available” badge applies to all three pillars.
The one exception: customized built-ins (elements that extend a built-in like HTMLButtonElement with is="...") are not supported in Safari. Prefer autonomous custom elements (extends HTMLElement) for maximum portability.
Lit itself supports Chrome 104+, Firefox 121+, Safari 16.4+. For production apps targeting older browsers, the @webcomponents/polyfills package fills gaps.
Runnable demo
Section titled “Runnable demo”The demo below shows a simple distributable badge element that illustrates the “published package feel” — exactly what a consumer would see after installing your component from npm.