Skip to main content
Vuetify0 is now a release candidate!
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

toHighlight

Pure transformer that splits text into matched and unmatched chunks. Returns a plain HighlightChunk[] — wrap the call in computed() for reactive recomputation.

Usage

ts
import { computed } from 'vue'
import { toHighlight } from '@vuetify/v0'

const chunks = computed(() =>
  toHighlight(() => props.text, () => props.query, { ignoreCase: true })
)
// chunks.value → [{ text: 'Hello ', match: false }, { text: 'World', match: true }]

Architecture

toHighlight resolves its input through a fixed priority order:

Highlight Resolution

Use controls to zoom and pan. Click outside or press Escape to close.

Highlight Resolution

Reactivity

toHighlight is a pure transformer — it reads each input through toValue once and returns a plain HighlightChunk[]. To make the result track upstream changes, wrap the call in computed() (or any reactive scope). The function itself creates no reactivity.

BehaviorReactiveNotes
Calling toHighlight(text, query)One-shot snapshot at call time
Wrapping in computed(() => toHighlight(...))Re-runs when tracked refs change
Passing refs or getters as argumentstoValue unwraps them on every call
Mutating returned chunksTreat the array as derived; do not mutate
Tip

Reach for plain values, refs, or getters Every input accepts MaybeRefOrGetter<T>. Pass a literal for static input, a Ref for v-model integration, or a getter (() => props.text) for prop-driven reactivity. Wrap the call in computed() when you want the result to update automatically.

Examples

A mail-style search pane that exercises every input shape toHighlight accepts. The search Input.Root drives a single-term query, the saved-filter Toggle.Root chips extend that into a multi-term array, and the Server snippets Switch.Root swaps the body-text path from client-side substring matching to caller-supplied MatchRange[] returned by a mock backend. Every option is a real-world button you’d find on a working inbox, not a “demo mode” toggle.

The MessageRow sub-component owns its toHighlight calls so the parent list stays dumb — pass it the resolved terms array and the serverMode flag and it decides which shape to feed the transformer. Pulling the per-row work into a child also means the same row component drops into any list with a query in scope.

  • Single query — type into the search box with no chips active. terms is a one-element string[]. toHighlight accepts that just as happily as a bare string, so this and “Multiple” share a code path. ignoreCase: true, matchAll: true are the typical defaults for search UIs; drop either flag when stricter behavior is warranted (legal text, identifier lookups).

  • Multiple queries — toggle on one or more saved filters. Each filter chip appends its label to terms, the Input value joins them, and toHighlight searches each term independently. Overlapping or adjacent spans collapse automatically — ['budget', 'budgets'] against “budget vs budgets line item” yields two clean highlights, not four overlapping ones. Derive the array from chips, a tokenizer, an API suggestion list, or a comma-split text input — anything that produces string[].

  • Pre-computed ranges — flip Server snippets on. The body text now ignores query entirely and renders from [start, end] pairs supplied by snippets() in messages.ts — a stand-in for a real search backend (Algolia, Elasticsearch, your own indexer) that returns character offsets alongside matched documents. The whole-word matcher used by snippets() highlights different spans than client-side substring matching, so the visual difference between the two modes is real, not cosmetic. The matched chunks render with an underline so the source of truth is obvious at a glance.

MatchRange is exported as readonly [number, number] where end is exclusive — the same convention as String.prototype.slice. Caller-supplied ranges are sorted and merged before chunking, so unordered or overlapping input is normalized for you.

FileRole
messages.tsRecords plus a snippets() function that fakes a backend’s offset response
MessageRow.vueRenders one row; owns both toHighlight calls (subject + body)
inbox.vueEntry: Input.Root search, Toggle.Root filters, Switch.Root mode
PrioritySourceCondition
1matchesnon-empty array
2querystring or string[]
3No-match fallbackneither provided
Saved filters:
3 of 4 threads Highlighting "search"
MC
Marcus Chen8:15 AM

Re: design review for the search experience

Loved the new typeahead — the highlight on each result row reads much faster than the underlined variant. One nit: the empty-state copy still says "no matches" instead of "no results".

PS
Priya ShahYesterday

Weekly product sync notes

Recap: launched the new search bar, started the onboarding redesign, paused the budget dashboard work pending review. Open questions on the search ranking tweak are tracked in the planning doc.

G
GitHubYesterday

New review request on vuetifyjs/0#222

J-Sek requested your review on the toHighlight transformer PR. The branch adds a pure splitter and updates the create-filter live-search example to drop the v-html highlight path.

Accessibility

Wrap matched chunks in the native <mark> element. It carries the implicit ARIA role mark and is announced by screen readers as highlighted or marked text. No additional ARIA attributes are needed on the wrapper element.

Tip

WCAG 1.4.3 (Contrast — Minimum) applies to highlighted text. Ensure sufficient contrast between the mark background color and the surrounding text.

FAQ

Discord
Need help? Join our community for support and discussions ↗

API Reference

The following API details are for the toHighlight composable.
Was this page helpful?

Ctrl+/