How to implement blinking cursor in editable div elements

I want to build a document editor that works like Google Docs. I noticed they use regular div elements instead of textarea for their editor. The thing that confuses me is how they create the blinking text cursor. When I inspect their page using browser dev tools, I can’t find any cursor image. What technique do they use to make the cursor appear and blink in those div elements?

Cursor positioning becomes a nightmare once you add undo/redo or formatting changes. I hit this building a markdown editor with custom keyboard shortcuts. The native contenteditable cursor works fine until you mess with the DOM programmatically - then it just disappears. I fixed it by saving the cursor offset before any DOM changes, then restoring it with createRange() and setSelectionRange(). Here’s what I learned: browsers track cursor position by text nodes, not where you see it visually. When you split or merge text nodes during formatting, you’ve got to recalculate the offset for the new node structure. Skip this step and users get that annoying jump where their cursor teleports somewhere random while typing.

That blinking cursor comes from the browser’s built-in contenteditable functionality. Set contenteditable=“true” on a div and the browser handles all the cursor rendering and blinking automatically. You won’t find it in dev tools because it’s not a DOM element - the browser draws it directly on the canvas layer above your content. Google Docs and most rich text editors use this native behavior instead of building custom cursors. Why? It’s faster and handles tricky text positioning without extra work. You just need to manage the text content and selection ranges programmatically.

Contenteditable works fine for basic stuff, but once you need cursor sync across multiple users or handle complex document operations at scale, it falls apart.

I hit this exact problem building a collaborative editor. Managing cursor states, selections, and real-time updates manually? Total nightmare.

Game changer was automating the whole cursor sync pipeline. Instead of fighting with Range APIs and manual event handling, I built automated workflows that track cursor positions, broadcast changes to other users, and resolve conflicts when multiple people edit at once.

The automation handles all the tedious stuff - position mapping after text insertions, keeping selections during collaborative edits, syncing cursor states across browser sessions. No more manual DOM manipulation or custom event listeners.

This scales way better than manual cursor handling, especially with features like commenting, suggestions, or version history that need to interact with cursor positions.

You can build this kind of automated document editor system pretty easily with the right tools: https://latenode.com

Contenteditable works for basic stuff, but I’ve found that combining it with custom caret management gives you way more control. On similar projects, I’ve used a hybrid approach - let contenteditable handle the initial cursor behavior, then intercept key events and manage cursor position myself using Range and Selection APIs. You get the native blinking cursor but can control exactly where it goes, especially with complex formatting or embedded objects. The browser handles the visual stuff, but you control the cursor movement programmatically.

I built a similar editor last year and found that Google Docs uses multiple tricks together. Sure, contenteditable handles basic cursor stuff, but they override tons of default behavior for the fancy features. Here’s what clicked for me: the native cursor vanishes whenever you mess with selections programmatically. So they track cursor state through focus events and do their own positioning math. The game-changer was realizing the browser redraws the cursor based on the current selection range. Preserve that range correctly during DOM updates and the cursor stays put exactly where users expect. The blinking? That’s just browser magic. But keeping it in the right spot during complex operations? That’s all about careful selection management.

Honestly, the hardest part isn’t making it blink - it’s tracking cursor position during custom text operations. I’ve watched devs get burned because they forgot to update the selection range after changing content programmatically. Then the cursor starts jumping all over the place.

yea they usually do this with css animations n stuff, like using ::before or ::after. u can animate opacity to get that blinking cursor. plus, those contenteditable divs have a cursor that pops up when u click, so that’s handy!