Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

totoosa's avatar

Livewire doesn't keep track of my request when navigating within my component.

I have a blogpost component that renders blogpost and shows categories ontop as a means of filtering said blogpost across different topics. Now all functionality of rendering, filtering ,pagination and searching works as expected but after navigating to a particular category and we attempt to go to a next page or search blogposts. the filtering to determine which blogpost which is a check on our url request seems to fail . The x-paper-link and all relevant code are below. I can't seem to fix it either, what would be the best approach to fixing such a bug,

{{-- BlogPostList.php --}}
<div class="flex flex-col items-start gap-12 w-full mx-auto my-16 md:my-20  lg:my-24 px-4 md:px-8 relative z-10">
    <!-- Blog Categories -->
    <div class="flex flex-col items-center gap-2 self-stretch relative">
        <h2 class="font-chillax-Variable font-[450] text-3xl lg:text-4xl text-eerieBlack-100 leading-none tracking-tight ">Our Latest News</h2>
        <livewire:blog-search>
        
        <x-paper-tab >
            <x-paper-link wire:navigate href="{{route('blog')}} " :active="!request()->has('category')">All Posts</x-paper-link>
            @foreach($blogCategories as $blogCategory)
                <div wire:key='{{$blogCategory->id}}'>
                <x-paper-link wire:navigate
                    href="{{ route('blog', ['category' => $blogCategory->slug])}}" 
                    :active="request()->has('category') && request()->query('category') === $blogCategory->slug">
                    {{$blogCategory->title}}
                </x-paper-link>
                </div>
            @endforeach
        </x-paper-tab>

    </div>
    
    <div class="w-fit columns-1 md:columns-2 gap-4 mx-auto backdrop-blur-lg">
        @foreach($blogPosts as $blogPost)
            <div wire:key='{{$blogPost->id}}'>
            <x-blog.blog-post-item :key="$blogPost->id" :blogPost="$blogPost"/>
            </div>
        @endforeach
    </div>

    <div class="flex w-full justify-center items-center">
        
        {{ $blogPosts->onEachSide(1)->links(data: ['scrollTo' => false]) }}
    </div>
</div>


<script>
    document.addEventListener("livewire:navigated", () => {
        // Add data-scroll-x to body:
        document.body.setAttribute("data-scroll-x", window.scrollX);
    })
</script>

So when i click on the each x-paper-link, it shows me relevant blogpost across said category with the livewire class like having said functionality

{{-- blog-post-list.blade.php --}}
<?php

namespace App\Livewire;

use App\Models\BlogPost;
use App\Models\BlogCategory;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\Features\SupportPagination\WithoutUrlPagination;
use Livewire\WithPagination;

class BlogPostList extends Component
{

    use WithPagination, WithoutUrlPagination;

    #[Url()]
    public $search = '';

    #[Url()]
    public $category = '';

    #[Computed()]
    public $scrollToTop = false;

    #[On('search')]
    public function updateSearch($search){
        $this->resetPage();
        // $this->clearCategory();
        $this->search = $search;
    }

    #[Computed()]
    public function clearCategory()
    {
        $this->scrollToTop = false; // Ensure scrollToTop is false
        $this->redirect(route('blog')); // Redirect to the same route without 'category' parameter
    }

    public function setCategory($slug)
    {
        $this->category = $slug;
        $this->scrollToTop = true; // Set scrollToTop to true when category is updated
    }

    public function updatingCategory()
    {
        $this->scrollToTop = true; // Set scrollToTop to true when category is updating
    }

    public function dispatchNavigatedEvent()
    {
        $this->dispatchBrowserEvent('livewire:navigated');
    }

    #[Computed()]
    public function blogPosts(){
        return BlogPost::published()
            ->orderBy('published_at', 'desc')
            ->where('title','like', "%{$this->search}%")
            ->when(BlogCategory::where('slug',$this->category)->first(), function($query){
                $query->withBlogCategory($this->category);
            })
            ->paginate(4);
    }

    #[Computed()]
    public function blogCategories(){
        return BlogCategory::whereHas('blogPosts', function ($query) {
            $query->published();
        })->get();
        
    }
    
    public function render()
    {
        return view('livewire.blog-post-list', [
            'blogCategories' => $this->blogCategories(),
            'blogPosts' => $this->blogPosts()
        ]);
    }

}

x-paper-link.php

@props(['active', 'categorySlug'])

