I have a basic capture template:

("a" "Article" entry     
(file+headline "~/GitLab/Reports/Bibliography/references.org" "Article")
"* %^{Title} \n%?")

that asks you to fill the Title field.

Now, my question is how to automatically fill this using the title of the original document (the original #+TITLE my title). One must certainly use sexp with something like:

("a" "Article" entry     
(file+headline "~/GitLab/Reports/Bibliography/references.org" "Article")
"* %(...some elisp instructions...) \n%?")

The problem is that I do not find/know a function returning the title.

I mean:

("a" "Article" entry     
(file+headline "~/GitLab/Reports/Bibliography/references.org" "Article")
"* %(org-capture-get :original-file-nondirectory) \n%?")

fills with the original file name, but the equivalent for title:

%(org-capture-get :title)

does not exist.

I also try, from an org-mode file:

#+TITLE: Titi Toto

#+BEGIN_SRC emacs-lisp
(plist-get (org-export-get-environment) ':title)

| Titi Toto |


(plist-get (org-export-get-environment) ':title)

does not work anymore when used in the capture template.

Any help is welcome


2 Answers 2


You need a function that collects the title of the original file (the file visited by the current buffer when org-capture was called). If you have org-collect-keywords (added in b4e91b7e949), you can use:

(defun get-title (file)
  (let (title)
    (when file
          (get-file-buffer file)
        (pcase (org-collect-keywords '("TITLE"))
          (`(("TITLE" . ,val))
           (setq title (car val)))))

Then, in the template, add:

%(get-title (org-capture-get :original-file))

If you have an older version installed, replace (pcase ...) with:

  (goto-char (point-min))
  (when (re-search-forward "^[ \t]*#\\+title:[ \t]*\\(.*\\)$" nil t)
    (setq title (match-string 1))))
  • 1
    I'd simply your elaborate get-title function, removing the title variable and changing the pcase form to just (cadar (org-collect-keywords '("TITLE"))).
    – Omar
    Nov 14, 2020 at 18:23

org-roam has a function that does this here

(defun org-roam--extract-titles-title ()
  "Return title from \"#+TITLE\" of the current buffer."
  (let* ((prop (org-roam--extract-global-props '("TITLE")))
         (title (cdr (assoc "TITLE" prop))))
    (when title
      (list title))))

(defun org-roam--extract-global-props (props)
  "Extract PROPS from the current org buffer.
The search terminates when the first property is encountered."
  (let ((buf (org-element-parse-buffer))
    (dolist (prop props)
      (let ((p (org-element-map buf 'keyword
                 (lambda (kw)
                   (when (string= (org-element-property :key kw) prop)
                     (org-element-property :value kw)))
                 :first-match t)))
        (push (cons prop p) res)))
  • 3
    Please write your answer such that it is still useful in case your link breaks.
    – Stefan
    May 28, 2020 at 3:49

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.