# Ionic Selectable: a searchable select for Ionic that also edits its own items

> Ionic Selectable replaces the stock Ionic select with a component that can search, group, virtual scroll, and let the user add, edit and delete entries. Version 8 is the standalone Angular rewrite for Ionic 9, and the interesting engineering is in how grouping and virtual scroll share one row stream.

**eakoriakin/ionic-selectable** — Ionic Selectable is an Ionic versatile and highly customizable component that serves as a replacement to Ionic Select, and allows to search items, including async search, create items, customize the layout with templates and much more. It provides an intuitive API and is easy to set up and use.

- Repository: https://github.com/eakoriakin/ionic-selectable
- Stars: 553 · Forks: 131
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/eakoriakin-ionic-selectable

## One component covering search, grouping and editing

Ionic Selectable is an Ionic component similar to Ionic Select that allows you to search items, including async search, infinite scrolling, virtual scrolling and more, and the repository description adds the rest: it serves as a replacement to Ionic Select, allows to search items including async search, create items, customize the layout with templates and much more.

The feature list is the clearest statement of the scope, and it is longer than the summary suggests. Single selection. Multiple selection. Search items. Search items asynchronously. Search by several item fields. Forms. InfiniteScroll. Virtual scroll with group headers. Templates. Grouping items. Editing, adding and deleting items. Disabling items.

That is a lot of behaviour for one input control, and it explains the shape of the API. Almost every one of those is a mode of the same modal rather than a separate component, so the component grows inputs and outputs instead of spawning variants.

The package is published on npm as ionic-selectable, MIT licensed, with the author listed as Evgenii Koriakin. The release history shows the big jump: v8.0.0 on 2026-10-15, v7.0.2 on 2026-08-27, and v5.0.3 back in October 2023. The last push was on 2026-10-15.

## Version 8 is a standalone rewrite for Ionic 9

The compatibility table is the first thing to check, and it maps the component major to the Ionic and Angular versions it supports.

Version 8.x targets Ionic 9 and Angular 21 to 22. Version 7.x targets Ionic 8 and Angular 20 to 22. Version 6.x targets Ionic 7 and Angular 16. Version 4.x targets Ionic 4, and version 3.x targets Ionic 3, both with no Angular version listed.

The 8.x notes explain what changed. The component, the modal and all template directives are standalone, and Ionic is consumed through @ionic/angular, since Ionic 9 removed the @ionic/angular/standalone entry point. It works both in apps bootstrapped with provideIonicAngular() and in apps still using the deprecated IonicModule.forRoot(). If you are still on Ionic 8, the instruction is to use ionic-selectable@7.

The packaging detail underneath that is worth knowing. The package is published in Angular's partial compilation mode, so a build made with Angular 21 links cleanly into Angular 21 and 22 applications. Partial compilation is what lets one library artifact serve several Angular versions, and without it you would be rebuilding the package per Angular major.

There is also a packaging distinction that catches people out. The README says which package you are looking at: this is the Angular component, and there is a separate Stencil-based line on the v6 branch that ships web components for vanilla JS and other frameworks. The instructions below are for the Angular package.

## Installation needs the CDK as well as the component

The install is two commands rather than one, and the reason is a peer dependency.

```bash
npm install ionic-selectable --save
```

Then the Angular CDK, which is a peer dependency because it powers the virtual scroll viewport, so you install it too if your app does not have it already:

```bash
npm install @angular/cdk --save
```

The manifest confirms the peer dependency set: @angular/cdk, @angular/common, @angular/core and @angular/forms all at 21.0.0 or newer, and @ionic/angular at 9.0.0 or newer. That is the concrete version floor for the 8.x line, and it is stricter than the compatibility table's wording, since the table says Angular 21 to 22 while the manifest says at least 21.

The import depends on how the app is structured. Because the component is standalone, you import it directly into whichever component uses it, alongside the Ionic pieces and the forms module:

```ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { IonItem, IonLabel } from '@ionic/angular';
import { IonicSelectableComponent } from 'ionic-selectable';

@Component({
  selector: 'app-home',
  templateUrl: './home.page.html',
  imports: [FormsModule, IonItem, IonLabel, IonicSelectableComponent]
})
export class HomePage { }
```

If you use the template directives, the ionicSelectableItemTemplate and ionicSelectableValueTemplate among them, you import them alongside the component, or you import IonicSelectableModule, which re-exports the component and every directive at once:

```ts
import { IonicSelectableModule } from 'ionic-selectable';

@Component({
  // ...
  imports: [IonicSelectableModule]
})
export class HomePage { }
```

And IonicSelectableModule is also still a valid NgModule import, so existing module-based apps keep working unchanged:

```ts
@NgModule({
  imports: [IonicSelectableModule]
})
export class AppModule { }
```

So the migration path for a version 7 app is to keep the module import, and the path for a new standalone app is to import the component directly.

## The template is where the configuration happens

There is no separate configuration object. The component is configured through inputs in the template, and the two field names are the essential ones.

```html
<ion-item>
  <ion-label>Port</ion-label>
  <ionic-selectable
    [(ngModel)]="port"
    [items]="ports"
    itemValueField="id"
    itemTextField="name"
    [canSearch]="true"
    (onChange)="portChange($event)">
  </ionic-selectable>
</ion-item>
```

itemValueField names the property to use as the value and itemTextField names the property to display, so the same item object can carry an id and a name. The items input takes the array, ngModel binds the selection, canSearch turns on the search field, and onChange emits when the selection changes.

The data on the other side is an ordinary typed array, and the change handler receives the component and the value:

