**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.