Note
This is part of the Advanced Customizations.
The default ordering methods sort the notes alphanumerically. For a custom order, one has to include a numerical prefix to each title, making it unnecessarily long. Instead, we can use an extra attribute in the front matter to implicitly define the order we want.
Update: Folders deserve to be sorted as well. There are two schemes for folder ordering:
- Place folders ahead of files as we used to do, then sort folders and files using frontmatter attributes
folderOrder
andnoteOrder
respectively. - Treat folders the same as pages. That is,
folderOrder
will be compared withnoteOrder
when it comes to folder-page sorting.
We place all numbered objects ahead of the rest. Then sort the numbered items based on values in noteOrder
and folderOrder
. Those without an available value will fall back to the alphanumerical order.
Add noteOrder
and folderOrder
To assign a number to a note, simply add a noteOrder
attribute to its frontmatter. But which .md
file should I put the folderOrder
into?
Only the folderOrder
in index.md
inside that folder will be accessed. Based on the guide, it’s suggested to place an index.md
under any folder. Here’s why it’s a good practice:
- You can overwrite the name of a folder using
title
in the frontmatter ofindex.md
. - Separate
folderOrder
andnoteOrder
so that note files will not have folder attributes in it. - Allow further customization for folders, for example, Customizing Collapse Status.
Note
If you choose to hide a folder following the guides in Creating a Secret Garden, remember to also include an
exclusive
tag in theindex.md
.
To sum up, noteOrder
defines the ranking of the note, and folderOrder
defines the ranking of the parent folder among its sibling (notes and) folders.
Add frontmatter
back
Note
After my upgrade on Mar 15, 2025, from my last upgrade on Jan 1, the type of
node
has been changed fromFileNode
inquartz/components/ExplorerNode.tsx
, toFileTrieNode
inquartz/util/fileTrie.ts
.There is no
frontmatter
attribute in the latestnode
structure. Thus, we can manually includefrontmatter
inContentDetails
andlinkIndex.set()
inquartz/plugins/emitters/contentIndex.tsx
.Not sure whether there are any drawbacks. Let me know if you have any comments.
Add our attribute frontmatter
to the existing type ContentDetails
, which defines node.data
.
...
// mod: inherit definition of frontmatter
import { QuartzPluginData } from "../vfile"
export type ContentIndexMap = Map<FullSlug, ContentDetails>
export type ContentDetails = {
slug: FullSlug
filePath: FilePath
title: string
links: SimpleSlug[]
tags: string[]
content: string
richContent?: string
date?: Date
description?: string
// mod: add frontmatter
frontmatter?: QuartzPluginData["frontmatter"]
}
Then set the value properly in linkIndex.set()
.
linkIndex.set(slug, {
slug,
filePath: file.data.filePath!,
title: file.data.frontmatter?.title!,
links: file.data.links ?? [],
tags: file.data.frontmatter?.tags ?? [],
content: file.data.text ?? "",
richContent: opts?.rssFullHtml
? escapeHTML(toHtml(tree as Root, { allowDangerousHtml: true }))
: undefined,
date: date,
description: file.data.description ?? "",
// mod: add the original frontmatter as whole
frontmatter: file.data.frontmatter,
})
Define sortFn
According to the tips, create the Explorer functions as follows. Comment out one of the methods to use another.
Method I is to comply with the sorting traditions in Quartz, where notes must come after folders. Method II is recommended as it provides full control over the order.
// mod: define Explorer functions
import { Options } from "./quartz/components/Explorer"
export const mapFn: Options["mapFn"] = (node) => {
return node
}
export const filterFn: Options["filterFn"] = (node) => {
return node.slugSegment !== "tags"
}
export const sortFn: Options["sortFn"] = (a, b) => {
// mod: sort folders and files based on folderOrder and noteOrder
// to find ways to retrieve folderOrder and noteOrder from frontmatter
// we now have to include frontmatter in ContentDetails and linkIndex.set()
// extract order from frontmatter
const orderA = a.isFolder
? a.data?.frontmatter?.folderOrder as number | undefined
: a.data?.frontmatter?.noteOrder as number | undefined
const orderB = b.isFolder
? b.data?.frontmatter?.folderOrder as number | undefined
: b.data?.frontmatter?.noteOrder as number | undefined
// method I: folders first, then files, sort folders and files separately
// compare orderA and orderB, those undefined will be placed at the end
if ((!a.isFolder && !b.isFolder) || (a.isFolder && b.isFolder)) {
if (orderA !== undefined && orderB !== undefined) {
// compare based on the order
return orderA - orderB;
} else if (orderA !== undefined) {
// move B to the back
return -1;
} else if (orderB !== undefined) {
// move A to the back
return 1;
} else {
// fall back to alphabetical order
return a.displayName.localeCompare(b.displayName);
}
}
// keep folders in front
if (!a.isFolder && b.isFolder) {
return 1
} else {
return -1
}
// method II: sort folders together with files, treat folders as files
// compare orderA and orderB, those undefined will be placed at the end
if (orderA !== undefined && orderB !== undefined) {
return orderA - orderB
} else if (orderA !== undefined) {
return -1
} else if (orderB !== undefined) {
return 1
} else {
return a.displayName.localeCompare(b.displayName)
}
}
Then apply at wherever Explorer
is included.
Component.Explorer({
mapFn,
filterFn,
sortFn,
}),