kerrialnewham / autocomplete
Installs: 6
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:symfony-bundle
pkg:composer/kerrialnewham/autocomplete
Requires
- php: ^8.2
- symfony/form: ^7.0|^8.0
- symfony/options-resolver: ^7.0|^8.0
- symfony/stimulus-bundle: ^2.9
- symfony/twig-bundle: ^7.0|^8.0
Requires (Dev)
- doctrine/doctrine-bundle: ^2.11
- doctrine/orm: ^3.0
Suggests
- doctrine/doctrine-bundle: Required for Doctrine integration with Symfony
- doctrine/orm: Required for AutocompleteEntityType and automatic entity provider generation
This package is auto-updated.
Last update: 2026-01-06 13:42:23 UTC
README
A highly customizable autocomplete bundle for Symfony that uses server-side rendering with Twig templates and Stimulus for interactivity. Unlike other solutions, this bundle doesn't depend on Tom Select or similar libraries, giving you complete control over styling and behavior.
Features
- Server-side rendering with Twig templates for maximum customizability
- Stimulus-based client-side interactivity
- Support for single and multiple selection modes
- Keyboard navigation (arrow keys, enter, escape)
- Debounced search requests
- Extensible provider system
- Customizable templates for options, chips, and the entire widget
- Built for Symfony 7+ and PHP 8.2+
Installation
Install the bundle via Composer:
composer require kerrialnewham/autocomplete
Configuration
Add the bundle routes to your config/routes.php:
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; return function (RoutingConfigurator $routes) { $routes->import('@AutocompleteBundle/config/routes.php'); };
Or in config/routes.yaml:
autocomplete: resource: '@AutocompleteBundle/config/routes.php'
Quick Start
The bundle includes example providers to help you get started quickly:
Try the Country Provider
- Register the example provider in
config/services.yaml:
services: Kerrialnewham\Autocomplete\Example\CountryProvider: tags: - { name: 'autocomplete.provider' }
- Add the form theme to
config/packages/twig.yaml:
twig: form_themes: - '@Autocomplete/form/autocomplete_widget.html.twig'
- Include the CSS in your base template (e.g.,
templates/base.html.twig):
<link rel="stylesheet" href="{{ asset('bundles/autocomplete/autocomplete.css') }}">
- Use it in your form:
use Kerrialnewham\Autocomplete\Form\AutocompleteType; $builder->add('country', AutocompleteType::class, [ 'provider' => 'countries', 'placeholder' => 'Search for a country...', ]);
Live Demo Page
Want to see it in action first? The bundle includes a complete demo page:
- Follow the steps above to register the CountryProvider and form theme
- Import the demo routes in
config/routes.php:
$routes->import('@AutocompleteBundle/src/Example/demo_routes.php');
- Visit
/autocomplete/demoin your browser
The demo showcases single selection, multiple selection with chips, custom configurations, and form submission handling.
See the Example Providers for more examples including database integration.
Usage
1. Create an Autocomplete Provider
Providers define how to search and retrieve data for your autocomplete fields. Create a class that implements AutocompleteProviderInterface:
<?php namespace App\Autocomplete\Provider; use Kerrialnewham\Autocomplete\Provider\Contract\AutocompleteProviderInterface; use App\Repository\UserRepository; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; #[AutoconfigureTag('autocomplete.provider')] class UserProvider implements AutocompleteProviderInterface { public function __construct( private readonly UserRepository $userRepository, ) { } public function getName(): string { return 'users'; } public function search(string $query, int $limit, array $selected): array { $users = $this->userRepository->searchByName($query, $limit, $selected); return array_map(function ($user) { return [ 'id' => (string) $user->getId(), 'label' => $user->getFullName(), 'meta' => [ 'email' => $user->getEmail(), ], ]; }, $users); } }
The search() method must return an array of items with this structure:
[
[
'id' => 'unique-id',
'label' => 'Display Label',
'meta' => [...], // Optional additional data
],
// ...
]
2. Use in Forms
Using AutocompleteType
<?php namespace App\Form; use Kerrialnewham\Autocomplete\Form\AutocompleteType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; class MyFormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('user', AutocompleteType::class, [ 'provider' => 'users', 'placeholder' => 'Search for a user...', 'multiple' => false, ]) ->add('tags', AutocompleteType::class, [ 'provider' => 'tags', 'placeholder' => 'Add tags...', 'multiple' => true, 'min_chars' => 2, 'debounce' => 500, 'limit' => 20, ]); } }
Available Options
| Option | Type | Default | Description |
|---|---|---|---|
provider |
string | 'default' | Name of the autocomplete provider |
multiple |
bool | false | Allow multiple selections |
placeholder |
string | 'Search...' | Input placeholder text |
min_chars |
int | 1 | Minimum characters before search |
debounce |
int | 300 | Debounce delay in milliseconds |
limit |
int | 10 | Maximum results to fetch |
attr |
array | [] | Additional HTML attributes |
3. Register the Form Theme
Add the autocomplete form theme to your config/packages/twig.yaml:
twig: form_themes: - '@Autocomplete/form/autocomplete_widget.html.twig'
4. Use Directly in Twig (Without Forms)
You can also use the autocomplete widget directly in Twig templates:
{% include '@Autocomplete/autocomplete/autocomplete.html.twig' with {
name: 'user_id',
provider: 'users',
multiple: false,
placeholder: 'Search for a user...',
min_chars: 1,
debounce: 300,
limit: 10,
value: null
} %}
Customization
Customize Templates
The bundle provides three main templates that you can override:
1. Options Template (_options.html.twig)
Override this to customize how search results are displayed:
{# templates/autocomplete/_options.html.twig #} {% for result in results %} <div class="autocomplete-option custom-option" role="option" data-autocomplete-option-value="{{ result.id }}" data-autocomplete-option-label="{{ result.label }}" {% if result.meta is defined %}data-autocomplete-option-meta="{{ result.meta|json_encode }}"{% endif %} tabindex="-1"> <div class="option-content"> <strong>{{ result.label }}</strong> {% if result.meta.email is defined %} <small>{{ result.meta.email }}</small> {% endif %} </div> </div> {% else %} <div class="autocomplete-no-results">No users found</div> {% endfor %}
2. Chip Template (_chip.html.twig)
Override this to customize selected item badges:
{# templates/autocomplete/_chip.html.twig #} <div class="autocomplete-chip custom-chip" data-autocomplete-chip-value="{{ item.id }}" data-autocomplete-target="chip"> <span class="chip-avatar">{{ item.label|first }}</span> <span class="autocomplete-chip-label">{{ item.label }}</span> <button type="button" class="autocomplete-chip-remove" data-action="autocomplete#removeChip" aria-label="Remove {{ item.label }}"> ✕ </button> <input type="hidden" name="{{ name }}[]" value="{{ item.id }}" data-autocomplete-target="selectedInput"> </div>
3. Widget Template (autocomplete.html.twig)
Override the entire widget template for complete control.
Customize Styling
Built-in Themes
The bundle includes four built-in themes, all packaged in a single CSS file:
- default - Clean, modern design with pill-shaped chips
- dark - Dark mode variant with muted colors
- cards - Card-style layout with metadata display (email, domain, role, avatar)
- bootstrap-5 - Bootstrap 5 compatible styling
To use a theme, specify it in your form:
$builder->add('country', AutocompleteType::class, [ 'provider' => 'countries', 'theme' => 'dark', // or 'cards', 'bootstrap-5' ]);
Theme switching is instant - it's controlled by a data-autocomplete-theme attribute on the wrapper element.
Override Default Theme Styles
Add custom CSS in your own stylesheet. Use data attribute selectors to target specific themes:
/* Override default theme */ [data-autocomplete-theme="default"] .autocomplete-input { border: 2px solid #4f46e5; border-radius: 8px; } /* Override dark theme */ [data-autocomplete-theme="dark"] .autocomplete-chip { background-color: #7c3aed; } /* Override all themes */ .autocomplete-option:hover { background-color: #eef2ff; }
Create a Custom Theme
To create a completely custom theme, add CSS scoped to your theme name:
/* My custom theme */ [data-autocomplete-theme="my-custom"] .autocomplete-wrapper { font-family: 'Inter', sans-serif; } [data-autocomplete-theme="my-custom"] .autocomplete-input { background: linear-gradient(to right, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 1rem; } [data-autocomplete-theme="my-custom"] .autocomplete-chip { background-color: #667eea; color: white; }
Then use it in your form:
$builder->add('country', AutocompleteType::class, [ 'provider' => 'countries', 'theme' => 'my-custom', ]);
Disable Bundle CSS
If you want complete control, don't include the bundle's CSS file and write your own from scratch. The bundle will still function - you'll just need to provide your own styles for the HTML structure.
Stimulus Controller Events
The autocomplete controller dispatches custom events:
// Listen for selection events document.addEventListener('autocomplete:select', (event) => { console.log('Selected:', event.detail.item); }); // Listen for removal events document.addEventListener('autocomplete:remove', (event) => { console.log('Removed:', event.detail.value); });
Entity Autocomplete
The bundle provides built-in support for Doctrine entities with automatic provider generation and data transformation.
Installation
To use entity autocomplete features, install Doctrine ORM:
composer require doctrine/orm doctrine/doctrine-bundle
Using AutocompleteEntityType
The AutocompleteEntityType extends Symfony's EntityType and provides autocomplete functionality with zero configuration:
use Kerrialnewham\Autocomplete\Form\Type\AutocompleteEntityType; $builder->add('author', AutocompleteEntityType::class, [ 'class' => User::class, 'choice_label' => 'username', 'placeholder' => 'Search for a user...', ]);
With Query Builder
Customize the search query:
$builder->add('category', AutocompleteEntityType::class, [ 'class' => Category::class, 'choice_label' => 'name', 'query_builder' => function (EntityRepository $repo) { return $repo->createQueryBuilder('c') ->where('c.active = true') ->orderBy('c.name', 'ASC'); }, ]);
Multiple Selection
$builder->add('tags', AutocompleteEntityType::class, [ 'class' => Tag::class, 'multiple' => true, 'choice_label' => 'name', 'theme' => 'cards', ]);
With Callable Choice Label
$builder->add('author', AutocompleteEntityType::class, [ 'class' => User::class, 'choice_label' => fn(User $u) => $u->getFullName(), 'theme' => 'bootstrap-5', ]);
Custom Choice Value
Use a property other than 'id' as the identifier:
$builder->add('product', AutocompleteEntityType::class, [ 'class' => Product::class, 'choice_label' => 'name', 'choice_value' => 'uuid', // Use UUID instead of ID ]);
Using EntityType with Autocomplete Extension
You can also add autocomplete to existing EntityType fields:
use Symfony\Bridge\Doctrine\Form\Type\EntityType; $builder->add('author', EntityType::class, [ 'class' => User::class, 'autocomplete' => true, // Enable autocomplete 'placeholder' => 'Search for a user...', 'theme' => 'dark', ]);
This approach works with all existing EntityType options and requires minimal changes to existing forms.
Custom Entity Providers
For complex search logic or custom metadata, create a custom provider:
<?php namespace App\Autocomplete\Provider; use App\Entity\User; use App\Repository\UserRepository; use Kerrialnewham\Autocomplete\Provider\Contract\AutocompleteProviderInterface; use Kerrialnewham\Autocomplete\Provider\Contract\ChipProviderInterface; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; #[AutoconfigureTag('autocomplete.provider')] class UserProvider implements AutocompleteProviderInterface, ChipProviderInterface { public function __construct( private readonly UserRepository $userRepository, ) { } public function getName(): string { // Override auto-generated provider return 'entity.App\Entity\User'; } public function search(string $query, int $limit, array $selected): array { // Custom search: email, username, first/last name $qb = $this->userRepository->createQueryBuilder('u') ->where('LOWER(u.username) LIKE :query') ->orWhere('LOWER(u.email) LIKE :query') ->orWhere('LOWER(u.firstName) LIKE :query') ->orWhere('LOWER(u.lastName) LIKE :query') ->setParameter('query', '%' . strtolower($query) . '%') ->setMaxResults($limit); if (!empty($selected)) { $qb->andWhere('u.id NOT IN (:selected)') ->setParameter('selected', $selected); } $users = $qb->getQuery()->getResult(); return array_map(fn(User $u) => [ 'id' => (string) $u->getId(), 'label' => $u->getFullName(), 'meta' => [ 'email' => $u->getEmail(), 'avatar' => $u->getAvatarUrl(), 'role' => $u->getRoleLabel(), ], ], $users); } public function get(string $id): ?array { $user = $this->userRepository->find($id); if (!$user) { return null; } return [ 'id' => (string) $user->getId(), 'label' => $user->getFullName(), 'meta' => [ 'email' => $user->getEmail(), 'avatar' => $user->getAvatarUrl(), 'role' => $user->getRoleLabel(), ], ]; } }
The custom provider will automatically override the auto-generated one thanks to the naming convention (entity.App\Entity\User).
How It Works
Entity to ID Conversion:
- Form field works with entity objects (like standard EntityType)
- Data transformer converts entity ↔ ID for AJAX communication
- Form submission automatically loads entities from database
Automatic Provider Generation:
- Provider created on-the-fly based on entity class
- No manual configuration needed for simple cases
- Custom providers automatically override defaults
Chip Rendering:
- Selected items rendered server-side via
/_autocomplete/{provider}/chip - Full entity data available for rich chip templates
- Supports metadata display (avatars, emails, etc.)
Advanced Usage
Custom Provider with Complex Queries
<?php namespace App\Autocomplete\Provider; use Kerrialnewham\Autocomplete\Provider\Contract\AutocompleteProviderInterface; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; #[AutoconfigureTag('autocomplete.provider')] class ProductProvider implements AutocompleteProviderInterface { public function __construct( private readonly EntityManagerInterface $em, ) { } public function getName(): string { return 'products'; } public function search(string $query, int $limit, array $selected): array { $qb = $this->em->createQueryBuilder(); $qb->select('p') ->from(Product::class, 'p') ->where('p.name LIKE :query OR p.sku LIKE :query') ->andWhere('p.active = true') ->setParameter('query', '%' . $query . '%') ->setMaxResults($limit); if (!empty($selected)) { $qb->andWhere('p.id NOT IN (:selected)') ->setParameter('selected', $selected); } $products = $qb->getQuery()->getResult(); return array_map(function ($product) { return [ 'id' => (string) $product->getId(), 'label' => sprintf('%s (%s)', $product->getName(), $product->getSku()), 'meta' => [ 'price' => $product->getPrice(), 'stock' => $product->getStock(), ], ]; }, $products); } }
Architecture
Server-Side Rendering
Unlike traditional autocomplete libraries that render results with JavaScript, this bundle uses Twig templates to render HTML on the server. This approach provides:
- Complete control over HTML structure and styling
- Better SEO and accessibility
- Consistency with your existing Twig templates
- Type safety with Symfony's templating system
How It Works
- User types in the input field
- Stimulus controller debounces input and sends AJAX request to
/autocomplete/{provider} - Controller fetches results from the provider
- Results are rendered using
_options.html.twig - HTML is returned and injected into the dropdown
- User selects an option
- In multiple mode, a chip is created using
_chip.html.twig
Requirements
- PHP 8.2 or higher
- Symfony 7.0 or higher
- Stimulus Bundle 2.9 or higher
License
This bundle is released under the MIT License.
Contributing
Contributions are welcome! Please submit pull requests or open issues on GitHub.