← Back toMirror a text area

Create your own custom cursor in a text area

Written byPhuoc Nguyen
27 Sep, 2023
Have you ever noticed that vertical blinking line that shows where the next character will go when you're typing? That's called a caret. And did you know you can customize it? It's true!
Customizing the caret can be super useful. For example, in a code editor, changing the shape and color of the caret could help with readability and make it easier to see different types of text. And in a chat app, a personalized cursor could add a fun touch to the user experience. You can even use custom cursors to create cool visual effects on a webpage.
By default, the caret is just a plain vertical line. But with CSS, you can change its shape and color using the `caret-shape` or `caret-color` property. Unfortunately, you can't make it wider or add animations that way.
But don't worry! In this post, we'll show you how to create a custom caret in a text area using JavaScript.

Hiding the default cursor

It's the pretty easy to make the default cursor disappear. All you need to do is set the `caret-color` property to a transparent color. This will make the default cursor look like it's vanished into thin air.
.container__textarea {
caret-color: transparent;

Creating a fake cursor

If you've been following our post on how to calculate cursor coordinates, you know that we've built the mirror element content using three parts: two text nodes representing the text before and after the cursor position, and an empty element representing the cursor.
But here's the cool part: we can use that empty element as a fake cursor! First, we'll add a special CSS class for it:
const pre = document.createTextNode(textBeforeCursor);
const post = document.createTextNode(textAfterCursor);
const caretEle = document.createElement('span');
caretEle.innerHTML = ' ';

mirroredEle.innerHTML = '';
mirroredEle.append(pre, caretEle, post);
Now that we've added the `container__cursor` class to the cursor element, we have full control over its appearance. We can add a background or increase the width, like this:
.container__cursor {
background-color: rgb(15 23 42);
width: 4px;
And voila! With just a few lines of code, we've created a fake cursor in CSS.
To make your fake cursor look more realistic, you can make it blink. There are a few ways to achieve this effect, such as repeatedly changing the background color or opacity.
In this example, we'll use the first approach by defining a keyframe named `blink` using the `@keyframes` rule. This keyframe has three steps: 0%, 50%, and 100%. At 0% and 100%, we set the background color of the cursor to transparent, which makes it disappear. At 50%, we set the background color to a specific color, such as rgb(15 23 42), which makes it visible.
@keyframes blink {
0%, 100% {
background-color: transparent;
50% {
background-color: rgb(15 23 42);
Next, we apply this animation to our cursor element using the `animation` property in CSS. We set its value to `blink`, specify a duration of 1 second, and make it repeat infinitely by setting its iteration count to `infinite`. Feel free to adjust these CSS styles to match your specific needs.
.container__cursor {
animation: blink 1s infinite;
With these styles applied, your custom cursor will now blink at a regular interval, giving it a more realistic appearance.

Moving the fake cursor alongside the real one

Now, let's talk about the final piece of the puzzle: moving the fake cursor in sync with the real one.
Usually, there's no built-in event to detect when users move the cursor inside a text area. However, we can use the `selectionchange` event, which is triggered when users select text on the page, focus on a text area, or move the cursor inside it.
To re-position the cursor, we handle the `selectionchange` event and check whether the text area is currently being focused by comparing the active element (`document.activeElement`) with the text area. If they match, we update the mirrored element, which includes the fake cursor element.
Here's a code snippet to give you an idea of how it works:
const handleSelectionChange = () => {
if (document.activeElement === textarea) {
// Re-update the mirrored element ...

document.addEventListener('selectionchange', handleSelectionChange);
Now let's take a look at our final example of the steps we've been following. You can either click or use the arrow keys to move the cursor, and the fake cursor will instantly move to the position you want it to be.

See also

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 reach out to me on Twitter or 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