My configuration for org-roam-capture-templates

Introduction

Today I decided to reconfigure org-roam-capture-templates. I haven’t changed this custom variable for months.

My use case

I use org-roam-node-find to search nodes. When a node is not found, I type the name of the new node in the prompt shown by org-roam-node-find and press Enter. In the version of Org Roam that I’m using (version 2.2.2), org-roam-node-find automatically creates a new node when the user press Enter and there’s no selected node.

I don’t create nodes through org-roam-capture.

My previous configuration

When using the configuration in the first code block below, nodes are created as shown in the second code block below.

(setq org-roam-capture-templates
      '(("d"
         "default"
         plain
         "%?"
         :target (file+head "%<%Y_%m_%d_%H_%M_%S_%N_%z>.org" "#+TITLE: ${title}")
         ;; We set immediate-finish to t because we want to switch to the
         ;; buffer that has the new node. If :immediate-finish equals
         ;; nil, a new window is opened for the new node, the name of
         ;; that window has the prefix "CAPTURE-".
         :immediate-finish t)))

:PROPERTIES:
:ID:       fafaeaac-46e8-4b85-a310-ebdad6feaab5
:END:
#+TITLE: Title of a node
<point>

I wanted to store the creation date of each node, so I added a hook to org-roam-capture-new-node-hook.

(defun my-org-roam-set-property-created-top-property-drawer ()
  (save-excursion
    (goto-char (point-min))
    (org-set-property
     "CREATED"
     (format-time-string "[%Y-%m-%d %H:%M:%S %z]"))))

(add-hook 'org-roam-capture-new-node-hook 'my-org-roam-set-property-created-top-property-drawer)

:PROPERTIES:
:ID:       c8e231df-d7b2-402e-9e69-6caaf4e26f23
:CREATED:  [2024-09-20 22:48:26 -0500]
:END:
#+TITLE: Title of a node
<point>

I wanted to remove the spaces in the properties drawer were unnecessary, so I removed them by setting org-property-format as shown in the first code block below. The second code block below shows the result of creating a node.

(setq org-property-format "%s %s")

:PROPERTIES:
:ID: a7e89a71-96b7-419a-aeb1-7dbfea3606b3
:CREATED: [2024-09-20 23:00:58 -0500]
:END:
#+TITLE: Title of a node
<point>

I wanted to separate the properties drawer and the title with an empty line, so I added the hook shown in the first code block below.

(defun my-org-roam-insert-empty-line-above-title ()
  (save-excursion
    (goto-char (point-min))
    (search-forward "#+TITLE:")
    (beginning-of-line)
    (newline)))

(add-hook 'org-roam-capture-new-node-hook 'my-org-roam-insert-empty-line-above-title)

:PROPERTIES:
:ID: 59f88b7d-4da0-45d5-96ee-711210a37e32
:CREATED: [2024-09-20 23:07:49 -0500]
:END:

#+TITLE: Title of a node
<point>

Reason I decided to change my configuration

Let me make something clear: There are two possible formats to define an Org Roam node: (1) using #+TITLE and setting ID in the top property drawer in an Org Mode file (see first code block below) and (2) setting the property ID in a headline (see second code block below).

:PROPERTIES:
:ID: fad0e874-35fb-4548-a08b-7ee232f4bf20
:CREATED: [2024-09-20 23:25:24 -0500]
:END:

#+TITLE: Title of an Org Roam node

* Title of an Org Roam node
:PROPERTIES:
:ID: fad0e874-35fb-4548-a08b-7ee232f4bf20
:CREATED: [2024-09-20 23:25:24 -0500]
:END:

Most of my Org Roam nodes are headlines inside an Org Mode file that has #+TITLE and ID in the top property drawer (the #+TITLE usually is the main subject of all the headlines in that specific Org Mode file). I initially created Org Roam nodes using the format #+TITLE because that was the default configuration.

I have now decided to only use headlines because I usually move nodes between files in org-roam-directory and it is easier to move nodes when they are headlines: I can select an entire subtree using org-mark-element (by default, bound to M-h in org-mode-map).

My current configuration

I had to add the hook my-org-roam-remove-top-property-drawer because Org Roam automatically sets an ID for the top property drawer and it seems that it is not possible to change that behavior. See this thread in the official Org Roam forum.

(setq org-roam-capture-templates
      `(("d"
         "default"
         entry
         ,(string-join
           '("* ${title}"
             ":PROPERTIES:"
             ":ID: %(org-id-new)"
             ":CREATED: %(my-org-verbose-timestamp)"
             ":END:")
           "\n")
         :target (file "%<%Y_%m_%d_%H_%M_%S_%N_%z>.org")
         :immediate-finish t)))

(defun my-org-roam-remove-top-property-drawer ()
  (save-excursion
    (goto-char (point-min))
    (let* ((element (org-element-at-point-no-context))
           (type (org-element-type element)))
      (when (eq type 'property-drawer)
        (delete-region (org-element-begin element) (org-element-end element))))))

(add-hook 'org-roam-capture-new-node-hook 'my-org-roam-remove-top-property-drawer)

Conclusion

My current configuration can’t automatically create nodes with the format #+TITLE. The only reason I would create an Org Roam using #+TITLE is when I need to export the Org Roam to PDF because #+TITLE has a special meaning for the Org Mode PDF exporter. In my current workflow, I hardly ever need to export to PDF. When I need to do it, I will use the default template and manually set #+TITLE. I could define a yasnippet that inserts #+TITLE: to help me do this.

Leave a Reply

Your email address will not be published. Required fields are marked *