```ts
import { Component } from '@angular/core';
import { IonicSelectableComponent } from 'ionic-selectable';

interface Port {
  id: number;
  name: string;
}

@Component({ /* ... */ })
export class HomePage {
  ports: Port[] = [
    { id: 1, name: 'Tokai' },
    { id: 2, name: 'Vladivostok' },
    { id: 3, name: 'Navlakhi' }
  ];
  port: Port | undefined;

  portChange(event: { component: IonicSelectableComponent; value: any }) {
    console.log('port:', event.value);
  }
}
```

The selection is typed as Port or undefined, so the component does not force you through any. The event type is spelled out with the component and the value, which is what lets a form know which control changed.

## Virtual scroll and grouping share one flattened row stream

The virtual scroll section is where the component does something that is more than a rendering trick.

Set hasVirtualScroll when the list is long enough that rendering every item hurts. The modal then renders the list through an Angular CDK virtual scroll viewport, keeping only the visible rows in the DOM.

```html
<ionic-selectable
  [(ngModel)]="port"
  [items]="ports"
  itemValueField="id"
  itemTextField="name"
  [hasVirtualScroll]="true"
  [canSearch]="true">
</ionic-selectable>
```

Grouping then works with virtual scroll rather than instead of it. Groups are flattened into a single stream of header and item rows, so groupValueField and groupTextField work exactly as they do in a regular list:

```html
<ionic-selectable
  [(ngModel)]="port"
  [items]="ports"
  itemValueField="id"
  itemTextField="name"
  groupValueField="country.id"
  groupTextField="country.name"
  [hasVirtualScroll]="true">
</ionic-selectable>
```

The flattening is the key idea. A virtualised list has to be a flat sequence of rows to measure against a viewport offset, so a grouped list has to be flattened into headers and items before it can be virtualised at all. Doing that inside the component means the same grouping configuration works whether or not virtual scroll is on.

When the items are not grouped with groupValueField, headers can still be inserted with virtualScrollHeaderFn. It receives each item and returns the header text to render before it, or undefined for no header:

```html
<ionic-selectable
  [hasVirtualScroll]="true"
  [virtualScrollHeaderFn]="getGroupText">
</ionic-selectable>
```

```ts
getGroupText(port: Port, index: number, ports: Port[]) {
  if (index === 0 || port.country.id !== ports[index - 1].country.id) {
    return port.country.name;
  }

  return undefined;
}
```

So there are two ways to get headers. The declarative one where the component works out the groups from two field names, and the programmatic one where you write the function and return undefined for rows that should have no header.

## Row height has to be fixed, and that has consequences

Rows are rendered at a fixed height, which defaults to 40px. Both items and group headers are pinned to it, and you change it with virtualScrollApproxItemHeight, which accepts a number of pixels or a px string:

```html
<ionic-selectable [hasVirtualScroll]="true" [virtualScrollApproxItemHeight]="56">
</ionic-selectable>
```

The value is also exposed to CSS as --ionic-selectable-virtual-item-height on the viewport, so the styles can follow the same number.

The fixed height is not a simplification, it is a requirement of the technique. A virtual scroll viewport measures positions against a uniform row height to know what is in view and where to scroll. Rows of different heights would break the offset arithmetic.

That constraint then flows into the content, because the item template has to fit in the space it is given. An item template with a long wrapping title or a two-line description will either be clipped or will need the row height raised for the whole list, which costs vertical space in the modal.

The name of the option is worth noticing too. It is an approximate item height, not a row height, which is the conventional naming in the Angular CDK for the same idea: the value is the estimate the virtualiser uses to compute offsets, and it is exposed to CSS so that the rendered rows and the estimate stay in agreement.

## Conclusion

Adopt Ionic Selectable if you are on Ionic 9 and need a select that users can search, group, and add items to, and you want one component instead of a stock select plus a custom search overlay. Do not adopt it in a vanilla JS or non-Angular project, because the main package is the Angular one and the Stencil line lives on a separate branch. Before you upgrade to 8.x, check the compatibility table, because the peer dependencies require Angular 21 or newer and Ionic 9, and read the note about partial compilation, which is what lets a build made with Angular 21 link into an Angular 22 application.

## FAQ

### How do I install Ionic Selectable?

Run `npm install ionic-selectable --save` and then `npm install @angular/cdk --save`, because the CDK is a peer dependency that powers the virtual scroll viewport. The 8.x line requires Angular 21 or newer and Ionic 9.

### How do I enable virtual scroll in Ionic Selectable?

Set hasVirtualScroll on the component. The modal then renders the list through an Angular CDK virtual scroll viewport, keeping only the visible rows in the DOM. Rows are rendered at a fixed height, 40px by default, and you change it with virtualScrollApproxItemHeight.

### Can I group items and use virtual scroll at the same time?

Yes. Groups are flattened into a single stream of header and item rows so that groupValueField and groupTextField work exactly as they do in a regular list. If you do not group by a field, you can still insert headers with virtualScrollHeaderFn, returning the header text or undefined for no header.

### Is Ionic Selectable available outside Angular?

The main npm package is the Angular component, and the README says the instructions are for the Angular package. There is a separate Stencil-based line on the v6 branch that ships web components for vanilla JS and other frameworks.

## Sources

- [eakoriakin/ionic-selectable on GitHub](https://github.com/eakoriakin/ionic-selectable)
- [Issues](https://github.com/eakoriakin/ionic-selectable/issues)
- [License: MIT](https://github.com/eakoriakin/ionic-selectable/blob/main/LICENSE)
- [README](https://github.com/eakoriakin/ionic-selectable/blob/main/README.md)
- [Releases](https://github.com/eakoriakin/ionic-selectable/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/eakoriakin-ionic-selectable
