Learn how to build custom web components in Wix Velo using Shadow DOM. In this tutorial, I'll show you the same patterns we use at UH Tech to build advanced components for real client projects like ElitePetz and Verify Vault. What you'll learn: ✓ Why custom components beat native Wix elements ✓ How to set up a custom element in Wix Editor ✓ What Shadow DOM is and why it matters ✓ How to write your first custom component ✓ Style isolation that just works Perfect for Wix Velo developers who want to go beyond basic components. 🔗 To Get Code: https://github.com/UH-TECH/Custom-Web-Components-in-Wix-Velo 🔗 Need a Wix Velo developer? https://uhtech.solutions 🔗 Connect with us: https://www.linkedin.com/company/uh-technology-software About UH Tech: We're a software studio that builds advanced systems on Wix Velo, Shopware 6, and Next.js. New Wix Velo tutorials every Tuesday. #WixVelo #CustomElements #ShadowDOM #WebComponents #WixTutorial
Web Element in Wix Velo
// public/admin-table.js
if (!customElements.get('admin-table')) {
class AdminTable extends HTMLElement {
constructor() {
super();
// Attach Shadow DOM for full encapsulation
this.attachShadow({ mode: 'open' });
this._data = [];
}
// Tell the browser which attributes to watch
static get observedAttributes() {
return ['result'];
}
// Lifecycle: when element is added to DOM
connectedCallback() {
this.render();
}
// Lifecycle: when watched attribute changes
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'result' && newValue) {
try {
this._data = JSON.parse(newValue);
this.render();
} catch (err) {
console.error('Invalid JSON in result attribute', err);
}
}
}
render() {
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
font-family: system-ui, sans-serif;
}
table {
width: 100%;
border-collapse: collapse;
background: #0A1628;
color: #fff;
border-radius: 8px;
overflow: hidden;
}
th, td {
padding: 12px 16px;
text-align: left;
border-bottom: 1px solid #1a2942;
}
th {
background: #8B5CF6;
font-weight: 600;
}
tr:hover td {
background: #1a2942;
}
</style>
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
</tr>
</thead>
<tbody>
${this._data.map(row => `
<tr>
<td>${row.name}</td>
<td>${row.email}</td>
<td>${row.status}</td>
</tr>
`).join('')}
</tbody>
</table>
`;
}
}
customElements.define('admin-table', AdminTable);
}