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

Statamic Meilisearch Laravel Package

elvenstar/statamic-meilisearch

Integrates Meilisearch with Statamic, providing a fast, typo-tolerant search driver for your site's content. Configure indexes and searchable fields, then query results through Meilisearch while keeping Statamic collections and entries as the source of truth.

View on GitHub
Deep Wiki
Context7

Getting Started

Version note (v4.1.0): this release only widens the framework constraint to include Laravel 13 — no config, API, index-format, or naming changes. It is a drop-in composer update for anyone already on v4.x. If you're pinning laravel/framework to ^13.0, this is the release that unblocks you; otherwise there is nothing to migrate and no index rebuild required.

Minimal setup:

  1. composer require statamic-rad-pack/meilisearch
  2. Run a Meilisearch instance (docker run -p 7700:7700 getmeili/meilisearch:latest) and add MEILISEARCH_HOST / MEILISEARCH_KEY to .env.
  3. Point Statamic's existing search config at the driver — in config/search.php, set the default 'driver' => 'meilisearch' (or set it per index) and add the driver's host/key/index-prefix block. Publish the addon config with php artisan vendor:publish and check the tag it registers before hand-editing anything.
  4. Declare an index with searchables (e.g. ['collection:articles']) and the fields you want searchable.
  5. php artisan statamic:search:index to populate Meilisearch, then Search::index('articles')->search('...') from a controller, or the Antlers search tag.

Where to look first:

  • config/search.php after publish — this is the surface area for 90% of the work.
  • vendor/statamic-rad-pack/meilisearch/src/ — the service provider (driver registration), the driver class, and the indexing/search implementations. Skim these before filing an issue; they tell you exactly which Meilisearch settings the package sets for you.
  • Your Meilisearch dashboard / curl http://localhost:7700/indexes — always confirm what actually landed in the index.

First real use case: index one blog collection, render results in an Antlers template with pagination, then add a filter (e.g. by category taxonomy) to prove the filterable-attribute pipeline works end to end. Filters are where most first integrations break.

Implementation Patterns

Driver-per-index. Statamic resolves a driver per index, not just globally. Keep the global default as local (or null) and opt specific indexes into meilisearch — this lets you keep cheap local search for low-value content and reserve Meilisearch for the indexes that need typo tolerance, facets, or ranking. It also makes it trivial to A/B a driver.

Shape documents with a transform. Don't dump entire entry arrays into the index. Use the index's field config / transform callback to emit a flat, small document (id, title, slug, url, excerpt, category, date, site). Smaller documents mean faster indexing, cheaper filtering, and results you can render without a second database hit. Anything derived (absolute URLs, resolved relationship titles) should be computed at index time, not at search time — search should not trigger Eloquent/Statamic lookups per hit.

Declare filterable and sortable attributes explicitly. Meilisearch rejects filters on attributes that aren't in filterableAttributes, and sorting silently does nothing if the attribute isn't sortableAttributes. Maintain that list in the same place as your searchable fields so the two never drift. If your index config supports it, have the package apply settings on reindex; if not, script the settings PUT alongside statamic:search:index in your deploy.

Treat indexing as an event-driven pipeline. Statamic fires save/delete events; the addon pushes changes to Meilisearch. That means:

  • A queue worker must be running in every environment where editors publish, or new entries never appear.
  • Bulk operations (imports, statamic:entries scripts, taxonomy reshuffles) should be followed by an explicit php artisan statamic:search:index.
  • In deploy scripts, reindex after migrations that change content shape, and consider statamic:search:clear before a full rebuild to avoid orphaned documents from deleted entries.

Multi-site: decide deliberately. Either keep one index and add site as a filterable attribute (simpler, lets you search across sites), or use per-site indexes (cleaner isolation, more admin surface). Pick one before content editors start relying on it — switching later means reindexing and touching every query.

Testing and CI. Spin up the getmeili/meilisearch Docker image as a CI service, point MEILISEARCH_HOST at it, and seed in a setUp. Meilisearch is task-based and asynchronous: after writing documents, poll the task (or use the client's wait helper) before asserting on results, otherwise tests are flaky by design. For pure unit tests of your controllers, bind a fake/swap on the search index rather than hitting Meilisearch at all.

Reach for the raw client for anything non-search. The package wraps Statamic's search contract, so facets, stats, and index settings are outside it. Bind or resolve the underlying Meilisearch\Client and use it directly for health checks, admin dashboards, or analytics — just don't duplicate the write path.

Gotchas and Tips

  • This is not Laravel Scout. Do not run php artisan scout:import, and don't expect a Searchable trait. It plugs into Statamic's search abstraction. Half the confusion in the wild comes from mixing the two up.
  • Asynchronous by default. Documents are not queryable the instant a save returns. In production this is invisible; in tests, feature demos, and "publish then immediately preview" flows it looks like a bug. Wait on the task or add a tiny retry.
  • API key permissions are the #1 silent failure. The key needs documents.*, indexes.*, settings.*, and tasks.get. A read-only key will let search work while every write 403s in the queue log. Check failed_jobs first when nothing indexes.
  • Filter syntax is strict. Unknown filterable attributes produce a 400, not empty results. Surface Meilisearch's error message in your own logging during development — the framework-level exception usually swallows the useful detail.
  • Shared Meilisearch instances collide. Dev, staging, and prod pointed at the same host with the same index name will overwrite each other. Always set an environment-specific index prefix (e.g. staging_articles).
  • Deleted entries linger. Unpublishing or hard-deleting outside the normal event path leaves documents in the index. Schedule a periodic full reindex, or make statamic:search:clear && statamic:search:index part of the deploy for content-heavy sites.
  • Pagination ceilings. Meilisearch caps how deep you can paginate by default (1000 hits). If you're building archive pages, set hitsPerPage sensibly and offer filtered browsing rather than deep pagination.
  • Empty results usually mean an empty index, not a bad query. Before debugging query syntax, hit the Meilisearch dashboard and confirm document count. Nine times out of ten the index was never built in that environment, or the queue wasn't running.
  • Config typos fail quietly. A misspelled index name in Search::index('articals') gives you an empty collection, not an exception. Keep index names in a constant or enum.
  • Laravel 13 / upgrade path. v4.1.0 is additive only. If you're testing against Laravel 13, add the matrix entry and re-run your integration tests against a real Meilisearch container — that's the only place where version skew would show up. There is no reason to rush this upgrade unless a Laravel 13 constraint is blocking you.
  • Extension points worth knowing: the driver is resolved through the container, so you can decorate or swap the client binding (Meilisearch\Client) in a service provider to add middleware, logging, or a tenancy-aware host — this is cleaner than forking the package for one behavioural tweak.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings