Skip to main content

Key Sequence Snippet Import

BetterTouchTool can bulk-import text-expansion snippets from CSV or JSON files. Each snippet creates a Key Sequence trigger that fires when you type the trigger text and then either pastes replacement text or runs a JavaScript transform.

alt text

Supported Action Types

TypeDescription
Plain text pastePastes a string using the Insert / Type / Paste Custom Text action (action 118).
JavaScript transformRuns a JavaScript function via Transform & Replace Selection With JavaScript (action 284). The script has full access to BTT's JavaScript API (callBTT, variables, shell scripts, etc.).

CSV Format

  • Separator: ; (semicolon). Only the first semicolon on each line is used as delimiter, so semicolons inside JavaScript code are fine.
  • Comments: Lines starting with # are ignored.
  • Empty lines are skipped.

Plain text snippet

trigger_text;replacement text

JavaScript snippet

Prefix the content with JSPASTE::

trigger_text;JSPASTE:javascript code

Configuring the between-key pause

By default the allowed pause between keys is 0.5 s. You can change it for all subsequent snippets by adding a special comment anywhere in the file:

#BTTBetweenTime: 0.5

This applies to every snippet below that line, so you can change it multiple times in a single file:

#BTTBetweenTime: 0.2
fast1;quick snippet
fast2;another quick one

#BTTBetweenTime: 0.8
slow;this one allows slower typing

Example CSV file

# Optional: set the pause between keys (default 0.3s)
#BTTBetweenTime: 0.4

# Text expansions
addr;123 Main Street, Springfield
sig;Best regards, John
brb;Be right back!

# JavaScript snippets
date;JSPASTE:async () => { return new Date().toLocaleDateString(); }
hello;JSPASTE:async () => { return "Hello World!"; }
uuid;JSPASTE:async () => { return crypto.randomUUID(); }

JSON Format

A JSON array of objects. Each object has:

FieldRequiredDescription
triggeryesThe text the user types to activate the snippet.
contentyesThe replacement text or JavaScript code.
typeno"text" (default) or "js".
betweenTimenoAllowed pause between key presses in seconds (default 0.3).

Example JSON file

[
{ "trigger": "addr", "content": "123 Main Street, Springfield" },
{ "trigger": "sig", "content": "Best regards, John", "betweenTime": 0.5 },
{ "trigger": "date", "type": "js", "content": "async () => { return new Date().toLocaleDateString(); }" },
{ "trigger": "hello", "type": "js", "content": "async () => { return \"Hello World!\"; }", "betweenTime": 0.8 }
]

How It Works

When a snippet is imported, BetterTouchTool:

  1. Converts each character of the trigger text into the correct macOS key code (using the current keyboard layout).
  2. Builds the full key sequence structure (key-down events, key-up events, and interleaved mixed events).
  3. Handles uppercase letters automatically by grouping consecutive uppercase characters under a single Shift key press.
  4. Sets deleteAfterwards equal to the trigger length, so the typed trigger text is removed before the replacement is pasted.
  5. Creates a global Key Sequence trigger with the appropriate paste or JavaScript action.

Timing defaults

PropertyValueConfigurable
Pause between keys0.3 sYes - via #BTTBetweenTime: in CSV or "betweenTime" in JSON
Pause before / after0 sNo

JavaScript Snippets

JavaScript snippets use BTT's built-in JavaScript engine. The script must be an async function that returns a string:

async () => {
return "replacement text";
}

Inside the function you have access to the full BTT JavaScript API, for example:

async () => {
// Read a BTT variable
let name = await callBTT("get_string_variable", { variable_name: "myName" });
return `Hello ${name}!`;
}
async () => {
// Run a shell script
let result = await runShellScript({ script: "date '+%Y-%m-%d'" });
return result.trim();
}

See the BTT JavaScript integration docs for the complete API reference.


Espanso YAML Compatibility

BetterTouchTool can import Espanso match files (.yml/.yaml). This makes it easy to migrate existing text expansions or use Espanso-format snippet collections with BTT.

Supported Espanso Features

