Root and Element Internals You can always define the internals the same as you usually would, and if for some reason you need access to either the element itself or the shadow root, you can do so as illustrated below. const MyElement = customElement((params) => { const { internals, elementRef, root, connectedCallback, } = params; internals.role = 'button'; connectedCallback(() => { console.log(elementRef.querySelector('a[href]')); // light DOM console.log(root.querySelector('a[href]')); // shadow DOM }); // ... }); If you need to pass certain options to the attachShadow() call, you can do so by passing shadowRootOptions. const MyElement = customElement(() => { // ... }, { shadowRootOptions: { delegatesFocus: true } }); To opt out of using shadow DOM at all, pass false to attachShadow. This will change root above to reference the element itself, and stylesheets will apply to the global document rather than being encapsulated within the component. const MyElement = customElement(() => { // ... }, { attachShadow: false }); Keep in mind that opting out of shadow DOM means that <slot> tags will no longer work. For light DOM content, you can use the innerHTML property to reference the original content of the element. const MyElement = customElement(({ root }) => { return html` <button class="primary"> ${root.innerHTML} </button> `; }, { attachShadow: false });