Next-Gen App & Browser Testing Cloud
Trusted by 2 Mn+ QAs & Devs to accelerate their release cycles

Learn about Shadow DOM in Selenium, how to find elements using getShadowRoot(), JavaScriptExecuter and automate Shadow DOM with Selenium.

Faisal Khatri
January 13, 2026
When automating web applications with Selenium WebDriver, you may encounter elements that cannot be located using standard methods. This often happens when working with Shadow DOM in Selenium, where elements are encapsulated inside a Shadow Root and cannot be accessed through typical locators.
Because these elements are not part of the regular DOM tree, trying to interact with them directly will usually result in errors like NoSuchElementException. To automate elements inside a Shadow DOM, you need to first retrieve the Shadow Root and then locate the elements within it.
What Is Shadow DOM in Selenium?
Shadow DOM in Selenium refers to web elements that live inside a special, encapsulated part of the DOM tree created by the browser, called the Shadow Root.
These elements are hidden from the regular DOM structure, so standard Selenium locators like XPath or basic CSS selectors cannot access them directly.
How to Find Shadow DOM in Selenium?
Web elements inside Shadow DOM are inaccessible with standard locators. Selenium offers two ways to interact with them: using the getShadowRoot() method or JavaScriptExecutor.
How to Automate Shadow DOM in Selenium?
Automating Shadow DOM in Selenium involves setting up frameworks to handle shadow-root elements, typically using TestNG for test execution and Maven to manage project dependencies and builds.
Shadow DOM is a functionality that allows the web browser to render elements of Document Object Model (DOM) without putting them into the main document DOM tree. This creates a barrier between what the developer and the browser can reach.
The developer cannot access the Shadow DOM the same way they would with nested elements, while the browser can render and modify that code the same way it would with nested elements.

By implementing it, you can keep the style and behavior of one part of the document hidden and separate from the other code of the same document so that there is no interference.
Shadow DOM allows hidden DOM trees to be attached to elements in the regular DOM tree – the Shadow DOM tree starts with a Shadow Root, underneath which you can attach any element in the same way as the normal DOM.
Check out this blog to learn more about Shadow Root in Selenium.
Here are some Shadow DOM terminologies:
Shadow DOM in Selenium lets you access and test elements hidden from the normal DOM. It ensures stable locators and accurate interaction with modern web components.
Let’s take an example of the Watir Homepage and try to assert the Shadow DOM and the nested Shadow DOM text with Selenium WebDriver. It has 1 Shadow Root element before we reach text -> some text, and there are 2 Shadow Root elements before we reach the text > nested text.
HomePage of Watir.com

