Text filter

A column with no editorType (or editorType: 'text') gets the full text filter operator set. The default operator is contains.

Open the live example: Highlighted search matches (Filtering & Search)

Operators

Operator Label Matches when the cell value…
contains Contains includes the entered text
notContains Not contains does not include the entered text
equals Equals equals the entered text exactly
notEquals Not equals does not equal the entered text
startsWith Starts with begins with the entered text
endsWith Ends with ends with the entered text
regex Regex matches the entered regular expression (case-insensitive)
in In equals any value in the entered list
notIn Not in equals none of the values in the entered list
isBlank Blank is empty / whitespace-only
isNotBlank Not blank has any non-whitespace content

isBlank / isNotBlank take no value; in / notIn take a value list. All comparisons are case- and accent-insensitive (see below). An invalid regex pattern matches nothing (it never throws) and the filter-row input is flagged until the pattern compiles.

Through the column menu

Click the filter icon in the header → pick an operator → type a value → press Enter. The grid filters as you type (with a 150ms debounce).

Through the filter row

The examples on this page run against these rows:

<script lang="ts">
  import { SvGrid, type GridColumns, type SvGridApi } from '@svgrid/grid'

  type Person = {
    id: number
    name: string
    department: string
    city: string
    age: number
    salary: number
  }

  const people: Person[] = [
    { id: 1, name: 'Ada Lovelace',   department: 'Engineering', city: 'London',   age: 36, salary: 142000 },
    { id: 2, name: 'Grace Hopper',   department: 'Engineering', city: 'New York', age: 45, salary: 168000 },
    { id: 3, name: 'Linus Torvalds', department: 'Platform',    city: 'Portland', age: 54, salary: 155000 },
    { id: 4, name: 'Radia Perlman',  department: 'Networking',  city: 'Seattle',  age: 49, salary: 161000 },
    { id: 5, name: 'Barbara Liskov', department: 'Platform',    city: 'Boston',   age: 52, salary: 172000 },
  ]

  const columns: GridColumns<Person> = [
    { field: 'name',       header: 'Name',       width: 190 },
    { field: 'department', header: 'Department', width: 150 },
    { field: 'city',       header: 'City',       width: 130 },
    { field: 'age',        header: 'Age',        width: 80 },
    { field: 'salary',     header: 'Salary',     width: 130, format: { type: 'currency', currency: 'USD' } },
  ]
</script>
<SvGrid {data} {columns} features={features} filterMode="row" />

Each text column shows the operator picker plus a value control. Most operators use a single input; isBlank / isNotBlank hide the input entirely, and in / notIn show a chip input - type a value and press Enter or , to add it, Backspace to remove the last one.

In / Not-in value lists

in and notIn test the cell against a list of whole values (not substrings).

Pick from the column's values. When you focus the in / notIn chip input, a dropdown of the column's distinct values opens beneath it (the same values the set filter checklist uses, with the same match counts, and likewise limited to what the other columns' filters still leave reachable). Typing narrows the list; click a value to toggle it in or out. In the funnel menu and tool panel the plain text input offers the same values as native autocomplete suggestions.

Through the API, pass the list as a newline- or comma-separated string:

api.setFilter('symbol', { operator: 'in',    value: 'TSM\nBP\nBABA' })
api.setFilter('side',   { operator: 'notIn', value: 'Sell' })

Programmatically

api.setFilter('firstName', { operator: 'contains', value: 'ada' })
api.clearFilter('firstName')

Case + accent sensitivity (locale-aware filtering)

All built-in text operators are case AND accent insensitive out of the box. The grid normalises both the query and each cell value with NFD-decompose → strip combining marks (diacritics) → locale-aware lowercase, then runs includes / equals / startsWith.

applyExcelFilter('Café Genève', { id: 'name', operator: 'contains', value: 'cafe geneve' })
// → true

applyExcelFilter('München', { id: 'city', operator: 'startsWith', value: 'munch' })
// → true

Open the live example: Locale-aware text filter (Filtering & Search)

filterLocale prop

For locale-sensitive lowercasing (Turkish dotted-I vs dotless-i, German ß, etc.), thread a BCP-47 tag through filterLocale:

<SvGrid
  data={rows}
  columns={columns}
  features={features}
  filterLocale="tr-TR"
/>

With filterLocale="tr-TR":

Without a locale, String.prototype.toLowerCase() is used (Unicode default casing). 90 % of the time this is fine; the locale prop is for the edge cases.

Re-using the normaliser

The same normalizeForFilter helper that powers the built-in operators is exported, so you can use it in user-land code (e.g. a custom externalFilter pipeline):

import { normalizeForFilter } from '@svgrid/grid'

const filtered = rows.filter((r) =>
  normalizeForFilter(r.name, 'de-DE').includes(
    normalizeForFilter(query, 'de-DE'),
  ),
)

Opting out

If you need case-sensitive comparison, run in externalFilter mode and filter the data yourself before passing it in:

<script lang="ts">
  let needle = $state('')
  const filtered = $derived(
    needle ? rows.filter((r) => r.name.includes(needle)) : rows,
  )
</script>

<SvGrid
  data={filtered}
  columns={columns}
  features={features}
  filterMode="none"
  externalFilter={true}
/>

Try it

filterMode="row" puts a persistent input under every header, which suits a screen people filter constantly. The menu mode hides the same operators behind a header button and keeps the header row compact.

<SvGrid data={people} {columns} filterable filterMode="row" />
<SvGrid data={people} {columns} filterable filterMode="menu" />

See also

Live examples

  • Highlighted search matches - External search input + a custom cell snippet that wraps matched substrings in <mark>. Filters the dataset AND visually flags hits.
  • Locale-aware text filter - Pass filterLocale and the grid normalises text (NFD + diacritic strip + locale-aware lowercase) so "cafe" matches "Café", "tokyo" matches "Tōkyō".

Related articles