| 1 | Using a name without a hyphen | Custom element names must contain a hyphen. | customElements.define("app-card", AppCard); |
| 2 | Defining the same element twice | Duplicate definitions throw an error. | if (!customElements.get("app-card")) { customElements.define("app-card", AppCard); } |
| 3 | Forgetting to extend HTMLElement | The browser cannot create a proper custom element class. | class AppCard extends HTMLElement {} |
| 4 | Forgetting super() | The parent element class is not initialized. | constructor() { super(); } |
| 5 | Doing heavy work in constructor | Attributes and children may not be ready yet. | connectedCallback() { this.render(); } |
| 6 | Rendering repeatedly in connectedCallback | The callback can run more than once if the element is moved. | if (this.hasRendered) return; |
| 7 | Not cleaning up listeners | Can cause memory leaks and duplicate behavior. | disconnectedCallback() { this.removeEventListener("click", this.handleClick); } |
| 8 | Using anonymous listener functions | Anonymous functions are difficult to remove later. | this.handleClick = this.handleClick.bind(this); |
| 9 | Not disconnecting observers | Observers can keep running after the component is removed. | this.observer.disconnect(); |
| 10 | Not clearing timers | Timers can keep stale component references alive. | clearInterval(this.timerId); |
| 11 | Watching too many attributes | Unnecessary attribute changes cause extra work. | static get observedAttributes() { return ["open"]; } |
| 12 | Forgetting observedAttributes | attributeChangedCallback() will not run. | static get observedAttributes() { return ["value"]; } |
| 13 | Creating attribute-property loops | Can trigger repeated updates or infinite loops. | if (oldValue === newValue) return; |
| 14 | Using attributes for complex objects | Attributes are strings and are not ideal for objects. | element.data = userObject; |
| 15 | Not reflecting important state | CSS and external users cannot see component state. | this.toggleAttribute("open", isOpen); |
| 16 | Using Shadow DOM for everything | Some simple components do not need encapsulation. | Use light DOM when global styling is required. |
| 17 | Using closed Shadow DOM by default | Makes debugging, testing, and integration harder. | this.attachShadow({ mode: "open" }); |
| 18 | Expecting global CSS to style Shadow DOM | Normal external selectors cannot reach private Shadow DOM. | Use CSS Custom Properties or ::part(). |
| 19 | Hardcoding all Shadow DOM styles | Consumers cannot theme the component. | background: var(--card-bg, white); |
| 20 | Forgetting CSS variable fallbacks | Component styles may break when values are missing. | color: var(--text-color, #111827); |
| 21 | Exposing internal class names as API | Class names are implementation details and may change. | <button part="button"> |
| 22 | Forgetting the part attribute | ::part() cannot style anything without it. | <h2 part="title">Title</h2> |
| 23 | Changing part names often | Breaks consumer styling. | Treat part names as public API. |
| 24 | Using unclear part names | Makes styling hooks confusing. | part="header" |
| 25 | Expecting ::slotted() to style deeply nested children | ::slotted() only targets direct slotted elements. | ::slotted(h2) { color: #0d6efd; } |
| 26 | Not providing slot fallback content | Component may look empty when no content is passed. | <slot>Default content</slot> |
| 27 | Using slots without documenting them | Consumers will not know how to pass content. | Document slot names like title, icon, footer. |
| 28 | Dispatching events without useful names | Events like change can be unclear or conflict. | "value-changed" |
| 29 | Forgetting bubbles: true | Parent components may not receive the event. | bubbles: true |
| 30 | Forgetting composed: true | Events may not cross Shadow DOM boundaries. | composed: true |
| 31 | Putting too much data in event detail | Large or unrelated payloads make APIs hard to maintain. | detail: { id, value } |
| 32 | Not making cancelable events when needed | Consumers cannot stop important actions. | cancelable: true |
| 33 | Ignoring event retargeting | Outside listeners may see the host instead of internal elements. | event.composedPath() |
| 34 | Using unsafe innerHTML | Can create XSS security risks. | element.textContent = userInput; |
| 35 | Trusting frontend validation only | Users can bypass frontend validation. | Validate again on the server. |
| 36 | Using non-semantic markup | Hurts accessibility. | <button type="button">Save</button> |
| 37 | Ignoring keyboard support | Component becomes difficult to use without a mouse. | handle Enter, Space, Escape, and Tab. |
| 38 | Not managing focus | Menus, modals, and dialogs may be inaccessible. | this.shadowRoot.querySelector("button").focus(); |
| 39 | Using ARIA incorrectly | Bad ARIA can make accessibility worse. | Prefer semantic HTML first. |
| 40 | Not testing with screen readers or keyboard | Automated tests do not catch everything. | Test keyboard navigation and focus states. |
| 41 | Re-rendering too much | Can reduce performance and reset focus or state. | Update only changed DOM nodes. |
| 42 | Querying DOM repeatedly | Unnecessary queries add overhead. | this.button = this.shadowRoot.querySelector("button"); |
| 43 | Not batching DOM updates | Can cause layout thrashing. | requestAnimationFrame(() => this.render()); |
| 44 | Adding large dependencies | Reduces portability and increases bundle size. | Prefer browser-native APIs when possible. |
| 45 | Not testing in real browsers | Web Components rely on browser platform behavior. | Use Playwright or browser-based tests. |
| 46 | Not documenting public APIs | Consumers may misuse the component. | Document attributes, properties, events, slots, parts, and CSS variables. |
| 47 | Changing APIs without migration notes | Breaks applications using the component. | Use semantic versioning and migration guides. |
| 48 | Ignoring framework interoperability | Components may be hard to use in React, Angular, or Vue. | Test usage across target frameworks. |
| 49 | Using Declarative Shadow DOM without fallback strategy | Older environments may need progressive enhancement. | Use feature detection and hydration carefully. |
| 50 | Building components without ownership | No one maintains bugs, docs, or upgrades. | Define owners, support process, and release rules. |