Skip to content
← All writing
Technical4 min read

Building Textend: a native Mac text expander

How I built a Mac text expander, from its snippet library and template previews to overlapping keywords, text insertion, and iCloud sync.

By Dave Sebek

The keyword :sig on four keys expands into a saved email signature reading Best regards, Dave.

Keeping frequently used text close

I built Textend as a native Mac app for saving text and inserting it with a short keyword. An email signature, a repeated reply, or the structure of a command can live in a snippet library. Type the keyword in another app, and Textend replaces it with the saved text.

The library uses SwiftUI and AppKit, with SwiftData for storage. Collections and favorites help organize snippets, and search narrows the list. The editor sits beside a preview so you can see what a template will produce while you’re working on it.

Closing the library window leaves Textend running in the menu bar, where expansion can be paused or resumed. A snippet doesn’t need a keyword if you only want to find it in the library and copy it manually.

Textend's Mac library in Light appearance, with collections, a searchable snippet list, an Email Signature template, and its expanded preview.
01 / The snippet library, editor, and preview in Textend 0.2.1.

Previewing templates

A snippet can contain plain text or placeholders that resolve when it’s expanded. Each named placeholder uses either the clipboard or a formatted date and time. A template with {today}, for example, can insert the current date using a format chosen in the editor.

The preview has its own sample clipboard field. That lets you try a template with different input without changing the Mac’s clipboard. Copy Expanded Text uses the actual clipboard, as does keyword expansion. Both go through the same template resolver as the preview.

Clipboard placeholders also work with copied files, inserting their full paths into the template.

The special {cursor} marker sets where typing should continue after expansion. For a command template, that might be between a pair of quotation marks:

git commit -m "{cursor}"

Textend removes the marker and places the caret between the quotes, ready for you to write the commit message.

Handling overlapping keywords

Keyword matching gets more interesting when a shorter keyword is also the beginning of a longer one. If gc expands to git commit and gco expands to git checkout, replacing gc immediately would prevent you from finishing gco.

Textend waits briefly when an exact match could still become a longer keyword. The default is 250 milliseconds, adjustable in Settings. Finish typing gco before the delay ends, and the longer snippet expands. Pause at gc, and the shorter one expands. A keyword with no longer alternative expands immediately.

With example keywords gc and gco, pausing after gc inserts git commit, while typing o before the delay ends inserts git checkout.
02 / The delay applies when a keyword could still become a longer match.

If you continue typing but don’t complete a longer keyword, Textend leaves the text in place. Backspace, navigation, shortcuts, and app or window changes also cancel the pending match. That prevents a delayed expansion from replacing text after you’ve moved on.

Inserting text into other apps

Textend uses macOS Accessibility permission to monitor typing and insert expansions. Apps differ in how much information they expose about a text field, its contents, and the caret. Some browser editors omit that information or update it after the keyboard event arrives.

The expansion engine follows physical typing in the active app and window, using readable Accessibility information to check the match when it’s available. Tracking typing directly helped make expansion work across those different editors.

Short, single-line snippets are inserted through Unicode keyboard events. Longer text, or text containing line breaks or tabs, goes through the clipboard. Before a temporary paste, Textend saves the clipboard’s contents. It restores them afterward only if nothing else has changed the clipboard in the meantime, so copying something new won’t be undone by the cleanup.

For shorter caret moves, Textend can navigate backward from the end of the inserted text using keyboard events. That lets it place the cursor in editors with limited Accessibility support. Further typing or a focus change cancels a pending caret move.

Syncing the saved library

The library stays on the Mac, with optional iCloud sync through SwiftData and CloudKit. Saved snippets, collections, favorites, enabled states, and placeholder mappings can follow you between Macs. Appearance settings, expansion timing, permissions, clipboard contents, and unsaved drafts stay local.

Editing continues offline. When changes arrive from another Mac, an editor with no unsaved changes reloads the saved record. An editor with a draft keeps it. Saving that draft applies the fields changed locally while retaining remote changes to untouched fields.

The library can also be exported as a JSON file for backup or transfer.

Getting Textend

Textend runs on macOS 14 or later. You can download it and read the guide or browse the MIT-licensed source on GitHub.

  • SwiftUI
  • AppKit
  • SwiftData
  • macOS
  • Interaction design
← Back to writing

Let’s work together

I’m a designer and engineer who works across product, interfaces, and the systems behind them. I’m looking for remote work with room to take ownership and stay hands-on.

Work I’m interested in

  • Design and UX engineering with substantial interface and interaction work.
  • Frontend and product engineering, especially React, developer tools, and AI products.
  • Technical product roles that combine roadmap ownership with building and shipping.

Recently at Draftbit

Nov 2021 to Jun 2026

At Draftbit I worked across product, engineering, and growth. I designed and shipped tools that helped people build mobile apps, managed the product roadmap, and handled release QA, documentation, and product demos.

Have a role or project in mind?