Manuals / Playwright with Python / Ch 29

Part 4 · Advanced TechniquesAdvanced40 min read

21. Shadow DOM & Complex Components

Playwright with Python · 244 pages source format

Piercing shadow DOM Playwright's locators automatically pierce open shadow DOM by default — no special syntax needed in most cases: # Works transparently even if "custom-button" uses shadow DOM internally page.locator("custom-button").get_by_text("Submit").click() Pointers: This "just works" behavior is a genuine advantage over older tools, which often required manually accessing .shadowRoot via J

What you'll learn

  • Piercing shadow DOM
  • Handling custom web components

Piercing shadow DOM

Playwright's locators automatically pierce open shadow DOM by default — no special syntax needed in most cases:

Pointers: This "just works" behavior is a genuine advantage over older tools, which often required manually accessing .shadowRoot via JavaScript execution to reach shadow DOM content at all.

Run / study this snippet
# Works transparently even if "custom-button" uses shadow DOM internally
page.locator("custom-button").get_by_text("Submit").click()
Interactive study board
Piercing shadow DOMDrag stickies · tap for tips
Study mapDrag stickies · tap for tipsKeep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Assert the UIdrag · tap →Retry wiselydrag · tap →Seed datadrag · tap →Close the loopdrag · tap →Keep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Pathwise hackdrag · tap →Page under testdrag · tap →Multi-browserdrag · tap →Automation pathdrag · tap →Tooling nodedrag · tap →
Clear?

Handling custom web components

For components built with frameworks like Lit or native Web Components (common in design systems), the same role/text-based locators from Chapter 5 generally still apply, since they operate on the accessibility tree rather than raw DOM structure:

custom <my-button> element

Pointers: Closed shadow roots (a stricter encapsulation mode some components use deliberately to prevent external access) are the one case Playwright genuinely cannot pierce — this is a rare, deliberate choice by component authors, and if you hit it, there's no workaround short of the app changing that setting.

page.get_by_role("button", name="Save changes").click()  # works even inside a
Interactive study board
Handling custom web componentsDrag stickies · tap for tips
Study mapDrag stickies · tap for tipsKeep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Assert the UIdrag · tap →Retry wiselydrag · tap →Seed datadrag · tap →Close the loopdrag · tap →Keep it shortdrag · tap →Name the waitdrag · tap →Scope locatorsdrag · tap →Trace when stuckdrag · tap →One browser firstdrag · tap →Isolate statedrag · tap →Pathwise hackdrag · tap →Page under testdrag · tap →Multi-browserdrag · tap →Automation pathdrag · tap →Tooling nodedrag · tap →
Clear?

Checklist