Source Code

<div class="container py-5 d-flex justify-content-center">
  <div class="card bshl-card">
    <div class="card-body p-3">
      <input type="search" class="form-control mb-3" id="bshlInput" placeholder="Search articles...">
      <p class="small text-muted mb-2" id="bshlCount">6 articles</p>
      <ul class="list-unstyled mb-0" id="bshlList"></ul>
    </div>
  </div>
</div>

Bootstrap Search Results Highlighting — Free HTML CSS JS Snippet

Bootstrap Search Results Highlighting · Forms · Plain HTML, CSS & JS · Live preview

What's included

Features

Two independent escaping passes — one for the rendered HTML, one for the regex pattern itself
Special regex characters typed into the search box are treated as literal text, never as pattern syntax
The regex capturing group preserves the original casing of each highlighted match
Case-insensitive matching via the regex "i" flag, matching every occurrence via the "g" flag
A live result count that reflects the current query, distinct from a plain no-filter count

About this UI Snippet

Bootstrap Search Results Highlighting — HTML, CSS & JavaScript

Screenshot of the Bootstrap Search Results Highlighting snippet rendered live

Highlighting a live-typed query safely takes two separate escaping passes, and skipping either one breaks something different. escapeHtml() runs first so a result containing </>/& can never be misinterpreted as markup once it's injected via innerHTML. Separately, escapeRegExp() runs on the *query itself* before it's used to build a RegExp — without it, a user typing a character that's meaningful in regex syntax (like ., (, or *) would either throw a runtime error or silently match in unintended ways, since new RegExp(query) would interpret those characters as pattern syntax instead of literal text to search for.

highlight() builds the match pattern with a capturing group and the i/g flags (new RegExp('(' + escaped + ')', 'ig')), then replaces every match with <mark>$1</mark> — the capturing group is what lets the replacement preserve the original casing of the matched text (searching "grid" still highlights "Grid" with its real capital G intact) rather than replacing it with the lowercase query.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Hand this snippet to an AI coding assistant like Claude and ask it to highlight multiple separate search terms at once (splitting the query on whitespace), or to add fuzzy matching so close-but-not-exact spellings still return and highlight the closest matching substring.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a Bootstrap 5.3 live search with results highlighting, using the real Bootstrap CDN framework (bootstrap.min.css and bootstrap.bundle.min.js), not custom CSS made to resemble it.

Requirements:
- A search input filtering a list of at least 6 sample text items live on every keystroke, case-insensitive substring match.
- Every visible result must have its matching substring wrapped in a real <mark> element, preserving the original text's exact casing.
- The query must be escaped for safe use inside a RegExp before building the highlight pattern, so a user typing a regex-special character (., *, (, etc.) is treated as literal text rather than breaking the search or matching unexpectedly.
- The result text itself must also be HTML-escaped before highlighting, so any special HTML characters in the source text can never be misinterpreted as markup.
- Show a live result count reflecting the current query, and an explicit no-matches message when nothing matches.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Load the snippetAll 6 sample articles show with no highlighting, since no search has been typed yet.
  2. 2
    Type "grid"Only the CSS Grid article remains, and "Grid" inside its title is wrapped in a yellow highlight.
  3. 3
    Clear the input and type "a"Every article containing the letter "a" anywhere shows, each with every occurrence highlighted.
  4. 4
    Type a regex-special character like "(" The search still works correctly as literal text instead of breaking, thanks to the escaped pattern.
  5. 5
    Type something matching nothingThe list shows a plain "No articles match" message instead of an empty highlighted list.

Real-world uses

Common Use Cases

SEARCH
Documentation, help center, and FAQ search
Admin tables and log viewers
Highlighting exactly what matched in each row makes scanning long filtered lists noticeably faster.
Learning safe dynamic regex construction
A compact, realistic example of the escaping mistake ("what if the user types a regex special character") that's easy to overlook when building search-as-you-type features.

Got questions?

Frequently Asked Questions

It's treated as literal text to search for, not regex syntax — escapeRegExp() escapes every character with special regex meaning before the query is ever turned into a RegExp, so the search never throws an error or matches unpredictably.

<mark> is the semantically correct HTML element for "text highlighted for reference," carries a sensible default yellow background with zero CSS required, and is understood by assistive technology as a genuine highlight rather than arbitrary styling.

Yes — the regex's capturing group and the $1 in the replacement insert exactly what was matched in the source text, not the (possibly differently-cased) query the user actually typed.

Yes. The escaping and highlight-building logic is framework-agnostic; render the highlighted string via dangerouslySetInnerHTML (React), v-html (Vue), or [innerHTML] (Angular) since it's pre-escaped HTML, or split the matched ranges into an array of text/mark segments for a fully JSX-native render instead.