Articles
Web Components Interview Questions: Custom Elements, Shadow DOM, and Events
Prepare for Web Components interviews with event traces, lifecycle failures, Shadow DOM boundaries, form integration, and practical accessibility checks.
Web Components interview questions become much easier when you reason about boundaries: when an element acquires behavior, which state belongs to its public API, and what can cross its shadow root. Knowing the names of lifecycle callbacks is only the starting point.
The direct answer is to explain custom elements as browser-defined component interfaces, Shadow DOM as an encapsulation mechanism, and events as an explicit communication contract. Then demonstrate what happens during reconnection, attribute changes, form submission, and keyboard interaction. A component that looks correct but loses a value on submit is unfinished.
Platform details follow the HTML standard and linked browser specifications; verify support against your application's actual browser targets.
1. What problem do custom elements solve?
A custom element gives a reusable interface a browser-recognized tag, properties, attributes, methods, and lifecycle behavior. An autonomous element extends HTMLElement; its registered name contains a hyphen, such as quantity-picker. Shadow DOM is optional. You can build a custom element with ordinary children and still have a useful component.
Consider a checkout shared by two applications. The valuable contract is that a quantity picker accepts a number and announces a committed change. Neither application should need to know whether its implementation uses two buttons, a select control, or a framework internally.
In an interview, define the consumer-facing behavior before discussing implementation. Ask whether the element is interactive, participates in a form, needs server-rendered content, and must support an existing framework. Those requirements determine the complexity. Wrapping a native button solely to create a new tag may add obligations without solving a meaningful integration problem.
2. How should construction and connection differ?
The HTML standard distinguishes construction from connection to a document. Keep the constructor focused on internal setup, call super(), and avoid relying on attributes or children being available there. Connection work must tolerate the same element being removed and inserted again.
Imagine a shopping panel that subscribes to a document-level currency event every time connectedCallback() runs. After three detach-and-reattach cycles, one currency change might invoke three handlers if cleanup and handler identity are wrong. The symptom looks like a pricing bug, but its cause is ownership of a subscription.
A useful design pairs document subscriptions with removal during disconnection, and makes rendering repeatable. Internal listeners attached once to persistent internal nodes have a different lifetime. Do not indiscriminately recreate the shadow tree on every connection: that can discard focus, selection, and state.
Describe a test that connects, disconnects, and reconnects the same instance, then dispatches one event. Expect one response. This is more convincing than merely listing callback names in order.
3. Which values belong in attributes and properties?
Attributes are serialized markup values; properties can hold richer JavaScript values. A quantity can reasonably have a string attribute representation and a numeric property API. An array of product objects generally belongs in a property rather than a large JSON attribute.
Define invalid inputs explicitly. Suppose the minimum is one and the maximum is nine. Should quantity="banana" fall back to one, become invalid, or throw through a property setter? Different choices can be defensible, but consumers need a stable contract.
Boolean attributes introduce another trap: presence represents true under ordinary boolean-attribute semantics. The text disabled="false" still represents a present attribute. A custom element must implement its chosen behavior; naming an attribute disabled does not automatically provide native control semantics.
When reflecting state between a property and an attribute, avoid circular updates. Compare normalized values before writing and separate normalization from rendering. In a review exercise, trace a property assignment through reflection and the attribute callback, and verify that only one meaningful state change occurs.
4. What does Shadow DOM actually isolate?
Shadow DOM supplies a separate tree with styling and traversal boundaries. It is useful for keeping a page's broad selectors from accidentally changing component internals. It is not a security boundary, a replacement for input validation, or a promise that inherited styling disappears.
For a product badge, expose a small theming contract: perhaps a color custom property and a named part for a deliberately customizable label. CSS custom properties can participate in inheritance, while ::part exposes selected internal elements for styling. Document that interface instead of inviting consumers to depend on private markup.
An open root is available through the host's shadowRoot; a closed root is not exposed that way. Choosing closed mode does not make secret data safe inside the component. Ask what the mode improves for the consumer and for debugging before presenting it as a blanket best practice.
The practical interview question is often whether encapsulation helps this component enough to justify its integration costs.
5. How do bubbles and composed change an event trace?
For an event dispatched from an internal button, bubbles controls bubbling and composed controls crossing a shadow boundary. A normal bubble-phase listener on the document needs both for this scenario. Synthetic custom events do not enable either option by default.
| Internal event options | Document bubble listener | Reason |
|---|---|---|
| Neither enabled | No | No bubbling or boundary crossing |
| bubbles: true only | No | The shadow boundary stops outward propagation |
| composed: true only | No | No ordinary bubbling to the document |
| Both enabled | Yes | The event can cross and bubble outward |
Do not turn that table into a claim that composed, non-bubbling events are invisible outside. Capture listeners and host handling require separate reasoning. Also state the dispatch location: dispatching directly on the host is different from dispatching inside its shadow tree.
6. What should a public change event contain?
Here is a deliberately small event contract for an internal quantity button. Assume button is inside the shadow root and the component has already validated the next value.
button.dispatchEvent(new CustomEvent('quantity-commit', {
bubbles: true,
composed: true,
detail: { value: 3 }
}));
An outside listener should use the documented detail.value, not reach into the component to read a private input. Outside a shadow root, retargeting can make event.target identify the host rather than the original internal button. composedPath() helps inspect the propagation path, subject to closed-root visibility.
Decide whether the event reports a completed change or requests approval. If consumers may cancel an action, define a cancelable event and honor its result before committing. Calling preventDefault() on an informational, noncancelable event cannot undo state that the component has already saved.
Finally, avoid exposing mutable internal objects unnecessarily. A narrow payload reduces coupling and makes future internal changes easier to ship.
7. How do slots affect ownership?
A slot lets consumers supply light-DOM content for a location in the component's rendered composition. The content still belongs to the light DOM; visual placement does not transfer ownership into the shadow tree.
Suppose a notification component has a named action slot. The consumer supplies a retry button. Before attaching handlers, decide whether the component owns retry behavior or merely displays the supplied action. If both sides attach independent retry requests, a single click could submit twice.
Use slotchange when the assignment itself matters, but do not assume it reports every mutation inside an assigned descendant. If the component needs the changing text of a nested element, that is a separate observation requirement.
For accessibility, a slot should serve a clear content contract: heading, explanatory text, or action. Requiring consumers to reverse-engineer a particular child structure makes composition fragile. Test empty slots and fallback content as deliberately as the populated case.
8. How can a custom element participate in a form?
A styled input inside a shadow root does not automatically make its host a complete form control. The HTML standard defines form-associated custom elements and ElementInternals for participation, including setting a submitted value and reporting validity.
Consider an original booking example: seat-count has name seats, displays three, and belongs to a form. The expected submitted entry is seats=3. If the display changes to four but the submitted value remains three, visual rendering and form state have diverged.
Your design must cover setting the form value, invalid states, disabled behavior, reset behavior, and restoration where relevant. For a required count, explain how an empty or invalid value affects submission rather than merely coloring the border red.
A focused test changes the value, inspects FormData, resets the form, and inspects both display and submitted state again. Treat the browser as part of the component's behavior contract.
9. What makes the component accessible?
Start with native interactive controls whenever they meet the need. They supply substantial keyboard and semantic behavior that a clickable generic container lacks. A custom tag alone does not tell assistive technology what the user can do with it.
For the quantity picker, test an accessible name, keyboard operation, visible focus, disabled state, and understandable validation feedback. Check whether focus moves unexpectedly when rendering replaces nodes. Test how a screen reader announces the current value and whether repeated announcements become disruptive.
Do not assume labels or ID references work across tree boundaries exactly as they do within one document tree. Choose and verify the accessible naming mechanism for the browser and assistive-technology combinations you support. Form association and accessibility solve related but different problems.
A strong answer includes a concrete user journey: Tab to the control, increase once, hear or inspect the new value, submit, then recover from an invalid entry without losing context.
10. How would you debug a boundary failure?
Suppose the component displays three, an application listener never fires, and checkout submits one. These are three observations, not necessarily one bug. Begin by identifying the first violated contract.
| Observation | Targeted check | Useful evidence |
|---|---|---|
| No outside notification | Dispatch node and event options | Listener trace |
| Wrong submitted quantity | Form value synchronization | FormData entry |
| Multiple requests | Subscription lifecycle | Handler invocation count |
| Focus disappears | Node replacement during rendering | Active element before and after |
Reproduce with one component in a minimal page. Then vary one boundary at a time: internal versus host dispatch, ordinary versus shadow-contained input, one connection versus repeated connections. A framework wrapper may introduce another issue, but isolate platform behavior before blaming it.
Write the expected observation before changing code. Otherwise a modification that makes the screen look right can silently preserve the form bug.
11. How should you rehearse the implementation answer?
Build one quantity picker small enough to explain completely. Give it a documented numeric property, a deliberate attribute policy, a public event, and a form-submission story. Add only the behavior you can defend.
Then ask a partner to change the conditions. Remove and reinsert it. Supply invalid markup. Put two instances in the same form. Move focus through it using only a keyboard. Listen on a parent outside the shadow root. Each variation tests whether your contract survives a realistic integration.
When answering aloud, lead with the observable result, trace the platform mechanism, and finish with the test that would disprove your explanation. For example: "The document bubble listener misses this event because it is not composed; I would verify the dispatch node and options before changing the consumer."
That style demonstrates ownership of browser behavior. It also gives an interviewer a precise place to ask a deeper question, instead of forcing them to navigate a list of memorized APIs.
Sources and Further Reading
Research checked September 24, 2026. Examples and debugging exercises are original; browser compatibility should be checked for the intended deployment.