One of my goals for 2020 was to try zettelkasten for managing my notes. I've experimented with various approaches in the past: paper notes in folders (and all over my desk), self-hosted wikis, and custom software like OneNote or Notion.
A few years ago I started using LionWiki to manage a small personal knowledge base. It was somewhat successful, but it didn't quite fit in with my daily workflow. It also required a network connection and browser to be open, and re-organizing notes wasn't quite as quick as I would like. I really like it as a lightweight wiki solution, but for personal notes I wanted something more.
Eventually I switched to deft, an Emacs-based tool for organizing notes that is based on Notational Velocity. This summer I evaluated a number of other zettelkasten tools, but I settled on Zetteldeft as it integrated well with my existing set of notes.
zetteldeft
works alongside deft and adds support for special linking syntax, as
well as a slightly different naming scheme for notes (timestamp followed by the
title).

There are a few things I really like about deft/zetteldeft's approach:
- Text based notes – All of my notes are stored in
org-mode
format on my home computer. deft also supports plain text and markdown formats. - Fast to find notes – With a few keystrokes (
C-c z D
) I can bring up a new search, and the results are narrowed as I type. - Fast to add notes –
C-c z n
brings up a prompt for a new note title. Once that is entered, I'm taken to the note file with the title already filled in. - Linking to other notes is easy –
zetteldeft
supports linking to notes via a special character and the note id, which looks like this:ยง2018-07-09-2115
. With some modifications it can also use a specialzdlink
protocol inorg-mode
. - Fits in with my daily work – If I've found the solution to a problem, I can quickly create a note and paste the solution without having to switch to another application or open a browser tab.
My zetteldeft
configuration
The following code lives in my Emacs init.el
. It sets up deft
and
zetteldeft
, along with a bunch of keyboard shortcuts.
One additional change I made is binding C-c z p
to
sodaware/deft-open-preview
. This allows me to open notes in a preview window
without the list losing keyboard focus.
I use file-truename
when setting my notes path as zetteldeft
had some
problems locating my notes when using a relative path.
(use-package deft :bind (("C-c d" . deft)) :custom ;; Set deft path to full path so that zetteldeft works. (deft-directory (file-truename "~/Documents/notes")) (deft-extensions '("md" "org")) (deft-default-extension "org") (deft-recursive t)) (use-package zetteldeft :bind ("C-c z d" . deft) ("C-c z R" . deft-refresh) ("C-c z D" . zetteldeft-deft-new-search) ("C-c z s" . zetteldeft-search-at-point) ("C-c z c" . zetteldeft-search-current-id) ("C-c z f" . zetteldeft-follow-link) ("C-c z F" . zetteldeft-avy-file-search-ace-window) ("C-c z l" . zetteldeft-avy-link-search) ("C-c z t" . zetteldeft-avy-tag-search) ("C-c z T" . zetteldeft-tag-buffer) ("C-c z i" . zetteldeft-find-file-id-insert) ("C-c z I" . zetteldeft-find-file-full-title-insert) ("C-c z o" . zetteldeft-find-file) ("C-c z n" . zetteldeft-new-file) ("C-c z N" . zetteldeft-new-file-and-link) ("C-c z p" . sodaware/deft-open-preview) ("C-c z r" . zetteldeft-file-rename) ("C-c z x" . zetteldeft-count-words) :config (defun sodaware/deft-open-preview () (interactive) (deft-open-file-other-window)) (font-lock-add-keywords 'org-mode `((,zetteldeft-id-regex . font-lock-warning-face) (,zetteldeft-tag-regex . font-lock-warning-face))))
Additional changes: the zdlink
protocol
This is one of my favourite changes - it adds a new org-mode
protocol,
zdlink
, which allows me to insert an org
-style link to any note via C-c
C-l
. These links can be opened like any other org-mode
link, and because
they only include the note name (not the full path) they work across machines.
This isn't very useful if you're using markdown, but for pure org
it makes
linking and navigating much easier.
;; Add custom `zdlink` to handle zettledeft links. (eval-after-load 'org (lambda () (require 'zetteldeft) (org-link-set-parameters "zdlink" :follow (lambda (str) (zetteldeft--search-filename (zetteldeft--lift-id str))) :complete #'sodaware/zd-complete-link :help-echo "Searches provided ID in Zetteldeft"))) (defun sodaware/zd-complete-link () "Link completion for `zdlink' type links." (let* ((file (completing-read "File to link to: " (deft-find-all-files-no-prefix))) (link (zetteldeft--lift-id file))) (unless link (user-error "No file selected")) (concat "zdlink:" link)))
Additional feature: zetteldeft homepage
Note: This behaviour is now part of zetteldeft core. A home note can be
assigned via the zetteldeft-home-id
variable, and then accessed with M-x
zetteldeft-go-home
.
–
This is a small change that allows me to open a specific note file with a keyboard shortcut. This is useful when keeping a single note that acts as a gateway to all others.
I prefer the main search buffer for finding notes, but I keep one note that lists my main projects and links to their notes.
(defun sodaware/zd-homepage () "Open Zetteldeft home file." (interactive) (zetteldeft-find-file "2020-07-10-0959 Home.org")) (global-set-key (kbd "C-c z h") #'sodaware/zd-homepage)
Additional feature: .dir-locals.el config
This is a really simple .dir-locals.el
file that lives in my main notes
directory. It adds 3 features:
- emojify-mode support – Sometimes I see fancy notion setups and want to
spice up my buffers with some emoji. I don't, but with
emojify-mode
I could. - whitespace-mode - This removes trailing whitespace when I save notes.
- Expands notes on opening – I normally have
org-mode
headlines collapsed as I use it for organizing my todo lists, but for notes I like to see the entire document when I open it.
((org-mode . ((mode . emojify) (mode . whitespace-cleanup) (eval . (outline-show-all)))))
1 Comment
Hi, I stumbled upon your site when browsing the HN discussion on Org 9.4, and love that you share how you use Zetteldeft, which I authored.
I see you made a 'homepage' function, which, coincidentally, is something I was experimenting with as well, a couple of weeks ago. I'll soon include something like that in the package itself. Glad to see that it'll be of use.
Feel free to drop any suggestion or discussion topics in Github!
EFLS