Creating a Glossary

Terms and concepts are important in my knowledge management understanding. So I needed a dynamic Glossary. It should be easy to create new pages for each term or concept, should be connected all under the same index and listed on that index page, while again being collected under their first letters.
Since I was taking Java Programming classes, I first created a Java Glossary. I used dataview plugin to handle the lists, and gather related pages under those lists, created standart templates for each word/concept when created under a certain folder with the Templater plugin.
And this is my ever growing Java SDET GLOSSARY cloud. I Would like to hear from others creating glossaries?


My Main Glossary Home Page (With Index)


And my individual Glossary Entry (word/concept) Page:

1 Like

Wow, how’d you do this bit?

1 Like

@Meastn please come back and detail how you did this! This is exactly what I’ve been trying to create

1 Like

this glossary home page with index is all handmade?
Do you have something that automatically creates the home page from the list of entries?

1 Like

Did you ever find out how to do this part? I need this exact setup but I’m new to Obsidian so not sure of the game plan on how to keep it a) organized moving forward by following the same style and b) how to create a database like this

1 Like

Can you please share your workflow ?

My Glossary is created with a view lines of Dataview only. It looks like:

	definition AS "Definitions“
FROM #type/term 
WHERE lead != empty

And it also needs a Frontmatter in every note:

- - -
tags: type/term
definition: +++ Term definition goes here +++
- - -

# Term as title

Do you have a more efficient solution?


I don’t know if it’s more efficient but my definition template is:

  - vocabulary
type: term
definition: null
collapse: open 
**Created**: `=dateformat(this.file.ctime, "DDDD, HH:mm")` 
**Modified**: `=dateformat(this.file.mtime, "DDDD, HH:mm")` 
**Location**: `=this.file.path`

and my my dataview script is:

TABLE definition
FROM #vocabulary 
WHERE file.folder != "400 TEMPLATES"

I also use the Auto Note Mover plugin that looks for the tag vocabulary and it automoves those in to my vocab folder. It seems to work for me. Today anyway, I tend to keep fixing things until I break them.


Even tho the last reply is a bit older, I felt like sharing my solution in case anyone stumbles across this thread.
I created a DataviewJs Script inspired by Meastn’s Design. The difference between the Dataview DQL Queries provided by @Edmund and @statmonkey is that my Query includes some parts of the note. This obviously comes with some performance downsides since the note contents have to be fetched. However now I don’t have to write my Definition as metadata but just have it in the note content.

The resulting glossary looks like this: (Theme: Encore, with some custom CSS snippets)

A note for a glossary entry could look like this:

My Frontmatter looks like this:

  - "#glossar"
related: "[[Test Entry]]"
topic: German

The DataviewJs Script I am using might be a bit bloated and could be written simpler, but this is what I am working with. I tried to add enough comments so that it’s clear even for inexperienced JS users.

// Define custom values
const tagName = "#glossar";
const mySectionName = "Definition";

 * Returns a substring of the given markDownstring which will be the content of the
 * given section. Leading and trailing spaces will be trimmed.
 * The markdownstring should include the given sectionName with 1 to 5 (both inclusive)
 * hashtags in front with optional spaces in between. The section with the given
 * sectionName may be the last section in the markDownString.
 * @param markDownString a markdown formatteed string which contains the section to be searched for
 * @param sectionName    the name of the section to be searched for in the markDownString
 * @returns a substring of the markdownstring which is exactly the searched for section content (spaces trimmes)
 * or if the given section is not found: "<span>-</span>"
