Highlighting text interactively is a common feature in modern web applications, especially in educational tools, note-taking apps, and content editors. In this article, we will build an intuitive text highlighting feature using React and RSX, where users can select part of a text and highlight it with a small, smooth pop-up action.
Setting Up the Basic Highlight Feature
First, let's create a simple list item (<li>
) where users can select any portion of the text. Once selected, the text will be highlighted upon action.
We use React hooks like useState
to manage highlights and listen for user selection with the onMouseUp
event.
Full Working Code
Here’s the complete React component that allows you to select part of the text and highlight it with a popup button:
tsx
import React, { useState, useRef } from 'react';
const HighlightableListItem: React.FC = () => {
const [text] = useState('This is a sample text inside the list item. You can select and highlight parts.');
const [highlights, setHighlights] = useState<{ start: number; end: number }[]>([]);
const [showButton, setShowButton] = useState(false);
const [buttonPosition, setButtonPosition] = useState({ top: 0, left: 0 });
const [selectionRange, setSelectionRange] = useState<{ start: number; end: number } | null>(null);
const liRef = useRef<HTMLLIElement>(null);
const handleMouseUp = (e: React.MouseEvent) => {
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) {
setShowButton(false);
return;
}
const range = selection.getRangeAt(0);
const selectedText = selection.toString();
const start = range.startOffset;
const end = range.endOffset;
if (selectedText.length > 0 && range.startContainer === range.endContainer) {
// Only allow selection inside the li
if (liRef.current && liRef.current.contains(range.startContainer)) {
const rect = range.getBoundingClientRect();
setButtonPosition({ top: rect.top - 30 + window.scrollY, left: rect.left + window.scrollX });
setSelectionRange({ start, end });
setShowButton(true);
}
} else {
setShowButton(false);
}
};
const handleHighlight = () => {
if (selectionRange) {
setHighlights([...highlights, selectionRange]);
}
setShowButton(false);
window.getSelection()?.removeAllRanges();
};
const renderHighlightedText = () => {
if (highlights.length === 0) return text;
let elements = [];
let lastIndex = 0;
const sortedHighlights = [...highlights].sort((a, b) => a.start - b.start);
for (const { start, end } of sortedHighlights) {
if (lastIndex < start) {
elements.push(<span key={lastIndex}>{text.slice(lastIndex, start)}</span>);
}
elements.push(
<span key={start} style={{ backgroundColor: 'yellow' }}>
{text.slice(start, end)}
</span>
);
lastIndex = end;
}
if (lastIndex < text.length) {
elements.push(<span key={lastIndex}>{text.slice(lastIndex)}</span>);
}
return elements;
};
return (
<div style={{ position: 'relative', padding: '20px' }}>
<ul>
<li
ref={liRef}
onMouseUp={handleMouseUp}
style={{ userSelect: 'text', cursor: 'text', lineHeight: '1.6' }}
>
{renderHighlightedText()}
</li>
</ul>
{showButton && (
<button
style={{
position: 'absolute',
top: buttonPosition.top,
left: buttonPosition.left,
backgroundColor: '#007bff',
color: 'white',
border: 'none',
borderRadius: '5px',
padding: '5px 10px',
fontSize: '12px',
cursor: 'pointer',
zIndex: 1000,
}}
onClick={handleHighlight}
>
Highlight
</button>
)}
</div>
);
};
export default HighlightableListItem;
How It Works
- The text is displayed inside an
<li>
. - The user selects part of the text.
- A floating "Highlight" button appears near the selection.
- Clicking the button highlights the selected text in yellow.
- The app supports multiple highlights without overwriting previous ones.
Handling Edge Cases
While implementing interactive text highlighting, it’s important to handle:
- Selections crossing multiple elements (limit it within a single
<li>
). - Empty selections (don't show button if no text selected).
- Clearing selections after clicking "Highlight".
The above code addresses these edge cases carefully using window.getSelection()
and verifying the selection context.
Future Improvements
After setting up basic highlighting, you can further enhance it by:
- Offering multiple highlight colors (yellow, green, blue, etc.).
- Adding an undo feature to remove highlights.
- Enabling commenting on highlighted parts.
- Saving highlights to a backend database (for persistent storage).
These improvements can make your app feel even more professional and interactive.
Conclusion
Creating a text highlighting feature in React is easier than it sounds.
With React hooks, simple mouse event listeners, and smart conditional rendering, you can build a beautiful and efficient interactive UX.
Adding features like floating highlight buttons massively improves the overall feel and usability of your React application.