Now, if we try to locate the element using the cssSelector(“#shadow_content > span”), it doesn’t get located, and Selenium WebDriver will throw NoSuchElementException.

To avoid NoSuchElementException in Selenium and locate the element correctly for the text, we need to go through the Shadow Root elements. Only then would we be able to locate “some text” and “nested text” on the page.
You can use getShadowRoot() and JavaScriptExecutor to access Shadow DOM. It returns a ShadowRoot for direct element access, handles nested shadow trees, and throws NoSuchShadowRootException if none exists.
Selenium 4 introduced getShadowRoot() to access Shadow DOM elements directly from a Shadow Host. It returns a ShadowRoot object, which lets you interact with the component’s Shadow DOM. If no Shadow Root exists, it throws NoSuchShadowRootException.
As discussed earlier, this project on Shadow DOM in Selenium has been created using Maven. TestNG framework is used as a test runner. To learn more about Maven, you can go through this Maven tutorial for Selenium testing.
Once the project is created, we need to add the dependency for Selenium WebDriver and TestNG in the pom.xml file.
Versions of the dependencies are set in a separate properties block. This is done for maintainability, so if we need to update the versions, we can do it easily without searching the dependency throughout the pom.xml file.
Let’s move on to the test script of HomePage class. The Page Object Model (POM) has been used in this project as it helps reduce code duplication and improve test case maintenance.
public class HomePage {
public SearchContext expandRootElement (WebElement element) {
SearchContext shadowRoot = (SearchContext) ((JavascriptExecutor) getDriver ()).executeScript (
"return arguments[0].shadowRoot", element);
return shadowRoot;
}
public String getSomeText () {
return getDriver ().findElement (By.cssSelector ("#shadow_content > span"))
.getText ();
}
public String getShadowDomText () {
WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
SearchContext shadowRoot = shadowHost.getShadowRoot ();
String text = shadowRoot.findElement (By.cssSelector ("#shadow_content > span"))
.getText ();
return text;
}
public String getNestedShadowText () {
WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
SearchContext shadowRoot = shadowHost.getShadowRoot ();
WebElement shadowContent = shadowRoot.findElement (By.cssSelector ("#nested_shadow_host"));
SearchContext shadowRootTwo = shadowContent.getShadowRoot ();
String nestedText = shadowRootTwo.findElement (By.cssSelector ("#nested_shadow_content > div")).getText ();
return nestedText;
}
public String getNestedText() {
WebElement nestedText = getDriver ().findElement (By.id ("shadow_host")).getShadowRoot ()
.findElement (By.cssSelector ("#nested_shadow_host")).getShadowRoot ()
.findElement (By.cssSelector ("#nested_shadow_content > div"));
return nestedText.getText ();
}
public String getNestedTextUsingJSExecutor () {
WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
SearchContext shadowRootOne = expandRootElement (shadowHost);
WebElement nestedShadowHost = shadowRootOne.findElement (By.cssSelector ("#nested_shadow_host"));
SearchContext shadowRootTwo = expandRootElement (nestedShadowHost);
return shadowRootTwo.findElement (By.cssSelector ("#nested_shadow_content > div"))
.getText ();
}
}

First, we would find the locator for “some text” and “nested text” on HomePage.
public class HomePage {
public SearchContext expandRootElement (WebElement element) {
SearchContext shadowRoot = (SearchContext) ((JavascriptExecutor) getDriver ()).executeScript (
"return arguments[0].shadowRoot", element);
return shadowRoot;
}
public String getSomeText () {
return getDriver ().findElement (By.cssSelector ("#shadow_content > span"))
.getText ();
}
public String getShadowDomText () {
WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
SearchContext shadowRoot = shadowHost.getShadowRoot ();
String text = shadowRoot.findElement (By.cssSelector ("#shadow_content > span"))
.getText ();
return text;
}
public String getNestedShadowText () {
WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
SearchContext shadowRoot = shadowHost.getShadowRoot ();
WebElement shadowContent = shadowRoot.findElement (By.cssSelector ("#nested_shadow_host"));
SearchContext shadowRootTwo = shadowContent.getShadowRoot ();
String nestedText = shadowRootTwo.findElement (By.cssSelector ("#nested_shadow_content > div")).getText ();
return nestedText;
}
public String getNestedText() {
WebElement nestedText = getDriver ().findElement (By.id ("shadow_host")).getShadowRoot ()
.findElement (By.cssSelector ("#nested_shadow_host")).getShadowRoot ()
.findElement (By.cssSelector ("#nested_shadow_content > div"));
return nestedText.getText ();
}
public String getNestedTextUsingJSExecutor () {
WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
SearchContext shadowRootOne = expandRootElement (shadowHost);
WebElement nestedShadowHost = shadowRootOne.findElement (By.cssSelector ("#nested_shadow_host"));
SearchContext shadowRootTwo = expandRootElement (nestedShadowHost);
return shadowRootTwo.findElement (By.cssSelector ("#nested_shadow_content > div"))
.getText ();
}
}
Code Walkthrough:
Note: Automate Shadow DOM with cloud Selenium Grid. Try TestMu AI Today!
To access Shadow DOM elements using JavaScriptExecutor, execute a script that traverses the shadow roots. First, locate the Shadow Host element using standard selectors, then use element.shadowRoot to access the shadow DOM.
Let’s now see how to locate the Shadow Root elements using JavaScriptExecutor in Selenium WebDriver.

getNestedTextUsingJSExecutor() method has been created inside the HomePage Class, where we would be expanding the Shadow Root element based on the WebElement, we pass in the parameter.
Since in the DOM (as shown in the screenshot above), we saw that there are two Shadow Root elements we need to expand before we get to the actual locator for getting the text – nested text. Hence, the expandRootElement() method is created instead of copy-pasting the same JavaScriptExecutor code every time.
We would implement the SearchContext interface to help us with the JavaScriptExecutor and return the Shadow Root element based on the WebElement we pass in the parameter.
public SearchContext expandRootElement (final WebElement element) {
return (SearchContext) ((JavascriptExecutor) getDriver ()).executeScript ("return arguments[0].shadowRoot",
element);
}
public String getNestedShadowText () {
final WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
final SearchContext shadowRoot = shadowHost.getShadowRoot ();
final WebElement shadowContent = shadowRoot.findElement (By.cssSelector ("#nested_shadow_host"));
final SearchContext shadowRootTwo = shadowContent.getShadowRoot ();
return shadowRootTwo.findElement (By.cssSelector ("#nested_shadow_content > div"))
.getText ();
}
Code Walkthrough:
There are two main ways to run tests for automating Shadow DOM in Selenium. You can execute them directly from your IDE using TestNG for quick development and debugging, or run them from the command line using Maven, which is useful for automated builds and continuous integration setups.
You can use TestNG to run Selenium scripts that access Shadow Root through JavaScriptExecutor and locate inner elements. Execute the test from IDE or suite XML to validate Shadow DOM actions.
TestNG is used as a test runner. Hence, testng.xml has been created, using which we will run the tests by right-clicking on the file and selecting the option Run ‘…\testng.xml’. This file will be placed inside the root folder of the project.
To achieve scalability and reliability and test across different environments, we’ll run the tests on automation testing platforms such as TestMu AI.
TestMu AI offers an online Selenium Grid of over 3000 real browsers and operating systems to help you automate Shadow DOM in Selenium on the cloud. You can accelerate your Selenium testing with TestNG and reduce test execution time by multiple folds by running parallel tests on multiple browsers and OS configurations.
To get started, check out this guide on Selenium TestNG testing on TestMu AI.
But before running the tests, add the TestMu AI Username and Access Key. You can them from your Account Settings > Password & Security. Now add these credentials in the Run Configurations since we are reading the TestMu AI Username and Access Key from System Property.
Add values in the Run Configuration as mentioned below:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Shadow DOM Automation Tests">
<test name="Shadow DOM Tests on Watir Website">
<parameter name="browser" value="remote-chrome"/>
<classes>
<class name="ShadowDomTests">
<methods>
<include name="testShadowDomWatir"/>
</methods>
</class>
</classes>
</test> <!-- Test -->
<test name="Shadow DOM Tests on Selenium Playground Website">
<parameter name="browser" value="remote-chrome"/>
<classes>
<class name="ShadowDomTests">
<methods>
<include name="testShadowDomSeleniumPlayground"/>
</methods>
</class>
</classes>
</test> <!-- Test -->
</suite>
Here is the screenshot of the test run locally for Shadow DOM in Selenium using Intellij IDE.

To automate Shadow DOM using Maven, configure pom.xml with Selenium and TestNG, then run mvn test so the build executes scripts that access shadowRoot via JavaScriptExecutor for stable UI validation.
To run the tests using Maven, the following steps need to be run to automate Shadow DOM in Selenium:
Following is the screenshot from IntelliJ, which shows the execution status of the tests using Maven:

Once the tests are run successfully, we can go to the TestMu AI Web Automation Dashboard and view all the video recordings, screenshots, device logs, and step-by-step granular test run details.

In this blog on automating Shadow DOM in Selenium, we discussed finding Shadow DOM elements and automating them using the getShadowRoot() method introduced in the Selenium 4 and above version.
We also discussed locating and automating the Shadow DOM in Selenium WebDriver using JavaScriptExecutor and running the tests on the TestMu AI platform, which shows granular details of the tests run with Selenium WebDriver logs.
Finding web elements: https://www.selenium.dev/documentation/webdriver/elements/finders/
Did you find this page helpful?
More Related Hubs
TestMu AI forEnterprise
Get access to solutions built on Enterprise
grade security, privacy, & compliance