**Last updated**: 24 June 2025 | [**Change log**](/products/checkout/web/changelog/) # Shadow DOM You can inject the SDK in a Shadow DOM, making sure that the encapsulation of your HTML code/custom elements is preserved. Both `open` and `closed` modes are supported. #### Example configuration To inject the SDK in a Shadow DOM, you must pass a reference to the `shadowRoot` of your Shadow DOM in the SDK configuration: ```javascript Worldpay.checkout.init( { id: "your-checkout-id", form: "#card-form", fields: { pan: { selector: "#card-pan", placeholder: "4444333322221111" }, cvv: { selector: "#card-cvv", placeholder: "123" }, expiry: { selector: "#card-expiry", placeholder: "MM/YY" } }, shadowRoot: myShadowRoot // a reference to a shadow root is passed in the shadowRoot property }, function (error, checkout) { if (error) { console.error(error); return; } // rest of your code run at the time where the SDK is initialized } ); ``` ### Integration with custom elements You can also leverage the power of Shadow DOM to encapsulate all your checkout code in a custom element. Here is a full example: HTML // For production change to "https://access.worldpay-bsh.securedataplatform.com/access-checkout/v2/checkout.js" JavaScript customElements.define( 'custom-checkout', class extends HTMLElement { get checkout() { return this._checkout; } set checkout(value) { return this._checkout = value; } connectedCallback() { const shadow = this.attachShadow({ mode: 'closed' }); const template = document.getElementById('custom-checkout-template'); shadow.appendChild(template.content.cloneNode(true)); const thisInstance = this; Worldpay.checkout.init( { id: 'identity', form: '#card-form', fields: { pan: { selector: '#card-pan', placeholder: '4444 3333 2222 1111' }, expiry: { selector: '#card-expiry', placeholder: 'MM/YY' }, cvv: { selector: '#card-cvv', placeholder: '123' } }, styles: { 'input': { 'color': 'black', 'font-weight': 'bold', 'font-size': '20px', 'letter-spacing': '3px' }, 'input#pan': { 'font-size': '24px' }, 'input.is-valid': { 'color': 'green' }, 'input.is-invalid': { 'color': 'red' }, 'input.is-onfocus': { 'color': 'black' } }, enablePanFormatting: true, shadowRoot: shadow, }, function (error, checkout) { if (error) { console.error(error); return; } thisInstance.checkout = checkout; const form = shadow.getElementById('card-form'); form.addEventListener('submit', function (event) { event.preventDefault(); checkout.generateSessionState(function (error, sessionState) { if (error) { console.error(error); return; } // session state for card details console.log(sessionState); }); }); const clear = shadow.getElementById('clear'); clear.addEventListener('click', function (event) { event.preventDefault(); checkout.clearForm(function () { console.log('Form successfully cleared'); }); }); } ); } disconnectedCallback() { this.checkout.remove(); } } ); function addCheckout() { document.body.appendChild(document.createElement('custom-checkout')); } ### Limitations The SDK is not compatible with nested Shadow DOMs per se, because it can only be initialized in a Shadow DOM when the host element belongs to the document's Light DOM. If your Checkout form must be in a nested Shadow DOM, then a workaround consists in embedding your Checkout form into an iframe. Within the iframe's document context, the SDK can initialize normally either directly in the Light DOM or within a Shadow DOM.