In the world of text editing and programming, templating is asuperpower. It saves keystrokes, reduces errors, and lets youfocus on the bigger picture. For Emacs users, battle-tested packageslike yasnippet and tempel are often the go-to solutions, offeringpowerful features and extensive libraries.But what if you're in the mood for a little crafting? What if you wanta solution that's built right into Emacs, requires no third-partyinstallations, and can be easily copied and pasted into anyconfiguration?The all mighty built-in abbrev-mode is there for all of us Emacsusers! In this post, we'll explore how you can leverage this humble,built-in minor mode to create a powerful, progressive, and deeplypersonal (for good or bad) snippet system.What is Abbrev Mode?At its core, abbrev-mode is a simple expansion system. You define an"abbreviation" (a short string) and an "expansion" (the text it shouldbe replaced with). When the mode is active, it can automaticallyreplace the abbrev with its expansion as you type.However, I prefer a more deliberate approach. Instead of havingabbrevs expand automatically, I like to trigger them manually with thecommand expand-abbrev, which is bound to C-x '. This gives me fullcontrol and avoids unintentional expansions. If you prefer theautomatic method, you can always enable it with M-x abbrev-mode.Level 1: Simple Text ReplacementsLet's start with the basics. The simplest use of abbrevs is to replacea short string with a longer one. This is perfect for things you typeoften, like symbols, emojis, or special characters.Here is a minimal configuration example (full nicer code on the end ofthis post):(use-package abbrev :ensure nil :custom (save-abbrevs nil) :config (define-abbrev-table 'global-abbrev-table'(;; Arrows ("ra" "β") ("la" "β") ("ua" "β") ("da" "β") ;; Emojis for context markers ("todo" "π· TODO:") ("fixme" "π₯ FIXME:") ("note" "π NOTE:") ("hack" "πΎ HACK:") ("smile" "π") ("party" "π") ("up" "βοΈ") ("applause" "π") ("manyapplauses" "ππππππππ") ("heart" "β€οΈ") ;; NerdFonts ("nerdfolder" "ο» ") ("nerdgit" "ξ") ("nerdemacs" "ξ²"))))With these defined, I can type ra and then C-x ' to get a β, orfixme followed by C-x ' to get "π₯ FIXME:". It's simple, fast,and incredibly useful.Level 2: Running FunctionsWhat if you need more than just static text? What if you want toposition the cursor in a specific spot after the expansion? Abbrevscan do that too, by executing an Emacs Lisp function as part of theexpansion.Let's look at a common use case: creating a Markdown code block andplacing the cursor inside it.;; Markdown("cb" "```@\n\n```" (lambda () (search-backward "@") (delete-char 1)));; ORG("ocb" "#+BEGIN_SRC @\n\n#+END_SRC" (lambda () (search-backward "@") (delete-char 1)))When I expand cb, it first inserts the string ΛΛΛ @\n\n\ΛΛΛ. Then, it immediately runs the provided lambda function, whichsearches backward for the temporary @ marker, deletes it, and leavesthe point (the cursor) right where I need it, ready to type. Or mabewe want to do the same for org-mode? No problem, just check ocb.You could also make variations of these examples by adding your mostused language to the template, maybe adding comments, and whatheverelse you need!Level 3: The Big Guns - Interactive TemplatesNow for the really powerful stuff. Simple text replacement is great,and cursor positioning is even better, but what about true,interactive templates with placeholders? With a simple helper function(check for emacs-solo/abbrev--replace-placeholders on the full codeon the bottom of this post), we can make abbrev-mode do just that.The core of this system is a function that searches for placeholderslike ###1###, ###2###, and a special cursor marker ###@###. Itprompts you in the minibuffer for the value of each placeholder and,at the end, places the cursor exactly where you told it to.Here are a few examples that use this function:;; part of the `global-abbrev-table';;;; JS/TS snippets("imp" "import { ###1### } from '###2###';" emacs-solo/abbrev--replace-placeholders)("fn" "function ###1### () {\n ###@### ;\n};" emacs-solo/abbrev--replace-placeholders)("clog" "console.log(\">>> LOG:\", {###@### })" emacs-solo/abbrev--replace-placeholders)("cwarn" "console.warn(\">>> WARN:\", {###@### })" emacs-solo/abbrev--replace-placeholders)("cerr" "console.error(\">>> ERR:\", {###@### })" emacs-solo/abbrev--replace-placeholders)When I expand rfc with C-x ', Emacs first asks me for the value of###1### (the component name). Then it asks for ###2### (theinitial content). It replaces the placeholders with my input andcreates the full component structure in seconds.The Complete ConfigurationAs promised, here is the full use-package declaration that powers thissystem. You can copy and paste this directly into your Emacsconfiguration to get started.(use-package abbrev :ensure nil :custom (save-abbrevs nil) :config (defun emacs-solo/abbrev--replace-placeholders ()"Replace placeholders ###1###, ###2###, ... with minibuffer input.If ###@### is found, remove it and place point there at the end."(let ((cursor-pos nil)) (save-excursion(goto-char (point-min))(let ((loop 0) (values (make-hash-table :test 'equal))) (while (re-search-forward "###\\([0-9]+\\|@\\)###" nil t)(setq loop (1+ loop))(let* ((index (match-string 1)) (start (match-beginning 0)) (end (match-end 0))) (cond ((string= index "@")(setq cursor-pos start)(delete-region start end)) (t(let* ((key (format "###%s###" index)) (val (or (gethash key values)(let ((input (read-string (format "Value for %s: " key)))) (puthash key input values) input)))) (goto-char start) (delete-region start end) (insert val) (goto-char (+ start (length val)))))))))) (when cursor-pos(goto-char cursor-pos)))) (define-abbrev-table 'global-abbrev-table'(;; Arrows ("ra" "β") ("la" "β") ("ua" "β") ("da" "β") ;; Emojis for context markers ("todo" "π· TODO:") ("fixme" "π₯ FIXME:") ("note" "π NOTE:") ("hack" "πΎ HACK:") ("pinch" "π€") ("smile" "π") ("party" "π") ("up" "βοΈ") ("applause" "π") ("manyapplauses" "ππππππππ") ("heart" "β€οΈ") ;; NerdFonts ("nerdfolder" "ο» ") ("nerdgit" "ξ") ("nerdemacs" "ξ²") ;; HTML entities ("nb" " ") ("lt" "") ;; Markdown ("cb" "```@\n\n```" (lambda () (search-backward "@") (delete-char 1))) ;; ORG ("ocb" "#+BEGIN_SRC @\n\n#+END_SRC" (lambda () (search-backward "@") (delete-char 1))) ("oheader" "#+TITLE: ###1###\n#+AUTHOR: ###2###\n#+EMAIL: ###3###\n#+OPTIONS: toc:nil\n" emacs-solo/abbrev--replace-placeholders) ;; JS/TS snippets ("imp" "import { ###1### } from '###2###';" emacs-solo/abbrev--replace-placeholders) ("fn" "function ###1### () {\n ###@### ;\n};" emacs-solo/abbrev--replace-placeholders) ("clog" "console.log(\">>> LOG:\", { ###@### })" emacs-solo/abbrev--replace-placeholders) ("cwarn" "console.warn(\">>> WARN:\", { ###@### })" emacs-solo/abbrev--replace-placeholders) ("cerr" "console.error(\">>> ERR:\", { ###@### })" emacs-solo/abbrev--replace-placeholders) ("afn" "async function() {\n \n}" (lambda () (search-backward "}") (forward-line -1) (end-of-line))) ("ife" "(function() {\n \n})();" (lambda () (search-backward ")();") (forward-line -1) (end-of-line))) ("esdeps" "// eslint-disable-next-line react-hooks/exhaustive-deps" (lambda () (search-backward ")();") (forward-line -1) (end-of-line))) ("eshooks" "// eslint-disable-next-line react-hooks/rules-of-hooks" (lambda () (search-backward ")();") (forward-line -1) (end-of-line))) ;; React/JSX ("rfc" "const ###1### = () => {\n return (\n ###2###\n );\n};" emacs-solo/abbrev--replace-placeholders))))Conclusionabbrev-mode may not be the flashiest tool in the Emacs ecosystem,but it has something special: itβs yours to shape. With just ahandful of lines of Elisp, you can transform it from a humble textexpander into a personal snippet engine thatβs fast, predictable, andtailored to the way you think and write.By starting small, simple replacements and handy symbols and graduallylayering in cursor control, minibuffer-driven placeholders, anddynamic templates, you build a system that grows with yourworkflow. No external dependencies, no syncing snippet collectionsacross machines, no black-box magic. Just pure Emacs, bending to yourwill.Whether you stick with this approach or eventually reach for heaviertools like yasnippet or tempel, I hope this post shows thatsometimes the most powerful solutions are the ones hiding in plainsight. And with a little crafting, abbrev-mode can become one of themost quietly transformative parts of your everyday editing.Happy hacking. And happy expanding!