FeatureBTT SupportNotes
trigger / triggersFullMultiple triggers create separate key sequences
replace (plain text)FullSimple paste action
replace with varsFullGenerates BTT JavaScript that evaluates vars then pastes
htmlFullPasted via NSPasteboardTypeHTML
Multi-line (|, >)Full
Cursor positioning ($|$)FullConverted to move_cursor_left_by_x_after_pasting
propagate_caseFullALL CAPS and Title Case detected
word: truePartialUses longer between-key pause (0.4s)
Date extensionFullstrftime formats converted to JavaScript
Clipboard extensionFullUses BTT's get_clipboard_content()
Shell extensionFullUses BTT's runShellScript()
Script extensionFullRuns via shell with the specified interpreter
Random extensionFullJavaScript Math.random() selection
Echo extensionFullStatic variable
Choice extensionPartialUses BTT's show_simple_json_format_menu()
Form (shorthand)PartialUses prompt() for each field
image_pathNot supported
regex triggersNot supportedKey sequences are keystroke-based
form_fields (advanced)PartialBasic text fields and defaults

Example Espanso File

matches:
# Simple replacement
- trigger: ":addr"
replace: "123 Main Street, Springfield"

# Date variable
- trigger: ":now"
replace: "It's {{mytime}}"
vars:
- name: mytime
type: date
params:
format: "%H:%M"

# Shell command
- trigger: ":ip"
replace: "{{output}}"
vars:
- name: output
type: shell
params:
cmd: "curl -s 'https://api.ipify.org'"

# Clipboard + cursor positioning
- trigger: ":clink"
replace: "<a href='{{clipb}}'>$|$</a>"
vars:
- name: clipb
type: clipboard

# Case propagation
- trigger: ":btw"
replace: "by the way"
propagate_case: true

# Random choice
- trigger: ":quote"
replace: "{{output}}"
vars:
- name: output
type: random
params:
choices:
- "Every moment is a fresh beginning."
- "Whatever you do, do it well."

# Simple form
- trigger: ":meeting"
form: |
Meeting with [[person]] on [[date]] about [[topic]]

How It Works

For simple trigger → replace matches (no variables), BTT creates a plain-text paste action - identical to the CSV/JSON import.

For matches with variables, case propagation, cursor positioning, or HTML, BTT generates a JavaScript function that:

  1. Evaluates each variable in order (date formatting, shell commands, clipboard, etc.)
  2. Substitutes {{varname}} placeholders in the replacement template
  3. Applies case propagation if enabled
  4. Handles $|$ cursor positioning
  5. Calls paste_text() to insert the result

This means the full power of BTT's JavaScript API is available - shell scripts, Apple Shortcuts, BTT variables, and more.


AutoHotkey Hotstring Compatibility

BetterTouchTool can import AutoHotkey hotstring files (.ahk). This lets you reuse existing AHK text expansion scripts on macOS.

Supported AHK Syntax

; Comments start with semicolons

; Basic hotstring: typing "btw" + ending char → "by the way"
::btw::by the way

; Immediate trigger (* = no ending character needed)
:*:addr::123 Main Street

; Don't delete the abbreviation (B0)
:B0:@@::andreas@example.com

; Combined options
:*B0:!!::ALERT

; AHK escape sequences: `n = newline, `t = tab, `s = space
::nl::Line one`nLine two

; Multi-line with continuation section
::letter::
(
Dear Sir or Madam,

Thank you for your inquiry.

Best regards
)

; Case-sensitive (C option)
:C:AFK::Away From Keyboard

; Global default change
#Hotstring B0

Supported Options

OptionEffect in BTT
*Immediate trigger (no ending character) - this is BTT's default behavior
B0Don't delete the typed abbreviation (deleteAfterwards = 0)
B1Delete the abbreviation (default)
CCase-sensitive matching
OOmit ending character - accepted (BTT always omits)
R, T, SAccepted but no effect in BTT

Not Supported

FeatureReason
X option (execute code)AHK scripting is Windows-specific
#HotIf context sensitivityWould need BTT trigger conditions
{Send} commands in replacementsAHK-specific key sending
Ending character customizationBTT uses timing-based detection

Example AHK File

; Common abbreviations
::btw::by the way
::omw::On my way!
::thx::Thank you very much!

; Email shortcuts (immediate trigger)
:*:myemail::andreas@example.com
:*:sig::Best regards, Andreas

; Don't backspace the trigger
:B0:@@::andreas@example.com

; Multi-line template
::letter::
(
Dear Sir or Madam,

Thank you for your inquiry.

Best regards,
Andreas
)