← Back toMirror a text area

Provide a preview of the suggestion as users type in a text area

Written byPhuoc Nguyen
29 Sep, 2023
In our last post, we introduced an autocomplete feature. Today, we'll take it to the next level by adding a preview of the suggestion inline and giving users the ability to insert the suggestion with just a press of the Tab key. This pattern is commonly used in popular email clients, like Gmail.
Get ready to learn how to implement this awesome functionality!

Preparing the mirrored element

In the previous post, we displayed a list of suggestions when the cursor was at the end of the text area. But to show a preview of the suggestion inline without disrupting the current textarea content, we only displayed a single suggestion. To check if the cursor is at the end of the text area, we compared the `selectionStart` property with the length of the text area value.
const currentValue = textarea.value;
const cursorPos = textarea.selectionStart;
if (cursorPos !== currentValue.length) {
// Hide the suggestion if there is ...
To create a mirrored element of the text area, we used three elements: two text nodes representing the text before and after the cursor, and an empty `span` element representing the current caret. But since we can't customize the appearance of a text node, we replaced the text nodes with `span` elements.
The elements before and after the cursor are two `span` elements that represent the text before and after the cursor. We've updated the post-cursor element so that it now displays the first matching suggestion instead of the entire text before the cursor. This way, users can see the suggestion that's coming up next.
To simplify the process, we choose the first suggestion that matches the word being typed. However, it's up to you to decide which suggestion best matches the keyword and implement your own mechanism accordingly.
const currentValue = textarea.value;
const cursorPos = textarea.selectionStart;
const textBeforeCursor = currentValue.substring(0, cursorPos);

const preCursorEle = document.createElement('span');
preCursorEle.textContent = textBeforeCursor;

const postCursorEle = document.createElement('span');

// Fill the element after the cursor with a single suggestion
postCursorEle.textContent = matches[0];

const caretEle = document.createElement('span');
caretEle.innerHTML = ' ';

mirroredEle.innerHTML = '';
mirroredEle.append(preCursorEle, caretEle, postCursorEle);
To style the elements before and after the cursor, we added classes called `container__pre-cursor` and `container__post-cursor`, respectively. We made the text before the cursor invisible by setting the `color` property of the `container__pre-cursor` class to transparent. To make the suggested text stand out from the current content, we made it slightly blurry by setting the `opacity` property of the `container__post-cursor` class.
Here's an example of how these classes could look:
.container__pre-cursor {
color: transparent;
.container__post-cursor {
opacity: 0.4;

Using the Tab key for suggestions

Let's talk about how to use the Tab key to insert suggestions into your text. First, we'll add an event listener for `keydown` events on our text area. This will allow us to check whether the Tab key is pressed by using the `key` property.
Once we've identified that the Tab key has been pressed, we'll check if there's a match from our suggestions list by ensuring that the content of the post element isn't empty. If there is a match, pressing Tab will insert that suggestion into our text area.
Here's some sample code to help illustrate this process:
textarea.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') {
const postCursorEle = mirroredEle.querySelector('.container__post-cursor');
if (postCursorEle.textContent !== '') {
mirroredEle.innerHTML = '';
If all the conditions are met, we use the `preventDefault()` function to prevent the default behavior of the Tab key, which would normally bring users to the next focusable field. Then we use the `replaceCurrentWord()` function to replace the word currently being typed with the suggested word, which is the content of the post-cursor element.
Finally, we reset the content of the mirrored element to empty.
By using the Tab key in this way, you can easily insert suggestions into your text without interrupting your flow of thought. With these changes, we've added inline previews and keyboard accessibility to our autocomplete feature, making it even more user-friendly!
Take a look at the final demo below. To give it a try, simply type any keyword from the list of suggestions provided.
const suggestions = [
"You're the best.",
"I'm humbled and grateful.",
"You knocked me off my feet!",
"My heart is still smiling.",
"Your thoughtfulness is a gift I will always treasure.",
"Sometimes the simplest things mean the most.",
"The banana bread was fabulous. You made my day.",
"I'm touched beyond words.",
"All I can say is wow! (Except, of course, I'm grateful.)",
"My heart just keeps thanking you and thanking you.",
"You're a blessing to me.",
"Thank you for being my angel.",
If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn