LEARNING OBJECTIVES โต
- Structure a production-grade Web Component package configured for NPM distribution.
- Generate and validate the standardized Custom Elements Manifest (
custom-elements.json) using@custom-elements-manifest/analyzer. - Configure
package.jsonwith standardexportsmaps,"customElements", and TypeScripttypesfields. - Enable automated IDE autocomplete (VS Code / WebStorm) and Storybook documentation directly from source code annotations.
๐ The Mental Model & Story (Intuitive Foundation)
Imagine a specialized semiconductor manufacturer manufacturing high-precision microcontrollers.
When they ship thousands of microcontrollers to circuit board designers, they don't just dump a bag of raw black silicon chips on the loading dock. They include an authoritative Standard Datasheet (Pinout Diagram) detailing:
- Exact pin voltages (Properties & Attributes)
- Output signal frequencies (Custom Events)
- Mounting dimensions and socket connectors (Slots & Parts)
+-------------------------------------------------------------------------------+
| THE UNIVERSAL COMPONENT DATASHEET |
+-------------------------------------------------------------------------------+
| SOURCE CODE WITH JSDOC ANNOTATIONS: |
| /** |
| * @tag ui-button |
| * @attr {string} variant - Visual style variant |
| * @fires {CustomEvent} ui-click - Dispatched when button activates |
| * @cssprop --btn-bg - Background color token |
| */ |
| | |
| v |
| [@custom-elements-manifest/analyzer] |
| | |
| v |
| STANDARDIZED `custom-elements.json` MANIFEST |
| | |
| +-----------------------------+-----------------------------+ |
| | | | |
| v v v |
| VS Code IntelliSense Storybook Automated Docs React / Angular Wrappers
+-------------------------------------------------------------------------------+
The Custom Elements Manifest (CEM) is the official open W3C Community Group standard datasheet for Web Components. When you publish a component accompanied by custom-elements.json, IDEs, documentation engines, and framework compilers instantly understand how to provide autocomplete, type checking, and visual playgrounds.
Technical Deep Dive & Specifications
The Custom Elements Manifest (custom-elements.json)
The Custom Elements Manifest is a standardized JSON format describing all custom elements in a package:
{
"schemaVersion": "1.0.0",
"readme": "",
"modules": [
{
"kind": "javascript-module",
"path": "src/components/metric-card.js",
"declarations": [
{
"kind": "class",
"name": "MetricCard",
"tagName": "metric-card",
"description": "Displays an enterprise analytical metric card with trend indicators.",
"attributes": [
{
"name": "value",
"type": { "text": "string" },
"description": "The numeric metric value to display."
},
{
"name": "trend",
"type": { "text": "'up' | 'down' | 'neutral'" },
"default": "'neutral'",
"description": "The directional trajectory of the metric."
}
],
"events": [
{
"name": "metric-refresh",
"type": { "text": "CustomEvent<void>" },
"description": "Fires when user clicks the refresh icon."
}
],
"slots": [
{
"name": "",
"description": "Default slot for sub-metric descriptive text."
}
],
"cssParts": [
{
"name": "container",
"description": "The main card wrapper div."
}
],
"cssProperties": [
{
"name": "--metric-accent",
"description": "Accent border and highlight color.",
"default": "#3b82f6"
}
]
}
]
}
]
}
Modern package.json Distribution Architecture
A modern Web Component library should deliver pure ES Modules (ESM) and register its manifest so the ecosystem can discover it:
{
"name": "@acme/design-system",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"customElements": "custom-elements.json",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./button": {
"types": "./dist/components/button/button.d.ts",
"import": "./dist/components/button/button.js"
},
"./card": {
"types": "./dist/components/card/card.d.ts",
"import": "./dist/components/card/card.js"
},
"./custom-elements.json": "./custom-elements.json"
},
"files": [
"dist",
"custom-elements.json"
],
"scripts": {
"build": "tsc && cem analyze --litelement",
"analyze": "cem analyze --litelement"
},
"peerDependencies": {
"lit": "^3.0.0"
},
"devDependencies": {
"@custom-elements-manifest/analyzer": "^0.9.0",
"typescript": "^5.3.0"
}
}
๐ป Interactive Code Playground
Here is a fully documented, production-grade custom element (<metric-card>) annotated with standard JSDoc tags ready for @custom-elements-manifest/analyzer extraction.
Starter Code
Line-by-Line Code Breakdown
- Lines 44โ58: Standard JSDoc annotations. The CEM analyzer parses
@tag,@attr,@slot,@csspart,@cssprop, and@firesto build the machine-readablecustom-elements.json. - Line 60:
export class MetricCard extends HTMLElement: Standard ES module class export for npm bundling. - Line 72:
refresh-requestedcustom event is documented in the JSDoc header and dispatched with{ bubbles: true, composed: true }. - Line 99:
border-left: 4px solid var(--metric-accent, #3b82f6): Provides token customizability, documented in@cssprop.
Expected Browser Render Output
Two polished analytical metric cards display:
- Monthly Recurring Revenue: Green up arrow
โฒ +14.2%and Blue accent bar. - API Error Rate: Red down arrow
โผ -0.02%with customized Emerald accent bar via--metric-accent: #10b981.
๐๏ธ Hands-On Exercise
๐ฏ The Challenge: Package & Document <color-swatch>
Build a documented <color-swatch> component with complete JSDoc annotations and write the corresponding custom-elements.json JSON block.
Instructions:
- Implement class
ColorSwatchwith attributes:hex(color string) andname(label). - Document
@tag,@attr,@fires color-selected, and@csspart swatch-box. - When clicked, copy the hex code to clipboard (
navigator.clipboard.writeText) and dispatchCustomEvent('color-selected', { detail: { hex, name } }). - Provide the exact JSON declaration node that
@custom-elements-manifest/analyzerwill generate.
๐ Starter Code Sandbox
โ ๏ธ Common Pitfalls
- Bundling Peer Dependencies into the NPM Bundle: If your component uses Lit and you bundle Lit inside
dist/index.js, downstream applications that also use Lit will load two separate copies of the Lit runtime, bloating bundles and breakinginstanceofchecks. Always declare"lit"in"peerDependencies"or"dependencies", never bundle it as an inlined dependency. - Missing
"customElements"inpackage.json: Forgetting to add"customElements": "custom-elements.json"in yourpackage.jsonroot prevents tools like Storybook, VS Code Custom Data, and WebStorm from auto-discovering your component manifest.
๐ก Pro Tips
- Subpath Exports for Granular Tree-Shaking: Configure
exportsinpackage.jsonso consumers can import individual elements without loading the whole suite:import '@acme/ui/button.js'; // Imports only 2 KB button code - VS Code HTML Custom Data Generation: Use
@custom-elements-manifest/to-vscodeto generate avscode-html-custom-data.jsonfile. Adding this to your repository gives all VS Code users rich attribute suggestions and documentation tooltips when authoring HTML.
๐ Key Takeaways
- The Custom Elements Manifest (
custom-elements.json) is the standardized W3C JSON schema describing custom elements, properties, slots, events, and CSS tokens. - Use
@custom-elements-manifest/analyzerto extract documentation automatically from JSDoc and TypeScript source code. - Always declare
"customElements": "custom-elements.json"inpackage.json. - Configure fine-grained subpath exports in
package.jsonto enable optimal consumer tree-shaking. - Declare shared libraries (e.g. Lit) as
peerDependenciesor external modules to avoid duplicate runtime bundling. - --