In this third part of the series, we’ll dive into the role of JavaScript
Even though Livewire 3 already updates content without reloads, this small JavaScript bridge was essential for:
- Updating the URL in the browser history as filters change
- Keeping the page title and meta tags accurate for users
- Ensuring back/forward navigation in the browser re-triggers Livewire updates
The goal: a seamless and consistent UX without full page reloads or a separate SPA framework.
The Purpose for Users
For users, this JavaScript ensures that:
- When filters are applied, the page title and meta description change immediately in the browser tab.
- The browser URL updates dynamically, so sharing links always reflects the current filter combination.
- Clicking back/forward buttons updates the filters and product lists correctly, without any glitches.
- No flickering or reloads – everything feels native and smooth.
The Minimal JS Bridge
export default class ProductsFiltersLivewire {
constructor() {
this.isInitialized = false;
}
init(baseUrl) {
if (this.isInitialized) {
return;
}
this.isInitialized = true;
this.replaceHistoryState(baseUrl);
this.setupEventListeners();
this.setupLivewireHooks();
}
replaceHistoryState(baseUrl) {
history.replaceState({ url: [baseUrl] }, null, baseUrl);
}
setupEventListeners() {
window.addEventListener('popstate', (event) => {
Livewire.dispatch('url-changed-back', event.state.url);
});
}
setupLivewireHooks() {
// Update browser URL as filters or pages change
Livewire.on('url-updated', (url) => {
history.pushState({ url: url }, null, url);
});
Livewire.on('page-changed', (url) => {
history.pushState({ url: url }, null, url);
});
// Update meta tags and page heading instantly for a polished UX
Livewire.on('meta-tags-ready-for-update', (data) => {
document.title = data['meta_title'];
const robotsMeta = document.querySelector('meta[name="robots"]');
if (robotsMeta) robotsMeta.setAttribute('content', data['robots_content']);
const descMeta = document.querySelector('meta[name="description"]');
if (descMeta) descMeta.setAttribute('content', data['meta_description']);
const canonicalLink = document.querySelector('link[rel="canonical"]');
if (canonicalLink) canonicalLink.setAttribute('href', data['canonical_tag']);
const pageHeading = document.querySelector('.pageHeadingTitle');
if (pageHeading) pageHeading.textContent = data['page_heading_title'];
});
// Handle loading states for instant visual feedback
Livewire.hook('commit', (interaction) => {
this.handleCommit(interaction);
});
}
handleCommit({ component, commit, respond, succeed, fail }) {
this.setLoadingState(component.el, true);
succeed(() => {
this.setLoadingState(component.el, false);
});
fail(() => {
this.setLoadingState(component.el, false);
});
}
setLoadingState(el, loading) {
if (loading) {
el.classList.add("loading");
} else {
el.classList.remove("loading");
}
}
}
How It Interacts with Livewire
Livewire emits events like url-updated
, meta-tags-ready-for-update
, or lifecycle hooks (commit
) whenever filters change, new products are loaded, or the page is rehydrated.
This JS bridge listens to those events and:
- Updates the browser’s URL history
- Dynamically updates meta tags and titles in the browser DOM
- Manages loading states visually (e.g., a “loading” CSS class on filter panels or product areas)
Everything is driven by Livewire’s event system – the JS doesn’t do any data-fetching itself. It simply mirrors and finalizes what Livewire already did server-side.
Let’s Connect
If you’re looking for a senior developer to solve complex architecture challenges or lead critical parts of your product — let’s talk.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.