Craft Collection

Highlighted Text

Last updated: Oct 17, 2026

Splits text on every case-insensitive match of highlightedString and colors the matches, for search results that show why each one matched.

Prerequisites

Created in Next.js with TypeScript. Styled with TailwindCSS.


Note: the query is escaped before it becomes a regular expression. Without that, typing a lone ( throws during render and takes the page down, and . highlights every character.

Preview

  • Full-stack helpdesk with real-time ticket updates (Next.js)
  • Multiplayer drawing game over WebSockets (Socket.IO)
  • Personal finance tracker for iPhone and the web (SwiftUI)

Code

highlighted-text.tsx
tsx
import React from 'react';
 
type HighlightedTextProps = {
  text: string;
  highlightedString: string;
};
 
const escapeRegExp = (value: string) =>
  value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
 
export default function HighlightedText({
  text,
  highlightedString,
}: HighlightedTextProps) {
  if (!highlightedString.trim()) {
    return <span>{text}</span>;
  }
 
  const parts = text.split(
    new RegExp(`(${escapeRegExp(highlightedString)})`, 'gi'),
  );
 
  return (
    <span>
      {parts.map((part, index) =>
        part.toLowerCase() === highlightedString.toLowerCase() ? (
          <span key={index} className="text-orange-700 dark:text-orange-400">
            {part}
          </span>
        ) : (
          part
        ),
      )}
    </span>
  );
}

Splitting on a pattern wrapped in a capture group keeps the matches in the result, so every odd entry of parts is a match and the text keeps its original casing.