DEV Community

Mykola Vantukh
Mykola Vantukh

Posted on • Edited on

Laravel Livewire 3 Filter SEO and UI (Part 3)

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");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

Connect with me on LinkedIn

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.