Skip to content

Filter Badges

Filter badges give users an always-visible summary of the active filter model. AdvanceFilterPlugin owns the badge list, refreshes it when filters change, and routes remove and clear actions through the same filtering workflow as the popup and programmatic APIs.

Register AdvanceFilterPlugin, then enable its plugin-owned badge list through the grid property:

import { AdvanceFilterPlugin } from '@revolist/revogrid-pro';
import '@revolist/revogrid-pro/dist/revogrid-pro.css';
grid.plugins = [AdvanceFilterPlugin];
grid.filter = true;
grid.filterBadges = true;

The plugin mounts the list in the grid’s header slot. Set grid.filterBadges = false to remove it. You can also assign an options object instead of true:

grid.filterBadges = {
ariaLabel: 'Order filters',
emptyLabel: 'All orders are visible',
className: 'order-filter-list',
badgeClassName: 'order-filter-badge',
};

The list starts with No active filters by default. Each active condition becomes its own badge; multiple conditions for one column remain separate and include their AND or OR relation in the default label.

Every default badge has a remove button. Removing one condition preserves the remaining conditions and deletes the column entry when its last condition is removed. The badge list stays synchronized even when beforefilterapply is canceled for remote filtering.

The default renderer does not add a Clear all button. To clear filters programmatically, use the active plugin:

const filterPlugin = (await grid.getPlugins()).find(
(plugin) => plugin instanceof AdvanceFilterPlugin,
) as AdvanceFilterPlugin | undefined;
await filterPlugin?.clearFiltering();

A complete custom renderer also receives a clear() action. Its items expose individual remove() actions:

grid.filterBadges = {
render: ({ items, clear }) => {
const region = document.createElement('section');
region.setAttribute('aria-label', 'Active filters');
const list = document.createElement('div');
list.setAttribute('role', 'list');
for (const item of items) {
const entry = document.createElement('div');
entry.setAttribute('role', 'listitem');
const removeButton = document.createElement('button');
removeButton.type = 'button';
removeButton.textContent = `${item.label} — remove`;
removeButton.addEventListener('click', () => void item.remove());
entry.append(removeButton);
list.append(entry);
}
const clearButton = document.createElement('button');
clearButton.type = 'button';
clearButton.textContent = 'Clear all filters';
clearButton.disabled = items.length === 0;
clearButton.addEventListener('click', () => void clear());
region.append(list, clearButton);
return region;
},
};

Default labels use the column name, localized operator name, and a compact value summary. Selection filters report the number of excluded values, ranges show their endpoints, and structured filter types can supply their own compact summaries.

When a condition has a longer explanation, the badge includes an info button. It toggles a plain-text tooltip, exposes aria-controls and aria-expanded, and closes on Escape. Unknown object values are summarized without exposing transport JSON.

Use formatLabel and formatDetails when the default badge structure is correct but its text is application-specific:

grid.filterBadges = {
formatLabel: ({ column, prop, operatorName, filter, index }) => {
const name = column?.name ?? String(prop);
const relation = index ? `${filter.relation?.toUpperCase() ?? 'AND'} ` : '';
return `${name}: ${relation}${operatorName}`;
},
formatDetails: ({ filter }) =>
`Applied value: ${String(filter.value ?? 'none')}`,
removeAriaLabel: (item) => `Remove condition ${item.label}`,
detailsAriaLabel: (item) => `Explain condition ${item.label}`,
};

Both format callbacks receive prop, filter, the condition index, the matching column when available, and operatorName. formatDetails should return an empty string to suppress the info control; returning undefined allows the built-in details to be used.

The default renderer provides these semantics:

  • The root has aria-label="Active filters" and aria-live="polite".
  • An empty list uses role="status"; an active list uses role="list" with role="listitem" badges.
  • Remove buttons default to Remove {label} filter.
  • Details buttons default to Show details for {label}.

Customize the root label with ariaLabel, the empty message with emptyLabel, and the two button labels with removeAriaLabel and detailsAriaLabel. Add classes without replacing the built-in styles through className, badgeClassName, removeButtonClassName, and emptyClassName.

The default shell owns these semantics. A complete render callback replaces the list and releases its managed role, so your renderer must provide suitable roles, labels, focus behavior, and controls.

renderBadge replaces only the label content inside the default badge shell. The plugin still renders the list semantics, optional details control, and remove button:

grid.filterBadges = {
renderBadge: ({ item }) => {
const strong = document.createElement('strong');
strong.textContent = item.label;
return strong;
},
};

Use renderEmpty to replace only the content inside the default empty-state element:

grid.filterBadges = {
renderEmpty: () => {
const message = document.createElement('span');
message.textContent = 'No filters — showing every row';
return message;
},
};

renderBadge, renderEmpty, and render can return DOM nodes, a DocumentFragment, numbers, or nested arrays of supported values. Returned strings are always inserted as text, never parsed as HTML:

grid.filterBadges = {
renderBadge: ({ item }) => [item.label, '<em>text, not markup</em>'],
};

To render HTML or Markdown, sanitize it with your application’s renderer and return the resulting DOM nodes or fragment. RevoGrid does not parse renderer strings as markup.

onChange runs after each badge refresh, including the initial render. It receives the current read-only badge items:

grid.filterBadges = {
onChange: (items) => {
activeFilterCount.textContent = String(items.length);
},
};

Each item contains a render key, label, optional details, source filter context, and remove() action. Treat the array and items as read-only; update filtering through the provided actions or the filter plugin APIs.

OptionPurpose
classNameAdds classes to the plugin-owned root.
badgeClassNameAdds classes to each default badge.
removeButtonClassNameAdds classes to each default remove button.
emptyClassNameAdds classes to the default empty-state element.
ariaLabelLabels the live badge region.
emptyLabelReplaces the default No active filters message.
removeAriaLabelProduces the accessible label for a remove button.
detailsAriaLabelProduces the accessible label for a details button.
formatLabelProduces the badge’s plain-text label.
formatDetailsProduces the complete plain-text details explanation.
renderBadgeReplaces label content while retaining the default shell and actions.
renderEmptyReplaces content inside the default empty-state shell.
renderReplaces the complete list; use item.remove() and clear() to retain behavior.
onChangeObserves the current badge items after every refresh.
  • Registering filterBadges without AdvanceFilterPlugin. The property is implemented and managed by that plugin.
  • Expecting a string returned by a render callback to become HTML. Strings are intentionally text-safe; return trusted DOM nodes for rich content.
  • Using full render without wiring item.remove() and clear(). Replacing the complete UI also makes your application responsible for its controls.
  • Assuming the default renderer includes a clear-all action. It provides per-condition removal; add a custom control or call clearFiltering().
  • Omitting accessibility from a full renderer. Unlike renderBadge and renderEmpty, full render replaces the list structure and its managed role.
  • Mutating the items received by onChange. Use the filter APIs so the grid, remote filtering, and badges stay synchronized.
  • Treating badge actions as an editor for every canonical AST. Badges can summarize a grouped AST that cannot project to MultiFilterItem, but per-condition removal is available only for projectable state; update that tree with setFilterAst() or clear it with clearFiltering().