Lit components use shadow DOM to encapsulate their DOM. Shadow DOM provides a way to add a separate isolated and encapsulated DOM tree to an element. DOM encapsulation is the key to unlocking interoperability with any other code—including other web components or Lit components—functioning on the page.
Shadow DOM provides three benefits:
DOM scoping. DOM APIs like document.querySelector won't find elements in the component's shadow DOM, so it's harder for global scripts to accidentally break your component.
Style scoping. You can write encapsulated styles for your shadow DOM that don't affect the rest of the DOM tree.
Composition. The component's shadow root, which contains its internal DOM, is separate from the component's children. You can choose how children are rendered in your component's internal DOM.
Older browsers. On older browsers where native shadow DOM isn't available, the web components polyfills may be used. Please note that Lit's polyfill-support module must be loaded along with the web components polyfills. See Requirements for legacy browsers for details.
Lit renders components to its renderRoot, which is a shadow root by default. To find internal elements, you can use DOM query APIs, such as this.renderRoot.querySelector().
The renderRoot should always be either a shadow root or an element, which share APIs like .querySelectorAll() and .children.
You can query internal DOM after component initial render (for example, in firstUpdated), or use a getter pattern:
The @query, @queryAll, and @queryAsync decorators all provide a convenient way to access nodes in the internal component DOM.
Using decorators. Decorators are a proposed JavaScript feature, so you’ll need to use a compiler like Babel or TypeScript to use decorators. See Using decorators for details.
Modifies a class property, turning it into a getter that returns a node from the render root. The optional second argument when true performs the DOM query only once and caches the result. This can be used as a performance optimization in cases when the node being queried will not change.
Here, _divs would return both <div> elements in the template. For TypeScript, the typing of a @queryAll property is NodeListOf<HTMLElement>. If you know exactly what kind of nodes you'll retrieve, the typing can be more specific:
@queryAll('button')
_buttons!: NodeListOf<HTMLButtonElement>
The exclamation point (!) after buttons is TypeScript's non-null assertion operator. It tells the compiler to treat buttons as always being defined, never null or undefined.
Similar to @query, except that instead of returning a node directly, it returns a Promise that resolves to that node after any pending element render is completed. Code can use this instead of waiting for the updateComplete promise.
This is useful, for example, if the node returned by @queryAsync can change as a result of another property change.
To render an element's children, create a <slot> for them in the element's template. The children aren't moved in the DOM tree, but they're rendered as if they were children of the <slot>. For example:
You can specify fallback content for a slot. The fallback content is shown when no child is assigned to the slot.
<slot>I am fallback content</slot>
Rendering fallback content. If any child nodes are assigned to a slot, its fallback content doesn't render. A default slot with no name accepts any child nodes. It won't render fallback content even if the only assigned nodes are text nodes containing whitespace, for example <example-element> </example-element>. When using a Lit expression as a child of a custom element, make sure to use a non-rendering value when appropriate so that any slot fallback content is rendered. See removing child content for more information.
To access children assigned to slots in your shadow root, you can use the standard slot.assignedNodes or slot.assignedElements methods with the slotchange event.
For example, you can create a getter to access assigned elements for a particular slot:
get_slottedChildren() {
constslot=this.shadowRoot.querySelector('slot');
returnslot.assignedElements({flatten: true});
}
You can also use the slotchange event to take action when the assigned nodes change. The following example extracts the text content of all of the slotted children.
@queryAssignedElements and @queryAssignedNodes convert a class property into a getter that returns the result of calling slot.assignedElements or slot.assignedNodes respectively on a given slot in the component's shadow tree. Use these to query the elements or nodes assigned to a given slot.
Both accept an optional object with the following properties:
Property
Description
flatten
Boolean specifying whether to flatten the assigned nodes by replacing any child <slot> elements with their assigned nodes.
slot
Slot name specifying the slot to query. Leave undefined to select the default slot.
selector (queryAssignedElements only)
If specified, only return assigned elements that match this CSS selector.
Deciding which decorator to use depends on whether you want to query for text nodes assigned to the slot, or only element nodes. This decision is specific to your use case.
Using decorators. Decorators are a proposed JavaScript feature, so you’ll need to use a compiler like Babel or TypeScript to use decorators. See Using decorators for details.
The simplest way to customize the render root is to set the shadowRootOptions static property. The default implementation of createRenderRoot passes shadowRootOptions as the options argument to attachShadow when creating the component's shadow root. It can be set to customize any options allowed in the ShadowRootInit dictionary, for example mode and delegatesFocus.
The default implementation of createRenderRoot creates an open shadow root and adds to it any styles set in the static styles class field. For more information on styling see Styles.
To customize a component's render root, implement createRenderRoot and return the node you want the template to render into.
For example, to render the template into the main DOM tree as your element's children, implement createRenderRoot and return this.
Rendering into children. Rendering into children and not shadow DOM is generally not recommended. Your element will not have access to DOM or style scoping, and it will not be able to compose elements into its internal DOM.