Light DOM is a Lightning Web Components feature that’s been Generally Available in Lightning Experience, Experience Cloud, LWC OSS (Open Source), and all versions of the Salesforce mobile app since Summer ’23.
Lightning web components, by default, render in shadow DOM, which provides strong encapsulation and security for your components. However, at the same time, it prevents global styling and blocks third-party integrations that introspect the internals of your components. Light DOM is a feature that can be enabled granularly in selected components, so that shadow DOM doesn’t affect them.
How does light DOM work?
Let’s use a very simple Lightning web component as an example.
helloCodey.html
helloCodey.js
In the example above, the component’s default shadow DOM prevents the <p>
element from being reached by a CSS rule defined on the parent component or the host. Moreover, it doesn’t allow JavaScript code that’s external to the component to query the <p>
element using browser query APIs.
To activate light DOM for a component, you need to both specify the light renderMode
in its JavaScript file, and the lwc:render-mode
template directive on the <template>
tag of the component. Both changes are needed due to the way in which Lightning web components compile.
helloCodey.html
helloCodey.js
When you activate light DOM in a component, the component markup gets attached to the host element instead of its shadow tree. You can then access the markup from other components on the page like any other content in the document host that’s not protected by shadow DOM.
Light DOM components allow for the usage of standard browser query APIs like querySelector
and querySelectorAll
. In this case, instead of using this.template.querySelector
, you have to use this.querySelector
.
helloCodey.js
Or more simply, you can often just use the lwc:ref
directive in both cases (shadow and light DOM components) and skip the querySelector
.
helloCodey.html
helloCodey.js
When to use it, and when not to use it
Light DOM is an opt-in for each individual component. Its effects won’t apply to other components unless they also opt in. Note that base components always render in shadow DOM.
We recommend enabling light DOM if you have libraries that need to access component internals using standard browser query APIs, apply global styling, or need more flexibility to implement accessibility best practices, as long as the component does not expose sensitive data. We’ll cover these use cases more in-depth in the next section.
We don’t recommend enabling light DOM for a component if that component surfaces or works with sensitive data. Using light DOM removes shadow DOM encapsulation and exposes components to DOM scraping. So, be aware of this important consideration.
Use cases enabled by light DOM
Light DOM enables several use cases that were not previously supported.
1) Support of libraries that need access to a component’s internals
Light DOM enables the usage of libraries that need access to component internals. A good example of this are analytics libraries used in Experience Cloud sites, such as Google Analytics, as they need access to component internals to obtain better results.
We can test this use case, including the previous helloCodey
component, in a parent mascotChanger
component as follows.
mascotChanger.html
mascotChanger.js
Note that although the queried paragraph belongs to the helloCodey
component, we’re able to access it with this.template.querySelector
, because it belongs to the child light DOM. However, if the helloCodey
component wouldn’t have light DOM enabled, querySelector
would have returned null
.
You can also access light DOM component internals from a script that’s loaded as a static resource on the page, as long as the whole component ancestors are light DOM enabled. For example, in an LWR Experience Cloud site, which is fully light DOM, you could add a JavaScript static resource that finds the helloCodey
component internals as follows.
myJSResource.js
2) Easier implementation of deeply-nested components
Another example in which this can be useful is to implement complex, deeply-nested components. In that case, you may prefer to have a single shadow DOM component at the top level and light DOM components within to avoid overhead. For instance, a custom data table component can just have one big shadow DOM component around the whole thing, rather than a shadow for every table row and table cell.
This implementation makes it easier to query your own elements from the top-level component in your hierarchy, and also to implement accessibility. Additionally, there is a slight perf improvement in some use cases to using light DOM over shadow DOM, which is mostly due to the overhead of just creating the additional shadow nodes.
3) Global styling
Light DOM also facilitates global styling, as it allows CSS styles to cascade into the component markup. For instance, a light DOM component can set a style that is loaded and then applied once for all the light DOM components on the page. Injecting global styles through light DOM is only supported on Experience Cloud sites, CMS content editor, or Sales Enablement.
For instance, let’s define a colorChanger
component as follows.
colorChanger.html
colorChanger.js
colorChanger.css
The blue background color will be applied to the paragraphs of all the helloCodey
component instances on the page as it’s light DOM enabled.
In most cases, you won’t want your style to leak into other components. That’s still possible for light DOM components. You just need to place those style rules into a *.scoped.css
file, so they are scoped to the light DOM component. Scoped CSS is written exactly the same as regular CSS, but it’ll just apply only to that component without leaking out.
Note that if style rules are loaded globally as static resources on a Lightning Experience page or Experience Cloud site, they will be unscoped and applied to both light DOM but also shadow DOM components, as synthetic shadow won’t prevent them from leaking. This is a limitation that will be solved once native shadow is fully supported (currently in Developer Preview). When native shadow is enabled, only light DOM-enabled components will inherit global styles.
4) More flexible implementation of accessibility best practices
Light DOM enables a component to reference the id
of an element that lives in another light DOM-enabled separate component. This allows you to link two elements using id
and aria
attributes, granting you extra flexibility for implementing accessibility best practices in your projects. Let’s improve our mascotChanger
component to demonstrate this.
mascotChanger.html
mascotChanger.js
mascotNameInput.html
mascotNameLabel.html
Please note that Salesforce is currently working with the W3C to add new standards, so that native shadow DOM can participate in these accessibility patterns. This means that, in the future, this light DOM use case won’t be needed. As part of our accessibility efforts, we also sponsored Igalia to partially implement ARIA Element Reflection, which is now fully supported in Safari and partially in Chrome. If you want to know more about this topic, take a look at our cross-root-aria proposal, the repo for the Accessibility Object Model working group.
The following table summarizes the use cases and where they are supported.
Experience Cloud | Lightning Experience | Salesforce Mobile Apps | LWC OSS / LWR on Node.js | |
Support of libraries that need access to component’s internals | Yes | Yes | Yes | Yes |
Easier implementation of deeply-nested components | Yes | Yes | Yes | Yes |
Global styling | Yes | Not recommended* | Not recommended* | Yes |
More flexible implementation of accessibility best practices | Yes | Yes | Yes | Yes |
* While in Experience Cloud you are in control of the whole user interface, in Lightning Experience and Salesforce Mobile apps, it’s discouraged to have a top level Light DOM enabled component with unscoped styles, as they can impact the overall Salesforce’s look & feel.
Other considerations
When working with light DOM, there are some additional considerations to take into account, including:
- Events are not retargeted with light DOM. Read more in the developer guide.
- There’s no browser support for slots outside of shadow DOM, so they are emulated. This implies that some features, such as lifecycle hooks, are not available in them. Take a look at the documentation to know more.
- For now, light DOM-enabled components cannot be packaged.
Conclusion
In this blog post, we’ve reviewed what light DOM is, the use cases that it enables, and the considerations to take into account to decide which components will opt-in the feature. All the examples shown on this blog are in a GitHub repo that you can try out yourself.
To learn more about light DOM on the Salesforce Platform, read the documentation, or if you are working outside of the Platform read the OSS documentation.
If you decide to go ahead and transform your shadow DOM components into light DOM ones, check out this tool created by Salesforce Engineering to simplify the migration.
About the author
Alba Rivas works as a Principal Developer Advocate at Salesforce. You can follow her on Linkedin, Twitter, or GitHub.