React.js: Medium Highlights Tool using Hooks

Diving deeper into this world since 2020, focused on open-source development, writing my experiences and making what people wantDiving deeper into this world since 2020, focused on open-source development, writing my experiences and making what people want
With one simple swipe of the cursor, you can select a word, passage, or paragraph to highlight it.
As you see above, in this article, we'll build the Medium Highlights tool using React Hooks.
Table of content
- Pre-requirements
- Initializing your React App
- Folder structure
- Features
- Defining states
- Selecting text
- Final code
Pre-requirements
| Dependency | Version |
| create-react-app | 4.0.1+ |
| React.js | 17.0.1+ |
| React DOM | 17.0.1+ |
| React Scripts | 4.0.1+ |
Initializing your React App
npx create-react-app quoteh
Feel free to give a name to the app
cd quoteh
Folder structure
src
├── App.js
├── assets
│ ├── Logo.svg
│ └── Skeleton.svg
├── components
│ ├── quote.css
│ └── quote.js
├── global.css
├── index.js
├── reportWebVitals.js
└── setupTests.js
Features
Note: This repository will contain the first part of the project available for making the learning process faster and better.
The app will focus on the following features:
- Select text
- Show popup
- Highlight selected quote
- Share
- Create a dynamic image (canvas)
Defining states
First of all, I always start thinking about what are the required statement variables. Basically, we have:
- X (horizontal selection: number)
- Y (vertical selection → lines: number)
- Selected text (highlighted words: string)
- Popover (open/hide: boolean)
// Selection range initial state: number
const [xRange, setXRange] = useState(0);
const [yRange, setYRange] = useState(0);
// Selected text initial state: string
const [selectedText, setSelectedText] = useState('');
// Popover initial state: false
const [showPopover, setShowPopover] = useState(false);
Besides the statement variables, we also need to see that it is necessary to access the selected text (current value). For this, useRef will help.
const selectedTextRef = useRef(null);
Selecting text
Now, using getSelection API, you are able to access the range of the selected text. Besides that, it's necessary to convert it to a string (using toString) and remove ( trim) the new lines, tabs, etc.
const selection = window.getSelection();
const selectedText = selection.toString().trim();
We also need to create conditions: If the text was selected, show the popover. Otherwise, hide the popover.
if (!selectedText) {
// Hide popover when the text is not selected¹
hidePopover();
return;
}
Before, to continue the conditions, it's necessary to get the:
- Initial selection range (
getRangeAt(0)) - Start position of a node (
startContainer) - Final position of a node (
endContainer) - Current selected text (
current) - Highlightable region (
querySelector)
const selectionRange = selection.getRangeAt(0);
const startNode = selectionRange.startContainer.parentNode;
const endNode = selectionRange.endContainer.parentNode;
const highlightable = selectedTextRef.current;
const highlightableRegion = highlightable.querySelector('.popover');
If the highlightable region doesn't contain a starting/final node, hide the popover.
if (highlightableRegion) {
if (
!highlightableRegion.contains(startNode) ||
!highlightableRegion.contains(endNode)
) {
// Hide popover when the text is not selected¹
hidePopover();
return;
}
} else if (
!highlightable.contains(startNode) ||
!highlightable.contains(endNode)
) {
// Hide popover when the text is not selected¹
hidePopover();
return;
}
We also need to get the size of the element relative to the DOM. If the width is equal to 0, hide the popover.
const { x, y, width } = selectionRange.getBoundingClientRect();
if (!width) {
// `width` is equal to 0
// Hide popover when the text is not selected¹
hidePopover();
return;
}
To finish, set the value to the respective statement variables:
- Both (x & y) ranges will be relative to the DOM
- The text will be selected (
string) - The popover will be visible (
true)
setXRange(x + width / 10);
setYRange(y + window.scrollY - 34);
setSelectedText(selectedText);
setShowPopover(true);
const { onHighlightPop = () => {} } = props;
onHighlightPop(selectedText);
To capture the event, it's necessary to watch it (addEventListener) and clean after done → side effects (removeEventListener)
useEffect(() => {
window.addEventListener('mouseup', onSelectText);
return () => window.removeEventListener('mouseup', onSelectText);
}, [])
Final code

Thank you for reading this article! And if you want to be updated with the best content, subscribe to my newsletter and see you in the next one