const getSectionContent = (markDownString, sectionName) => {
    // match between 1 to 5 hashtags and optional spaces before the sectionName
    const sectionPattern = new RegExp("#{1,5}\\s*" + sectionName);
    // match between 1 to 5 hashtags
    const nextSectionPattern = new RegExp("#{1,5}");

    // check if sectionName can be found in the markDownString
    let sectionHeaderLength;
    if (sectionPattern.test(markDownString)) {
        sectionHeaderLength = markDownString.match(sectionPattern)[0].length;
    } else {
        return "<span>-</span> "; // break with custom string since section not found

    const startingIndex =;
    // search for a next section starting after the found sectionName
    const endingIndex = markDownString.substring(startingIndex + sectionHeaderLength)

    // Check if a next section exists or end of string has been reached
    if (endingIndex == -1) {
        return markDownString.substring(startingIndex + sectionHeaderLength).trim();
    } else {
        return markDownString.substring(
            startingIndex + sectionHeaderLength, // remove the section header
            startingIndex + sectionHeaderLength + endingIndex // add starting point since endingIndex is determined of substring

// == Render a table for each starting character ==
// group all notes with tag #glossar by the first character of the note file name
const groupedTableEntries = dv.pages(tagName).groupBy(note =>;
for (let group of groupedTableEntries) {

    const tableEntries = await Promise.all(
        group.rows // all entries for the current group aka current table
            .map(async (note) => {
                const content = await; // load the note content
                // map pages to custom object which contains all needed data
                return {
                    definition: getSectionContent(content, mySectionName), // get the section content
                    topic: note.topic,
                    related: note.related

    dv.header(3, group.key); // show the character as the header
    dv.table(["Begriff", "Definition","Thema", "Verwandt"],
        .sort((entryA, entryB) =>
        .map(entry => [, entry.definition, entry.topic, entry.related])

The script is inspired by S-Blu Github Pages, the Dataviews Codeblock Examples and this Post in a Showcase Thread

I’ve not seen this thread, or payed much attention to it, but I do believe using tasks with alternate checkboxes could be a very viable alternative to some of the schemes presented in this thread.

Some example glossary definitions

In my test vault, I’ve used two different syntaxes achieving slightly different goals, so sorry if the resulting output is a little confusing. The one closest matching to the one used in some of this thread could be expressed as:

 - [=] (topic:: German) Glossary [[f43593 glossary tasks]]
    - Bullet points
    - Will be rendered
  - [=] (topic:: File Format) Markdown
    - Markdown is a lightweight markup language for formatted Text. Obsidian uses Markdown for all notes.

Which in the original document shows as:

I’ve also got some entries where I rather use (term:: something) within the task test like in:

- [=] (term:: `while` loops) These are useful to repeat a code block an unknown number of times until some conditions are met.

Which shows as:

The main point being that you can toss in the glossary definitions as you go long in any document of your choice, and they’ll stand out just as much as you want, and you can use inline fields to describe aspects of the terms.

Queries related to these terms

The most basic query could be as simple as:

WHERE status = "="

Which in my test vault shows as:

Note that this result has link back to the definition of the term as part of it being a task query. Very nifty feature of the task queries. In this most basic presentation, I’ve not done any cleanup of the text, which can be rather easily done using FLATTEN ...text... as visual.

Another table based variant, which of course also can be styled to better display longer text if one would want that:

  default(task.topic, task.term) as Topic,
  text as Glossary,
  task.children.text as Definition,
  task.outlinks as Related
WHERE contains(file.tasks.status, "=")
FLATTEN file.tasks as task
FLATTEN regexreplace(regexreplace(task.text, 
  "\[\[.*?\]\]", ""),
  "\([^)]+::[^)]+\)( – )?", "") as text
WHERE task.status = "="

Which with wrapping enabled looks like:

I’ve joined up the topic & term in this case, but for real usage you should choose either style related to term/description or using topic/sublists. This would also clean up the table a little as it now is kind of messy.

Here you can see I’ve cleaned up the text (by removing the links and inline fields), and it shows the two variants combined into one table (sorry for the mess). But you hopefully get the idea, and see how much simpler the queries can be if using tasks for a glossary term instead of singling out notes. And all of this information is readily available in the cache, so no read to start messing with file reading and content searching within those files.

1 Like

@holroy I really like your approach, good use of existing tools and plugins, and works across all pages.
Would you mind sharing the CSS markup you are using for:

  1. The task icon
  2. The column wrapping in the table

Many thanks.