Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Mellisearch Laravel Package

statamic-rad-pack/mellisearch

A Statamic addon that brings Meilisearch to your site, delivering fast, typo-tolerant full-text search and filtering across your content. Index entries automatically, then query results with a simple, developer-friendly API.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up

composer require statamic-rad-pack/meilisearch
php artisan vendor:publish --tag=meilisearch-config

Run Meilisearch somewhere reachable (local Docker is fastest):

docker run -p 7700:7700 -e MEILI_MASTER_KEY=masterKey getmeili/meilisearch:latest

Then three env/config touchpoints:

SEARCH_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey

…and confirm config/search.php points the active driver at meilisearch (the package registers the driver; SEARCH_DRIVER is what Statamic reads).

Build the index:

php artisan statamic:search:index   # Statamic 5
php artisan search:index            # Statamic 4 and earlier

Where to look first: the published config/meilisearch.php. It is the whole contract — index definition, which fields are searchable, and which are filterable / sortable. Read that file before touching templates; almost every "search doesn't work" ticket traces back to it.

First use case: replace an existing collection search with the Antlers search tag:

{{ search:results index="default" query="{get:q}" limit="10" }}
    <a href="{{ url }}">{{ title }}</a>
{{ /search:results }}

If results appear, the driver, key, and index are all correct. Everything after that is tuning.

Implementation Patterns

Two API surfaces, same index. Antlers for the front end, the Search facade for anything programmatic (JSON endpoints, Livewire, Artisan commands):

use Statamic\Facades\Search;

Search::index('default')
    ->search($request->string('q'))
    ->where('collection', 'articles')   // must be declared filterable
    ->sort('date:desc')                 // must be declared sortable
    ->limit(20)
    ->get();

where() and sort() are not free-form. Meilisearch rejects filters and sorts on attributes that were never declared as filterable/sortable at index time — see Gotchas.

Model your index around the query, not the entry. Define searchable as the fields you actually want typed into a search box, plus any attributes you need for where(). Dumping every field into the index bloats payloads, slows indexing, and makes relevancy worse because Meilisearch scores against everything.

Scope with filterable attributes, not separate indexes. A single index with collection, site, locale, and a date field declared as filterable covers most multi-section sites:

{{ search:results index="default" query="{get:q}" where="collection:articles" }}

Reach for a second index only when the searchable payload genuinely differs (e.g. products vs. editorial content), because each index is a separate thing to keep in sync.

Treat reindexing as a deploy step. Statamic keeps the index current on entry save via its indexer, but config changes, blueprint changes, and content imported outside the CP do not retroactively fix what's already indexed. Put a full reindex in your deploy pipeline after migrations, and make it idempotent so it is safe to run twice.

Batch and background on large sites. A one-shot reindex of thousands of entries over a slow link will time out or trip request size limits. Chunk by collection and run it on a queue worker in production; run it manually in local dev where you want immediate feedback.

Use Search::fake() in tests. It keeps the suite off a live Meilisearch instance and makes assertions about what would have been queried:

Search::fake();
// ... hit the controller ...
Search::index('default')->assertExists(); // or assert query payloads

Reserve real Meilisearch for a thin integration test that proves the driver + key + filterable config actually work end to end.

Custom drivers are still first-class. If you outgrow the bundled behaviour, Search::extend('meilisearch', fn () => new YourDriver(...)) in a service provider lets you wrap or replace the driver without forking the package.

Gotchas and Tips

filterable and sortable are hard constraints, not hints. Add a field to where() without declaring it filterable and you get a runtime error from Meilisearch, not a graceful fallback — often only on the page that uses that facet. Declare them at the same time you write the query, not after.

Changing index settings requires a reindex. Meilisearch stores filterable/sortable attributes on the index itself. Editing config/meilisearch.php does nothing to an already-built index until you re-run the index command. This is the single most common source of "I added the field and it still won't filter."

Key permissions matter more than the key value. The master key works; a search-only key will let the front end query but silently fail every write during indexing. Use a key with index read/write for the app, and a search-only key only if you are querying Meilisearch directly from the browser.

Index-name collisions across environments. Dev, staging, and prod all defaulting to the same index name means someone's local reindex nukes production search. Suffix the index name with the environment (statamic_production, statamic_local) and thank yourself later.

Watch what content actually contains. For Markdown fields the indexed value is the rendered/augmented output — good for search relevance, bad if you expected raw markup. If you need to exclude navigation, shortcodes, or boilerplate, curate the searchable value rather than indexing the whole field.

Empty queries and typo tolerance. Meilisearch returns results even for very short or empty queries, and its typo tolerance will happily match things you did not intend. Guard empty queries in the controller/template before they hit the index, and tune limit rather than post-filtering in PHP.

Laravel 13 readiness lands in 4.1.0. If you are moving an app to Laravel 13, pin ^4.1 — 4.0.x will fail dependency resolution. On Laravel 11/12 nothing changed; 4.1.0 is a compatibility release with no API or config changes, so upgrading is low risk. Verify your Statamic major (and therefore which index command name applies) before copying commands out of older docs.

Debug in this order: is Meilisearch up and reachable (curl $MEILISEARCH_HOST/health) → is the key valid for writes → does the index exist and have documents → do the failing attributes appear in the index's filterable list → then look at your template. Almost every issue resolves in the first four steps.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums