We switched to a manual approval system for registration on this forum, as this makes it easier to keep bots out of the forum. We'll approve everybody who doesn't look like a bot!


Dear all, I'd like to start a discussion where we can discuss the implementation detail of snippets in Zettlr. As there are some approaches to it, I would like to ignite a discussion on how to actually do it for the use cases Zettlr solves.

Here's my own first draft:

UX side

  1. Add a dialog just as for Custom CSS or the Tags, in the Zettlr or File menu respectively.
  2. In there you can add, remove, and update snippets. The snippets' content is powered by an instance of the main application's CodeMirror with the respective syntax highlighting. The names should be only lowercase letters [a-z0-9] w/ no special chars (makes it easier to reference them during writing and storing them). Have a look at Question 3 for why this would save me a lot of pain in implementing.
  3. In these snippets, you can use an ID variable, because this is obviously something necessary for integrating nicely with the Zettelkasten functionality.
  4. Apart from that, you can use tab-positions that work in the way as in popular code editors (VSCode, Atom, etc.) — these will be indicated by $1, $2, $3, etc. and are highlighted appropriately. These enable you to press Tab to switch to the next position.

Technical side

  1. Everytime a user types something, a plugin checks if it's a colon (:). If it is, a dropdown opens with all snippets the user has created. Once the user picks a selection, the ZettlrEditor will pull in the corresponding snippets' contents. Having a certain character trigger the snippets rather than a fuzzy search within the snippet database will prevent hint-dropdowns popping up everywhere you type, which is a huge distraction.
  2. This content will be parsed to remove all tab-positions, which are then written to an array.
  3. The contents (with potential IDs replaced) will then be added to the cursor position. Now, whenever a user presses TAB, the cursor jumps to the next tab-position, until all tab-positions are removed from the array. Then TAB works normal again.


  1. Anything missing from this initial implementation? Please bear in mind that implementing this feature will indeed take a lot of time initially, with more easier addendums later on, so we shouldn't overload it in 1.5.
  2. Which actions abort going through all the tab-positions, clearing the full array immediately? (Such as using an arrow-key in Atom or VSCode)
  3. Do we save each snippet as a Markdown file in the format <name>.md inside the user folder, or all in one file? This might depend upon the expected sizes of these snippets. The former would make it easy to share the snippets with other users (maybe via this forum?).


  • Hi Hendrik,

    concerning your 3rd question, I spontaneous would opt for separate Markdown files for each snippet.

    But I'd suggest to go for a solution where the user can define one or even multiple directories as locations for the snippet files. Thus sharing and maintaining the snippets would be even possible in networks with shared storage places or at least syncing them between different devices of the same user.

    Keep up the good work!


  • edited November 2019

    Hi Hendrik,

    first of all: good idea.

    I wouldn't take a colon as the trigger key. Some character that is less often used like probably ~

    Q3: seperate md-files,
    I am not sure, what is meant by user folder, but if it is the document folder like a Zettelkasten, then I would prefer that the user may choose the folder where the snippets are saved to. I think it would mess up my document (Zettelkasten) folder. In my situation, I would create a sub-folder in my Zettelkasten, holding the snippets (as I do now with my pictures; these are stored in Media)

  • I wouldn't take a colon as the trigger key.

    Actually, I think maybe we don't even need any trigger character at all, because the responsible plugin should be able to automatically find relevant snippets (but I'd have to investigate more).

    And concerning the snippet folder: The idea was to have a sub-folder in the application data (that is: where the config, the stats, and all the behind-the-scenes-stuff is saved), so not in the documents, b/c snippets are not content, they are part of the config of an installation!

  • Snippet folder: that is fine. I was afraid all snippets have to be in the Zettelkasten ;-)

  • In the meantime: I am using a nifty cross-platform oss tool called espanso for this task:

    • trigger: ":mytrigger"
      replace: "some text and the ID {{mydate}}\nand more text after a line-break"

    • name: mydate
      type: date
      format: "%Y%m%d%H%M%S" # this ist the Zettlr ID format

  • On my Mac I use Keyboad Maestro to effectively do the same thing.

    ,ts[space] is not something I would type normally, so it triggers inserting a formatted timestamp, i.e. 20200306184837[space]--[space] for a quick formmated journal entry. Benefit of KeyboardMaestro is that the same triggers work in any application. I've used TextExpander also.

    I would almost lean to keeping that functionality out of the application since there are are similar applications on all supported platforms. I realize that there might be a cost associated with the applications, so that would be a negative.

    I fully support the concept, just coming from the UNIX philosophy on "one thing done well", it tickles a little.

  • @cta said:
    In the meantime: I am using a nifty cross-platform oss tool called espanso for this task:

    • trigger: ":mytrigger"
      replace: "some text and the ID {{mydate}}\nand more text after a line-break"

    • name: mydate
      type: date
      format: "%Y%m%d%H%M%S" # this ist the Zettlr ID format

    Tested that one out and I'm very impressed. Thank you so much for sharing. :smile:

Sign In or Register to comment.