How to Prevent Layout Shift in Your Web Component Sidebar
That annoying jump when your sidebar loads in? That's Cumulative Layout Shift (CLS), and it happens because your <x-sidebar>
component doesn't render its content immediately. Here's how to fix it properly:
When your page first loads:
The browser sees <x-sidebar>
as an empty tag
It lays out the page without reserving space for the sidebar
Then JavaScript runs and injects your sidebar content
The page suddenly shifts to make room - causing that jarring jump
Don't build DOM with strings in connectedCallback. Define a template outside your class instead:
javascript
// sidebar/index.js
const sidebarTemplate = document.createElement('template');
sidebarTemplate.innerHTML = `
<div id="sidebar" class="d-flex flex-column h-100 p-3 bg-body-tertiary">
<a href="/admin/home" class="h4 text-decoration-none text-nowrap">Central Api</a>
<hr>
<ul class="nav nav-pills flex-column">
<li class="nav-item">
<a id="home" class="nav-link" href="/admin/home">Home</a>
</li>
<li class="nav-item">
<a id="users" class="nav-link" href="/admin/users">Usuários</a>
</li>
</ul>
</div>
`;
class Sidebar extends HTMLElement {
connectedCallback() {
if (!this.shadowRoot) {
const shadow = this.attachShadow({ mode: 'open' });
shadow.appendChild(sidebarTemplate.content.cloneNode(true));
// Highlight active page
const url = window.location.pathname;
if (url.includes('/home')) {
shadow.querySelector('#home').classList.add('active');
} else if (url.includes('/user')) {
shadow.querySelector('#users').classList.add('active');
}
}
}
}
Add this to your global stylesheet:
css
x-sidebar {
display: block;
min-width: 220px;
min-height: 100vh;
}
This tells the browser "reserve this space" before the component even loads.
Make sure your component registers as soon as possible:
html
<head>
<script type="module" src="/main.js" defer></script>
</head>
Using defer
means it won't block page rendering but will execute in order.
If you want something to show before JavaScript loads:
html
<x-sidebar>
<div style="min-height: 100vh;">Loading sidebar...</div>
</x-sidebar>
This gets replaced when your component renders but prevents layout jumps.