@php
$classes = ($active ?? false)
    ? "list-none inline-block rounded-t-xl rounded-tr-[10px] relative before:content-[''] after:content-[''] before:absolute after:absolute before:bottom-0 after:bottom-0 before:bg-[#F8F0EA] after:bg-inherit hover:before:bg-[#EAE0DE] hover:after:bg-[#EAE0DE] before:w-0 after:w-5 before:h-0 after:h-2.5 before:-left-2.5 after:-right-[17.5px] font-[450] text-floralWhite-100 hover:text-roseQuartz-400 hover:bg-[#EAE0DE] bg-skyMagenta-100 shadow hover:shadow-lg z-30"
    : "list-none inline-block rounded-t-xl rounded-tr-[10px] relative before:content-[''] after:content-[''] before:absolute after:absolute before:bottom-0 after:bottom-0 before:bg-[#F8F0EA] after:bg-[#F8F0EA] hover:before:bg-[#EAE0DE] hover:after:bg-[#EAE0DE] before:w-0 after:w-5 before:h-0 after:h-2.5 before:-left-2.5 after:-right-[17.5px] hover:text-eerieBlack-70 hover:bg-[#EAE0DE] bg-[#F8F0EA] shadow hover:shadow-lg z-30"
@endphp


<a {{ $attributes->merge(['class' => $classes]) }}>
    <span class="block py-1 pt-1.5 px-4 text-left rounded-t-[10px] before:content-[''] after:content-[''] before:absolute after:absolute before:bottom-0 after:bottom-0 before:w-0 after:w-5 before:h-0 after:h-5 before:rounded-[0px] after:rounded-bl-full before:bg-floralWhite-100 after:bg-floralWhite-100 before:z-10 after:z-10 before:-left-5 after:-right-[24px] after:skew-y-12">{{$slot}}</span>
    <span class="absolute aspect-square h-[25px] -right-[24px] -bottom-0 bg-inherit triangle z-0"></span>
</a>

Note: i am setting the active state of x-paper-link based on the our query string attached to our url, it matches when you navigate but not afterwards.

0 likes
1 reply
LaryAI's avatar
LaryAI
AI
Best Answer
Level 58

It seems like the issue you're facing is related to Livewire's state management when dealing with query string parameters and navigation. When you navigate to a category and then attempt to paginate or search, the category filter is lost because the state is not properly maintained in the URL.

To fix this, you need to ensure that the category state is preserved in the URL when performing other actions like searching or paginating. Here's how you can modify your Livewire component to handle this:

  1. Use Livewire's WithQueryString trait to keep track of the query string parameters.
  2. Update the setCategory method to redirect with the category parameter.
  3. Ensure that the pagination links include the current state of the query string parameters.

Here's the updated Livewire component:

namespace App\Livewire;

use App\Models\BlogPost;
use App\Models\BlogCategory;
use Livewire\Component;
use Livewire\WithPagination;
use Livewire\WithQueryString;

class BlogPostList extends Component
{
    use WithPagination, WithQueryString;

    public $search = '';
    public $category = '';

    protected $queryString = [
        'search' => ['except' => ''],
        'category' => ['except' => ''],
    ];

    public function updatingSearch()
    {
        $this->resetPage();
    }

    public function updatingCategory()
    {
        $this->resetPage();
    }

    public function setCategory($slug)
    {
        $this->category = $slug;
        $this->resetPage();
    }

    public function clearCategory()
    {
        $this->category = '';
        $this->resetPage();
    }

    public function blogPosts()
    {
        $query = BlogPost::published()->orderBy('published_at', 'desc');

        if ($this->search) {
            $query->where('title', 'like', "%{$this->search}%");
        }

        if ($this->category) {
            $query->whereHas('categories', function ($query) {
                $query->where('slug', $this->category);
            });
        }

        return $query->paginate(4);
    }

    public function blogCategories()
    {
        return BlogCategory::whereHas('blogPosts', function ($query) {
            $query->published();
        })->get();
    }

    public function render()
    {
        return view('livewire.blog-post-list', [
            'blogCategories' => $this->blogCategories(),
            'blogPosts' => $this->blogPosts()
        ]);
    }
}

Make sure your pagination links in the Blade template are generated with Livewire's links() method, which should automatically include the current state of the query string:

{{ $blogPosts->links() }}

By using the WithQueryString trait, Livewire will automatically keep track of the search and category parameters in the URL, ensuring that the state is preserved when you paginate or search.

Lastly, ensure that your x-paper-link components are correctly setting the active state based on the current query string. If you're using Alpine.js or another front-end framework, you might need to adjust the logic to reactively update based on the Livewire state.

Please or to participate in this conversation.