Skip to content
Textfold

How a text expander actually works on macOS

You type four characters and your whole email signature appears. It feels instant, like the app just knows. It doesn't, and the third of a second in between is more interesting than it looks.

By Jeyanth, the developer of Textfold ·

You type ;sig, and your signature appears. In about a third of a second, a text expander listens to every key you press, decides you meant a snippet, deletes what you typed, borrows your clipboard, fakes a ⌘V, and puts your clipboard back before you notice.

I spent the last few months building Textfold, a text expander for Mac. This is what I learned about how that third of a second works, and the parts that are much harder than they look. If you use any text expander, this is also what it can see, and what it does with it.

Step 1: listening to every key

macOS lets an app see keystrokes system-wide through an event tap. Textfold creates one with CGEvent.tapCreate and asks for key presses and mouse clicks. This is why every text expander needs the Input Monitoring permission in System Settings. Without it, the app is deaf.

The tap is listen-only. It can read what you type but can't change or block it, which keeps the app from ever breaking your keyboard if something goes wrong.

Each key you type goes into a small rolling buffer, the last 64 characters at most. It lives only in memory and is never saved. The buffer is thrown away whenever the context changes:

  • You click somewhere, because the caret probably moved.
  • You press an arrow key, Home or End.
  • You use a shortcut like ⌘V or ⌃A, which isn't typing.
  • You switch to another app, which means a different text field.

Backspace removes the last character from the buffer, so fixing a typo doesn't break the match. Dead keys and function keys are ignored without a reset, so typing an accent doesn't wipe what came before.

Step 2: deciding you meant a snippet

After every key, the app checks whether the end of the buffer is one of your abbreviations. There are two ways a snippet can fire, and the difference matters more than it sounds.

Immediately. The snippet expands the moment the last character lands. ;sigworks like this. It's fast, but only safe for abbreviations you'd never type by accident, which is why they usually start with a symbol like ;.

After a word. The snippet waits until you type a space or punctuation, then checks the word before it. This lets you use plain words like addrwithout them firing in the middle of “address”. The check also looks one character further back: if a letter or digit comes right before the abbreviation, it's part of a longer word, so nothing happens. The space you typed is put back after the expansion.

When several abbreviations could match, the longest one wins. That handles most clashes, but immediate mode has a trap. If you have ;tm and ;tmx, the shorter one fires as soon as you type ;tm, and you never reach the x. Textfold's built-in packs avoid this by dropping a pack snippet whenever one of your own abbreviations starts with it. Your snippets always win.

Step 3: swapping the text

Once there's a match, the expander has to replace text inside an app it doesn't control. It can't reach into Mail or Slack and edit the text field. All it can do is pretend to be your keyboard.

  1. Delete the abbreviation. It sends one fake Backspace per character you typed, four for ;sig.
  2. Put the snippet on the clipboard. It saves whatever you had copied first, then places the expanded text there.
  3. Paste. It sends a fake ⌘V.
  4. Move the caret if the snippet has a cursor placeholder, using fake arrow keys.
  5. Restore your clipboard to what it was before.
  1. Delete ;sig

    4 fake Backspaces, a few ms apart

  2. Wait 40 ms

    let the app catch up

  3. Paste

    snippet on the clipboard, fake ⌘V

  4. Wait 250 ms

    let the app read it

  5. Restore clipboard

    what you had copied is back

One expansion, about a third of a second

The waits are what make it reliable: the paste has to land after the Backspaces, and your clipboard can only come back once the app has read the snippet.

Why paste instead of typing the snippet one key at a time? Typing a 300-word reply key by key is slow, it breaks on emoji and accented letters, and some apps auto-indent or autocomplete as you go. A paste lands in one go, keeps line breaks and Unicode intact, and works the same in native apps, Electron apps and terminals. Nearly every serious expander does it this way.

The parts that are harder than they look

Timing. Fake keystrokes aren't instant. Apps process them on their own schedule, and if the paste lands before the last Backspace, you get ;si followed by your signature. If the clipboard comes back before a slow app has read it, the app pastes whatever you had copied before. Textfold waits a few milliseconds between Backspaces, 40 ms before pasting, and 250 ms before restoring the clipboard. Those numbers are conservative on purpose, because Electron apps are the slowest to catch up.

Secure input. When a password field is focused, macOS turns on Secure Input and hides keystrokes from every event tap. Terminal's “Secure Keyboard Entry” and some password managers turn it on too, and sometimes leave it on. No expander can see through it, and it shouldn't. Early on, this just looked like “Textfold randomly stopped working”. Now the menu bar icon says it's paused and why, which saved me a lot of confused emails.

Clipboard managers. Every expansion briefly puts your snippet on the clipboard. Without care, apps like Maccy or Raycast would record each one, including any password a snippet generates. There's a community convention from nspasteboard.org for this: mark the clipboard entry as transient, and well-behaved clipboard managers skip it. Textfold sets that marker on every paste.

Undo. Sometimes a snippet fires when you didn't want it. Textfold lets you press Backspace right after an expansion, within 3 seconds, to undo it and get back exactly what you typed. That means remembering how many characters were inserted and where the caret ended up, then reversing all of it with more fake keys.

What this means for your privacy

A text expander sees nearly everything you type outside password fields: messages, notes, half-written emails. That's simply how the job works, so it's worth knowing what to ask before you install one.

  • Does it need an account? Your snippets don't need a server. An account usually means your snippets, and sometimes more, are stored somewhere else.
  • Does it go online? Ideally only for updates and a license check, never with what you type.
  • Is the typing buffer kept? It should be a short, in-memory record, cleared constantly, never written to disk.
  • Does it mark its pastes as transient? If not, your clipboard history fills up with every snippet.
  • Where do the snippets live? Files you can see and back up are easier to trust than a database only the app can read.

Textfold's answers: no account, no analytics, a 64-character buffer that lives only in memory, and snippets stored as plain .txt files in a folder you choose.

The third of a second, again

So next time ;sig turns into your signature: a listen-only tap saw four keys, a matcher found the longest abbreviation that fit, four fake Backspaces cleared it, your clipboard was borrowed for a ⌘V, and it was handed back about a quarter of a second later. None of it is magic. Most of the work is in the edge cases.

I'm curious: which app has broken your text expander most often? For me it's been the Electron ones. Tell me at hello@textfold.app.