PHPackages                             kerrialnewham/autocomplete - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. kerrialnewham/autocomplete

ActiveSymfony-bundle[Utility &amp; Helpers](/categories/utility)

kerrialnewham/autocomplete
==========================

v1.2.2(2mo ago)61.3k—6.9%MITPHPPHP ^8.1

Since Feb 12Pushed 2mo agoCompare

[ Source](https://github.com/Kerrialn/autocomplete)[ Packagist](https://packagist.org/packages/kerrialnewham/autocomplete)[ RSS](/packages/kerrialnewham-autocomplete/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (3)Dependencies (15)Versions (6)Used By (0)

Symfony Autocomplete Bundle
===========================

[](#symfony-autocomplete-bundle)

Server-side rendered autocomplete for Symfony. No Tom Select, no build step. Twig templates + Stimulus. Highly customizable, just override the twig template.

Installation
------------

[](#installation)

```
composer require kerrialnewham/autocomplete
```

Add routes to `config/routes.yaml`:

```
autocomplete:
    resource: '@AutocompleteBundle/config/routes.php'
```

or

```
return static function (RoutingConfigurator $routingConfigurator): void {
    $routingConfigurator->import('@AutocompleteBundle/config/routes.php');
}
```

Add the form theme to `config/packages/twig.yaml`:

```
twig:
    form_themes:
        - '@Autocomplete/form/autocomplete_widget.html.twig'
```

or

```
return static function (ContainerConfigurator $containerConfigurator): void {
    $containerConfigurator->extension('twig', [
            'form_themes' => [
                    '@Autocomplete/form/autocomplete_widget.html.twig',
            ],
    ]);
};
```

Include the CSS:

```

```

Requires `APP_SECRET` in your `.env` for HMAC-signed requests.

Demos
-----

[](#demos)

[![ScreenRecording2026-02-12at10 47 36-ezgif com-video-to-gif-converter](https://private-user-images.githubusercontent.com/41723430/548749281-0847d2ba-a545-4f1e-bec0-73fdf068225b.gif?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODMwMDAzMTIsIm5iZiI6MTc4MzAwMDAxMiwicGF0aCI6Ii80MTcyMzQzMC81NDg3NDkyODEtMDg0N2QyYmEtYTU0NS00ZjFlLWJlYzAtNzNmZGYwNjgyMjViLmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA3MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNzAyVDEzNDY1MlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTc5ZjJlNGZiMzJlMWUzYjM5OTJlYWQwZDM4NmYxZTNhNjlkNjczNjJlNWZhZTFmZDVhNTg3MjhkMGNiZWVlMDMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.PsP9-oBTK3bP5PYQe4Ul6GjUVLEtNSbgmu54XtRxI7s)](https://private-user-images.githubusercontent.com/41723430/548749281-0847d2ba-a545-4f1e-bec0-73fdf068225b.gif?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODMwMDAzMTIsIm5iZiI6MTc4MzAwMDAxMiwicGF0aCI6Ii80MTcyMzQzMC81NDg3NDkyODEtMDg0N2QyYmEtYTU0NS00ZjFlLWJlYzAtNzNmZGYwNjgyMjViLmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA3MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNzAyVDEzNDY1MlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTc5ZjJlNGZiMzJlMWUzYjM5OTJlYWQwZDM4NmYxZTNhNjlkNjczNjJlNWZhZTFmZDVhNTg3MjhkMGNiZWVlMDMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.PsP9-oBTK3bP5PYQe4Ul6GjUVLEtNSbgmu54XtRxI7s)[![cards-2-ezgif com-video-to-gif-converter](https://private-user-images.githubusercontent.com/41723430/548749395-37d62029-3c18-4b6c-a6be-1439e6350a1e.gif?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODMwMDAzMTIsIm5iZiI6MTc4MzAwMDAxMiwicGF0aCI6Ii80MTcyMzQzMC81NDg3NDkzOTUtMzdkNjIwMjktM2MxOC00YjZjLWE2YmUtMTQzOWU2MzUwYTFlLmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA3MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNzAyVDEzNDY1MlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWUwZDA0YjczOTg1MjBhMTMwMDVlYjQ5MDEyOGM4YmUyMzVmODFkZDNkZDFhMWVkOGMzYWEzMWFlNTUzYTUwMjcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.tQ5e7kiH6TYTmlZ5-5OLdLIsZo7dPQbT_NpJzRCTWzk)](https://private-user-images.githubusercontent.com/41723430/548749395-37d62029-3c18-4b6c-a6be-1439e6350a1e.gif?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODMwMDAzMTIsIm5iZiI6MTc4MzAwMDAxMiwicGF0aCI6Ii80MTcyMzQzMC81NDg3NDkzOTUtMzdkNjIwMjktM2MxOC00YjZjLWE2YmUtMTQzOWU2MzUwYTFlLmdpZj9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA3MDIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNzAyVDEzNDY1MlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWUwZDA0YjczOTg1MjBhMTMwMDVlYjQ5MDEyOGM4YmUyMzVmODFkZDNkZDFhMWVkOGMzYWEzMWFlNTUzYTUwMjcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRmdpZiJ9.tQ5e7kiH6TYTmlZ5-5OLdLIsZo7dPQbT_NpJzRCTWzk)

Form Types
----------

[](#form-types)

### AutocompleteType

[](#autocompletetype)

```
use Kerrialnewham\Autocomplete\Form\Type\AutocompleteType;

$builder->add('user', AutocompleteType::class, [
    'provider' => UserProvider::class,
    'placeholder' => 'Search for a user...',
]);

$builder->add('tags', AutocompleteType::class, [
    'provider' => TagProvider::class,
    'multiple' => true,
    'theme' => 'dark',
    'min_chars' => 2,
    'debounce' => 500,
    'limit' => 20,
]);
```

OptionTypeDefaultDescription`provider`string`null`Provider FQCN (e.g. `UserProvider::class`)`multiple`bool`false`Multiple selections`placeholder`string`'Search...'`Placeholder text`min_chars`int`1`Min characters before search`debounce`int`300`Debounce in ms`limit`int`10`Max results`theme`string|null`null`Theme name`floating_label`bool|null`null`Bootstrap 5 floating label support`extra_params`array`[]`Extra query parameters passed to the provider`attr`array`[]`HTML attributes### InternationalDialCodeType

[](#internationaldialcodetype)

```
use Kerrialnewham\Autocomplete\Form\Type\InternationalDialCodeType;

$builder->add('dialCode', InternationalDialCodeType::class);
```

Displays flag emoji, country name, and dial code.

### Adding Autocomplete to Existing Types

[](#adding-autocomplete-to-existing-types)

Pass `'autocomplete' => true` to any Symfony choice or entity type. Providers are resolved automatically.

```
$builder->add('author', EntityType::class, [
    'class' => User::class,
    'autocomplete' => true,
]);

$builder->add('status', EnumType::class, [
    'class' => Status::class,
    'autocomplete' => true,
]);

$builder->add('country', CountryType::class, [
    'autocomplete' => true,
    'theme' => 'dark',
]);

$builder->add('language', LanguageType::class, [
    'autocomplete' => true,
]);

$builder->add('currency', CurrencyType::class, [
    'autocomplete' => true,
]);

$builder->add('timezone', TimezoneType::class, [
    'autocomplete' => true,
]);

$builder->add('locale', LocaleType::class, [
    'autocomplete' => true,
]);
```

**Note:** EntityType with `autocomplete: true` does not support the `query_builder` or closure-based `choice_label` options, because closures cannot survive the HTTP boundary between form rendering and the AJAX search request. Use a string `choice_label` (e.g. `'name'`) or create a [custom provider](#custom-provider) instead.

Providers
---------

[](#providers)

Providers are identified by their **fully qualified class name (FQCN)**. The `AutocompleteProviderInterface` has a single `search()` method — no `getName()` required.

### Built-in

[](#built-in)

The built-in Symfony Intl providers are resolved automatically when using the corresponding form types with `autocomplete: true` (CountryType, LanguageType, etc.).

ProviderData`CountryProvider`Countries`LanguageProvider`Languages`LocaleProvider`Locales`CurrencyProvider`Currencies`TimezoneProvider`Timezones`DialCodeProvider`Dial codes with flag emoji### Custom Provider

[](#custom-provider)

Implement `AutocompleteProviderInterface` for search. Add `ChipProviderInterface` for server-side chip rendering in multiple mode.

```
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 search(string $query, int $limit, array $selected): array
    {
        return array_map(fn($user) => [
            'id' => (string) $user->getId(),
            'label' => $user->getFullName(),
            'meta' => ['email' => $user->getEmail()],
        ], $this->userRepository->searchByName($query, $limit, $selected));
    }

    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()],
        ];
    }
}
```

Reference the provider by its class name:

```
$builder->add('user', AutocompleteType::class, [
    'provider' => UserProvider::class,
]);
```

Results must return `id` (required), `label` (required), and `meta` (optional).

### Extra Parameters

[](#extra-parameters)

Use `extra_params` to pass context to your provider via the AJAX request. This is useful for dependent/filtered selects where the provider needs runtime context (e.g. filtering cities by the selected country).

```
$builder->add('city', AutocompleteType::class, [
    'provider' => CityProvider::class,
    'extra_params' => [
        'country' => $country->getId(),
    ],
]);
```

Read them in your provider via `RequestStack`:

```
use Symfony\Component\HttpFoundation\RequestStack;

#[AutoconfigureTag('autocomplete.provider')]
class CityProvider implements AutocompleteProviderInterface
{
    public function __construct(
        private readonly CityRepository $cityRepository,
        private readonly RequestStack $requestStack,
    ) {}

    public function search(string $query, int $limit, array $selected): array
    {
        $request = $this->requestStack->getCurrentRequest();
        $countryId = $request?->query->get('country');

        $qb = $this->cityRepository->createQueryBuilder('c')
            ->leftJoin('c.country', 'country')
            ->orderBy('c.name', 'ASC');

        if ($countryId) {
            $qb->andWhere('country.id = :country')
                ->setParameter('country', $countryId, 'uuid');
        }

        if (!empty($selected)) {
            $qb->andWhere($qb->expr()->notIn('c.id', ':selected'))
                ->setParameter('selected', $selected);
        }

        $cities = $qb->getQuery()->getResult();

        $results = [];
        foreach ($cities as $city) {
            $label = $city->getName();

            if ($query !== '' && !str_contains(mb_strtolower($label), mb_strtolower($query))) {
                continue;
            }

            $results[] = ['id' => (string) $city->getId(), 'label' => $label];
        }

        return array_slice($results, 0, $limit);
    }
}
```

### Overriding Entity Providers

[](#overriding-entity-providers)

When using `EntityType` with `autocomplete: true`, a provider is auto-generated using the entity FQCN as the provider name (e.g. `App\Entity\User`). To override it, register a custom provider under the same name:

```
#[AutoconfigureTag('autocomplete.provider')]
class UserProvider implements AutocompleteProviderInterface, ChipProviderInterface
{
    // ... your custom search/get logic
}
```

Then pass it via the `provider` option:

```
$builder->add('author', EntityType::class, [
    'class' => User::class,
    'provider' => UserProvider::class,
    'autocomplete' => true,
]);
```

Security
--------

[](#security)

All AJAX requests are signed with HMAC-SHA256 using `APP_SECRET`. This happens automatically.

The signature binds the route name, provider, theme, translation domain, choice parameters, locale, user ID, and timestamp. Signatures expire after 10 minutes.

This prevents provider tampering, parameter injection, cross-user replay, and replay attacks. Invalid signatures return `403 Forbidden`.

PhoneNumberType
---------------

[](#phonenumbertype)

Compound form type: dial code autocomplete + phone number text input. Stores as E.164 (e.g. `+447911123456`).

```
use Kerrialnewham\Autocomplete\Form\Type\PhoneNumberType;

$builder->add('phone', PhoneNumberType::class, [
    'theme' => 'flowbite',
]);
```

- `GB` + `7911123456` becomes `+447911123456`
- `+447911123456` becomes dial code `GB`, number `7911123456`
- NANP: longest dial code wins (`+1242...` resolves to Bahamas, not US)

### Doctrine DBAL Type

[](#doctrine-dbal-type)

Registers automatically when Doctrine DBAL is available.

```
#[ORM\Column(type: 'phone_number', nullable: true)]
private ?string $phone = null;
```

Maps to `VARCHAR(20)`.

Themes
------

[](#themes)

5 built-in themes, all in one CSS file, scoped via `data-autocomplete-theme`:

ThemeDescription`default`Clean, modern, pill-shaped chips`dark`Dark mode`cards`Card layout with metadata (avatar, email, role)`bootstrap-5`Bootstrap 5 compatible`flowbite`Flowbite UI```
$builder->add('country', AutocompleteType::class, [
    'provider' => CountryProvider::class,
    'theme' => 'dark',
]);
```

### Floating Labels (Bootstrap 5)

[](#floating-labels-bootstrap-5)

The `bootstrap-5` theme supports Bootstrap 5 floating labels. Set `floating_label` to `true`:

```
$builder->add('country', CountryType::class, [
    'autocomplete' => true,
    'theme' => 'bootstrap-5',
    'floating_label' => true,
]);
```

The label floats up on focus and when the input has content. Without `floating_label`, the label renders above the input as usual.

### Overriding Templates

[](#overriding-templates)

Override any template using [Symfony's bundle template override](https://symfony.com/doc/current/bundles/override.html#templates). Create the file at `templates/bundles/AutocompleteBundle/` with whatever markup you want.

Each theme has three templates:

TemplateOverride pathWidget`templates/bundles/AutocompleteBundle/theme/{theme}/autocomplete.html.twig`Options`templates/bundles/AutocompleteBundle/theme/{theme}/_options.html.twig`Chip`templates/bundles/AutocompleteBundle/theme/{theme}/_chip.html.twig`Example — override the options template:

```
{# templates/bundles/AutocompleteBundle/theme/default/_options.html.twig #}
{% if results is empty %}
    Nothing found
{% else %}

        {% for item in results %}

                {{ item.label }}

        {% endfor %}

{% endif %}
```

Use any HTML, any CSS framework. The only requirement is `data-autocomplete-option-value` / `data-autocomplete-option-label` on options and Stimulus `data-*-target` attributes on chips.

### Overriding CSS

[](#overriding-css)

Target a specific theme:

```
[data-autocomplete-theme="default"] .autocomplete-input {
    border: 2px solid #4f46e5;
}
```

Target all themes:

```
.autocomplete-option:hover {
    background-color: #eef2ff;
}
```

### Custom Theme

[](#custom-theme)

**CSS-only** — the `default` theme HTML is used, your CSS overrides styling:

```
[data-autocomplete-theme="my-theme"] .autocomplete-input {
    background: #667eea;
    color: white;
}
```

```
$builder->add('field', AutocompleteType::class, [
    'provider' => UserProvider::class,
    'theme' => 'my-theme',
]);
```

**Full custom** — create templates at `templates/bundles/AutocompleteBundle/theme/my-theme/` with any markup you want.

### Events

[](#events)

```
document.addEventListener('autocomplete:select', (e) => console.log(e.detail.item));
document.addEventListener('autocomplete:remove', (e) => console.log(e.detail.value));
```

Requirements
------------

[](#requirements)

- PHP 8.1+, Symfony 6.4+, Stimulus Bundle 2.9+
- Doctrine ORM (optional, for entity autocomplete)
- Doctrine DBAL (optional, for phone number column type)

License
-------

[](#license)

MIT

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance86

Actively maintained with recent releases

Popularity26

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~18 days

Total

5

Last Release

68d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/c4eae866619500d24f60960f0ce5a6c67dd3fa4211a463a52c745acfa4b2ace7?d=identicon)[Kerrialn](/maintainers/Kerrialn)

---

Top Contributors

[![Kerrialn](https://avatars.githubusercontent.com/u/41723430?v=4)](https://github.com/Kerrialn "Kerrialn (133 commits)")

---

Tags

autocompletesymfony-uxSymfony Bundlestimulus

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/kerrialnewham-autocomplete/health.svg)

```
[![Health](https://phpackages.com/badges/kerrialnewham-autocomplete/health.svg)](https://phpackages.com/packages/kerrialnewham-autocomplete)
```

###  Alternatives

[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k17.9M388](/packages/easycorp-easyadmin-bundle)[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.5k5.9M738](/packages/sylius-sylius)[2lenet/crudit-bundle

The easy like Crud'it Bundle.

1616.4k14](/packages/2lenet-crudit-bundle)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.1k17.8k](/packages/prestashop-prestashop)[oro/platform

Business Application Platform (BAP)

645143.5k115](/packages/oro-platform)[kimai/kimai

Kimai - Time Tracking

4.8k9.0k1](/packages/kimai-kimai)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
