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

Typeahead Bundle Laravel Package

lifo/typeahead-bundle

Symfony bundle that integrates Twitter Typeahead.js into forms and templates, adding AJAX-powered autocomplete and suggestion fields with minimal configuration. Provides helpers for connecting typeahead inputs to controllers and data sources.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register the bundle (Symfony 2/3 style; for Symfony 4+ add it to config/bundles.php):

composer require lifo/typeahead-bundle
// app/AppKernel.php
$bundles = [
    // ...
    new Lifo\TypeaheadBundle\LifoTypeaheadBundle(),
];

There are two files to read before you write a line of code: Form/Type/TypeaheadType.php (the options and the defaults it injects) and Resources/views/Form/typeahead_widget.html.twig (the markup and the JS init). Everything the bundle does happens between those two files. The bundle is a thin wrapper — it does not ship a JS plugin, so your base layout still needs jQuery and a Bootstrap-3-compatible typeahead.js (bootstrap-3-typeahead is the common fork) loaded in that order.

First use case — an autocomplete text field backed by a JSON endpoint:

use Lifo\TypeaheadBundle\Form\Type\TypeaheadType;

$builder->add('city', TypeaheadType::class, [
    'source'    => '/api/cities?q=%QUERY', // %QUERY is replaced client-side
    'minLength' => 2,
    'items'     => 8,
]);

The controller just returns a JSON array — strings, or {value, id} objects if you need to carry an identifier:

public function searchCities(Request $request)
{
    $cities = $this->getDoctrine()->getRepository(City::class)
        ->search($request->query->get('q'), 20);

    return $this->json(array_map(fn (City $c) => [
        'id' => $c->getId(), 'value' => $c->getName(),
    ], $cities));
}

Implementation Patterns

Label vs. value (the one pattern you will always need). Typeahead is a display widget, not a <select>. The input posts the label the user typed, not the id. The standard fix is a hidden field plus a typeahead:selected handler:

$builder
    ->add('city', TypeaheadType::class, ['source' => '/api/cities?q=%QUERY'])
    ->add('cityId', HiddenType::class, ['required' => false]);
$(document).on('typeahead:selected typeahead:autocompleted', 'input[data-provide="typeahead"]', function (e, item) {
    $(this).closest('form').find('[name$="[cityId]"]').val(item.id);
});

On submit, resolve the id in a PRE_SUBMIT listener (or a DataTransformer) so the entity is what actually gets persisted — never trust the free-text label.

Form theme override instead of inline script. Don't hand-write <script> blocks in your templates. Override the typeahead_widget block in your form theme once, and pass plugin options through type options rather than hard-coding them per view. This is the cleanest extension point the bundle offers: it keeps all the JS configuration in PHP.

Collections / dynamically added rows. The bundle initialises on DOM ready, so rows added later by a collection prototype won't be wired up. Either re-run the init on collection:entry-added, or use the delegate-event style above ($(document).on(...)) which survives DOM insertion for the selection handler. Prefer delegating early — it's the difference between "works" and "mysteriously broken on the second row".

Validate what comes back. Add a NotBlank/custom constraint on cityId and re-fetch the entity server-side, then compare it against the submitted label. It is the only reliable guard against a user posting arbitrary text into a field the UI pretends is a picker.

Testing. The type does nothing but set view variables and attributes, so a TypeTestCase asserting the rendered attributes/options is sufficient; there's no need to mock HTTP.

Gotchas and Tips

  • Bootstrap 3 is a hard ceiling. Bootstrap 4/5 removed the typeahead plugin entirely; the bundle's JS integration targets Bootstrap 3's plugin API. If you're on a modern stack, budget for a migration to Choices.js or Tom Select rather than fighting this. Last release was 2018 — treat it as a legacy dependency.
  • %QUERY must survive URL encoding. If your endpoint is generated by the router, generate('api_cities', ['q' => '%QUERY']) will percent-encode the placeholder. Build the string manually or decode it once before passing it as the source option.
  • Load order is silently fatal. jQuery → Bootstrap 3 → typeahead plugin → your init. jQuery loaded after the plugin produces no error, just a dead field. If nothing happens, check this first.
  • Check the network tab before debugging PHP. Empty dropdown with a 200 response usually means the JSON shape is wrong ([{value: ...}] vs. bare strings, or a Symfony profiler HTML wrapper on the response). Verify Content-Type: application/json.
  • The search endpoint goes through the firewall. It's a GET from an already-authenticated page, so it usually works — but if you use a stateless/firewall-per-path setup, whitelist the route explicitly.
  • Inspect the rendered data-* attributes. The Twig theme emits the plugin config as data attributes; when a plugin option "doesn't work", confirm it actually reached the DOM before assuming a bundle bug. minLength, items, and displayKey are the usual suspects.
  • Symfony version drift. The bundle predates the modern AbstractType signatures. If you're on 4.4+/5.x, expect deprecation noise at minimum and possible hard breaks; pin it, wrap it, and keep a replacement plan. Its 21 stars and zero dependents mean you are the maintainer of last resort.
  • Server-side limits are not optional. The plugin caps rendering with items, but the endpoint should also LIMIT — otherwise a single keystroke pulls your whole table.
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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource