How to Set Up Text Replacements That Work in Every App on Mac
macOS has a built-in text replacement system - but it only fires in native AppKit apps. A large share of modern Mac apps are built on Electron, which uses Chromium text fields that bypass macOS text services entirely. Charm fixes this by operating at the CGEventTap level, below all application frameworks, so your replacements fire in every app on your Mac without exception. Here is how to set it up.
Why do built-in text replacements fail in so many apps?
To understand the fix, it helps to understand the problem precisely. macOS text replacement is a feature of Apple's text services stack, which integrates with NSTextView - the text input component used by native AppKit applications. When you type in a Mail compose window, a Notes document, or a Pages page, the keystrokes pass through NSTextView, which checks them against your replacement list and performs substitutions automatically.
The problem begins when the app you are typing in does not use NSTextView. Many of the most popular productivity apps on Mac today are built with Electron - a framework developed by GitHub that packages the Chromium web browser inside a native-looking desktop window. Electron is popular with development teams because it allows them to ship one codebase on Mac, Windows, and Linux simultaneously.
Electron apps render their text input fields using Chromium's own engine. Chromium manages its input pipeline independently of macOS. When you type in an Electron-based text field, the keystrokes go directly into Chromium's input handler - Apple's NSTextView layer never receives them. The result is that every macOS text service - autocorrect, spell check, grammar check, and text replacement - stops working in those apps.
Studies of the Mac App Store download charts show that more than 30% of top productivity apps are Electron-based. For users whose core workflow involves developer tools, certain messaging platforms, or cross-platform productivity suites, this means built-in text replacement is unavailable for a significant portion of their daily work.
This is not a bug you can fix in System Settings. There is no preference to toggle, no permission to grant, and no workaround using native macOS features. The limitation is architectural.
How does Charm make text replacements work everywhere?
Charm uses a different approach: CGEventTap. This is a macOS API that intercepts keyboard and mouse events at the OS kernel event level - before they reach any application. CGEventTap sits below the layer at which applications decide how to handle input. It does not matter whether an app uses AppKit, Electron, Qt, Flutter, or any other framework. Every keystroke passes through the same kernel event pipeline, and CGEventTap intercepts them all at that level.
When Charm is running, it monitors the event stream for trigger abbreviations. When it detects a match - for example, the sequence ;;addr followed by a space - it deletes the trigger characters and types the expansion in their place, before the application has had any chance to process the original input. The application sees only the expanded text. It has no visibility into the substitution that occurred at the kernel level.
This architecture is the reason Charm's replacements fire in every application on your Mac, including apps where every other text service fails. The approach requires macOS Accessibility permission, which grants access to the input interception capabilities used by CGEventTap. All processing happens on-device.
Step-by-step: setting up Charm text replacements
Getting Charm's text replacements running takes about five minutes from download to first expansion.
- Download Charm. Visit theodorehq.com/charm and download the app. Charm requires macOS 14 Sonoma or later.
- Install and open Charm. Open the downloaded file and drag Charm to your Applications folder. Launch it from the Applications folder or using Spotlight (Cmd + Space, then type "Charm").
- Grant Accessibility permission. On first launch, Charm will prompt you to grant Accessibility access in System Settings under Privacy and Security. Click the link in the prompt, find Charm in the Accessibility list, and enable it. This step is required for CGEventTap to function. Without it, Charm cannot intercept keystrokes.
- Open Text Replacements. Click the Charm icon in your menu bar. Navigate to Text Replacements in the Charm interface.
-
Add your first replacement. Click the + button to add a new entry. In the trigger field, type your abbreviation - for example
;;em. In the expansion field, type the full text: your email address. Save the entry. - Test it everywhere. Switch to any app - including Electron-based apps where built-in replacements have never worked - and type your trigger followed by a space. The expansion fires immediately.
Repeat step 5 for each replacement you want to create. There is no limit on the number of entries. The replacements are active as long as Charm is running (it runs in the menu bar and starts automatically at login).
What trigger format works best?
Choosing good triggers prevents accidental expansions - a situation where a replacement fires when you did not intend it to. The safest format is a prefix that never occurs in natural text, followed by a short mnemonic.
The most widely recommended prefix is ;; (double semicolon). This sequence is impossible to type accidentally in normal English writing. Your trigger for an email address would be ;;em, your mailing address would be ;;addr, your phone number would be ;;ph.
Other prefix options that work equally well: ,, (double comma), // (double slash), or a single character you never type in normal prose. The exact prefix does not matter - consistency does. Pick one and use it for all your replacements.
Keep the suffix part short and memorable. The best triggers are ones you can recall without a reference list: two to four characters that obviously relate to the expansion. You should be able to build your mental library of triggers within a few days of use, after which they become as natural as keyboard shortcuts.
;;em for email address, ;;ph for phone number, ;;addr for mailing address, ;;sig for email signature, ;;ty for "Thank you for getting back to me.", ;;rgds for "Kind regards," - these seven replacements cover the highest-frequency repeated text for most knowledge workers.
Frequently asked questions
Why do macOS text replacements not work in every app?
macOS text replacements hook into NSTextView, which is Apple's native text input framework used in AppKit apps. Many modern Mac apps use Electron, which renders text fields using Chromium. Chromium bypasses NSTextView entirely - the replacement event is never triggered. This is a platform limitation, not a configuration problem.
How does Charm make text replacements work in every app?
Charm uses CGEventTap, a macOS API that intercepts keyboard events at the OS kernel level - below all application frameworks. Because it operates before events reach any app, Charm's replacements fire regardless of whether the app uses AppKit, Electron, or any other framework.
Do I need to disable macOS text replacements when using Charm?
No. The two systems operate independently and do not conflict with each other. macOS text replacements still fire in native apps. Charm covers the apps that macOS cannot reach. Running both simultaneously gives you the widest possible coverage with no overlap or interference.
What permission does Charm need to intercept keystrokes?
Charm requires Accessibility permission in System Settings under Privacy and Security. This is standard for Mac apps that use the Accessibility API. macOS prompts for it on first launch. Charm processes all keystrokes on-device - nothing is transmitted to any server.