I've noticed that org-structure-template-alist changed (I'm using org-mode version 9.2) to automatically expand to #+BEGIN_<some block tag> #+END_<some block tag>. I wonder if it's possible to add different kind of templates. For instance a :PROPERTIES:<some properties>:END: template.

Is it possible or should I turn to another package like yasnippets?

2 Answers 2



Haven't noticed that Org Mode 9.2 changed the mechanism of template expansion, where org-structure-template-alist is only for blocks defined by "#+BEGIN_" and "#+END_". And entry like ("p" ":PROPERTIES:?:END:") is no longer accepted.

As mentioned in the above link, other "complex" template can be defined by function tempo-define-template , and org-tempo must be loaded((require 'org-tempo)). Actually the entries of org-structure-template-alist are converted to org-tempo-tags via tempo-define-template by org-tempo, and org-tempo-tags defaults to:

(("<i" . tempo-template-org-index)
 ("<A" . tempo-template-org-ascii)
 ("<H" . tempo-template-org-html)
 ("<L" . tempo-template-org-latex)
 ("<v" . tempo-template-org-verse)
 ("<s" . tempo-template-org-src)
 ("<q" . tempo-template-org-quote)
 ("<l" . tempo-template-org-export-latex)
 ("<h" . tempo-template-org-export-html)
 ("<E" . tempo-template-org-export)
 ("<e" . tempo-template-org-example)
 ("<C" . tempo-template-org-comment)
 ("<c" . tempo-template-org-center)
 ("<a" . tempo-template-org-export-ascii)
 ("<I" . tempo-template-org-include))

For your case, you can define a template by:

(tempo-define-template "my-property"
               '(":PROPERTIES:" p ":END:" >)
               "Insert a property tempate")

Below answer only works for Org mode version prior to 9.2

Yes, you could add an entry to it like this:

(add-to-list 'org-structure-template-alist '("p" ":PROPERTIES:?:END:"))

Then in org file, you type <p and TAB, it will expand to property and leave the point at the position of ?.

And you can find more details in the documentation of the variable by typing C-h v org-structure-template-alist RET.

  • Very helpful answer, thanks. Btw, Is the > symbol on the tempo-define-template a typo? If not.... What is the role of it in the definition?
    – Dox
    Commented Jan 15, 2019 at 12:45
  • 1
    Glad it helps:) Not a typo, it means the line would be indented, tempo-define-template is built-in defun, see the docstring for details.
    – whatacold
    Commented Jan 16, 2019 at 15:21

The frequency at which they introduce incompatible changes in the customization of org-mode is really a pity.

The following code gives you the old structure templates of org-mode prior to version 9.2 back. The function org-complete-expand-structure-template is a pure copy from version 9.1 and org-try-structure-completion is a slightly modified version of that one of 9.1. (I added a type-check there.)

After installing that code you can just use your old template
(add-to-list 'org-structure-template-alist '("p" ":PROPERTIES:?:END:"))

(defvar org-structure-template-alist)

(defun org+-avoid-old-structure-templates (fun &rest args)
  "Call FUN with ARGS with modified `org-structure-template-alist'.
Use a copy of `org-structure-template-alist' with all
old structure templates removed."
  (let ((org-structure-template-alist
      (lambda (template)
        (null (stringp (cdr template))))
    (apply fun args)))

(eval-after-load "org"
  '(when (version<= "9.2" (org-version))
     (defun org-try-structure-completion ()
       "Try to complete a structure template before point.
This looks for strings like \"<e\" on an otherwise empty line and
expands them."
       (let ((l (buffer-substring (point-at-bol) (point)))
     (when (and (looking-at "[ \t]*$")
            (string-match "^[ \t]*<\\([a-zA-Z]+\\)$" l)
            (setq a (assoc (match-string 1 l) org-structure-template-alist))
            (null (stringp (cdr a))))
       (org-complete-expand-structure-template (+ -1 (point-at-bol)
                              (match-beginning 1)) a)

     (defun org-complete-expand-structure-template (start cell)
       "Expand a structure template."
       (let ((rpl (nth 1 cell))
         (ind ""))
     (delete-region start (point))
     (when (string-match "\\`[ \t]*#\\+" rpl)
        ((not (string-match "\\S-" (buffer-substring (point-at-bol) (point))))
         (setq ind (buffer-substring (point-at-bol) (point))))
        (t (newline))))
     (setq start (point))
     (when (string-match "%file" rpl)
       (setq rpl (replace-match
             (abbreviate-file-name (read-file-name "Include file: ")))
              t t rpl)))
     (setq rpl (mapconcat 'identity (split-string rpl "\n")
                  (concat "\n" ind)))
     (insert rpl)
     (when (re-search-backward "\\?" start t) (delete-char 1))))

     (advice-add 'org-tempo-add-templates :around #'org+-avoid-old-structure-templates)

     (add-hook 'org-tab-after-check-for-cycling-hook #'org-try-structure-completion)

     (require 'org-tempo)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.