If I have an org-mode buffer like that looks like:

* My headline
:ID: id1
* My second headline
:ID: id2

And I export this to HTML with a table-of-contents, the table of contents uses #sec-1 and #sec-2 as the anchor links for each headline.

I would like it to automatically use #ID-id1 and #ID-id2 so that I can change the order of the headlines without changing where the links point to. Is there a way to tell org-mode to export the table of contents using these anchors instead?

EDIT As Jonathan suggested below, I have created a custom function to use :CUSTOM_ID: instead, here is my setup in case others find it useful:

(defun my/org-custom-id-get (&optional pom create prefix)
  "Get the CUSTOM_ID property of the entry at point-or-marker POM.
If POM is nil, refer to the entry at point. If the entry does not
have an CUSTOM_ID, the function returns nil. However, when CREATE
is non nil, create a CUSTOM_ID if none is present already. PREFIX
will be passed through to `org-id-new'. In any case, the
CUSTOM_ID of the entry is returned."
  (org-with-point-at pom
    (let ((id (org-entry-get nil "CUSTOM_ID")))
       ((and id (stringp id) (string-match "\\S-" id))
        (setq id (org-id-new prefix))
        (org-entry-put pom "CUSTOM_ID" id)
        (org-id-add-location id (buffer-file-name (buffer-base-buffer)))

(defun my/org-add-ids-to-headlines-in-file ()
  "Add CUSTOM_ID properties to all headlines in the
current file which do not already have one."
  (org-map-entries (lambda () (my/org-custom-id-get (point) 'create))))

;; automatically add ids to captured headlines
(add-hook 'org-capture-prepare-finalize-hook
          (lambda () (my/org-custom-id-get (point) 'create)))))

Which I can then use the my/org-add-ids-to-headlines-in-file function to add :CUSTOM_ID: properties to each headline in a file.

1 Answer 1


The simplest solution to get this to work would be to substitute your cases of :ID: with :CUSTOM_ID:, those will be used without any further work required.

Both :ID: and :CUSTOM_ID: have targets created at the headline to allow for linking using them (in addition to the sec-# targets).


In ox-html.el there is org-html--format-toc-headline which is used to create the TOC contents for the headline. Relevant portion of the function:

(format "<a href=\"#%s\">%s</a>"
        ;; Label.
         (or (org-element-property :CUSTOM_ID headline)
             (concat "sec-"
                 (mapconcat #'number-to-string headline-number "-"))))
        ;; Body.
         (and (not (org-export-low-level-p headline info))
              (org-export-numbered-headline-p headline info)
              (concat (mapconcat #'number-to-string headline-number ".")
                  ". "))
         (apply (plist-get info :html-format-headline-function)
                todo todo-type priority text tags :section-number nil)))
Using :ID: by changing ox-html

If you change the link portion above from:

         (or (org-element-property :CUSTOM_ID headline)
             (concat "sec-"
                 (mapconcat #'number-to-string headline-number "-"))))


         (or (org-element-property :CUSTOM_ID headline)
             (org-element-property :ID headline)
             (concat "sec-"
                 (mapconcat #'number-to-string headline-number "-"))))

It will use the generated/set IDs.


If a property :CUSTOM_ID: is set, it will be used for the link ID, however an :ID: property will not. Looking elsewhere through org code, :ID: is meant for use by org-internals while :CUSTOM_ID: is meant for the user.

:ID: is used for determining the folder to store attachments in, with org-id it is used to allow crosslinking between multiple org-files with guaranteed unique IDs.

  • This looks like exactly what I'd like, the reason I am using :ID: is because I am using org-id-get-create to create the ID, is there a way to have org-id-get-create generate :CUSTOM_ID: instead of :ID:?
    – Lee H
    Commented Oct 15, 2014 at 12:10
  • Looks like org-id-get hardcodes the ID property when creating a new ID. You would have to create a new function (even if just by duplicating org-id-get and change ID to CUSTOM_ID. Commented Oct 15, 2014 at 12:30

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.