Skip to content

Instantly share code, notes, and snippets.

@wsydney76
Last active October 2, 2026 16:20
Show Gist options
  • Select an option

  • Save wsydney76/a117a0d463d112984bfd13d28367bc14 to your computer and use it in GitHub Desktop.

Select an option

Save wsydney76/a117a0d463d112984bfd13d28367bc14 to your computer and use it in GitHub Desktop.
Route Craft entry URL to Livewire fullpage-component
<?php
use CraftCms\Cms\Entry\Elements\Entry;
use CraftCms\Cms\Route\CurrentElement;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Validate;
use Livewire\Component;
use Livewire\Attributes\Url;
use Livewire\WithPagination;
/**
* A Livewire example full-page component that provides a search interface for Craft entries.
*
* Setup via a named route in `routes/web.php`:
*
* Route::livewire('/search', 'pages::search')->name('search');
*
* In Craft's section settings (single), set the "URI" to `search` and the "Route" to `search`.
*
* URI, domain, and HTTP method in named route and Craft's settings must match.
*
*/
new class extends Component {
use WithPagination;
#[Url]
#[Validate('not_regex:/ or /', message: 'The "OR" operator must be uppercase.')]
public string $search = '';
// Title/Teaser of the underlying entry element.
// Do not define a public `Entry $entry` property, as Craft's elements are not serializable
// and will throw an error when Livewire attempts to serialize the component state.
public string $title;
public string $teaser;
// Pull in the underlying entry element.
// The function signature must be exactly as this,
// otherwise #[CurrentElement] will not work
public function mount(#[CurrentElement] ?Entry $entry = null): void
{
[$this->title, $this->teaser] = [$entry->title, $entry->teaser];
$this->validate();
}
#[Computed]
public function entries()
{
return Entry::find()
->section('*')
->search($this->search)
->orderBy('score')
->withCustomFields(false)
->paginate(8);
}
// Set the page title passed to layout dynamically to the underlying entry's title.
public function render()
{
return $this->view()->title($this->title);
}
// Goto first page when search term changes
public function updatedSearch(): void
{
$this->resetPage();
}
};
?>
{{--
The Flux card component just adds some styling, but you can use any HTML you want here.
--}}
<flux:card variant="soft" body="divided">
<flux:card.header>
<flux:card.heading size="lg">{{ $title }}</flux:card.heading>
<flux:card.subheading>{{ $teaser }}</flux:card.subheading>
</flux:card.header>
<flux:card.body class="space-y-4">
{{--
wire:model provides two-way binding to the reactive search property, updating it as you type.
The Flux input component wraps a standard input and adds support for labels, icons, loading indicators, and error messages,
while providing styling consistent with the Flux design system, including dark mode.
You can use standard HTML elements instead if you prefer,
and handle validation errors using Laravel's standard validation approach.
--}}
<flux:input
label="Search for:"
type="search"
placeholder="Search..."
icon="magnifying-glass"
wire:model.live.debounce.250ms="search"
/>
<ul>
@forelse ($this->entries as $entry)
<li>{{ $entry->link }}</li>
@empty
<li>No entries found.</li>
@endforelse
</ul>
</flux:card.body>
<flux:card.footer>
{{-- Laravel's default navigation links --}}
{{ $this->entries->links() }}
</flux:card.footer>
</flux:card>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment