Adds Bases formula objects for the bookmarks of the current file.
Developer documentation: README-DEV.md.
file.bookmarksexposes the bookmarks of the current Bases row as a list of values of typebookmark.- Why: Bases has no native way to filter, group or display rows by bookmark membership. This list makes bookmark-based views possible, with per-bookmark operations on top.
- How: use
file.bookmarksin a Bases formula property. It is registered as an instance function onFileValuewhoseselfparameter is not optional, so it is read as a property without parentheses:file.bookmarks. The list is sorted ascending by escaped path and deduplicated by escaped path; each element displays only its own segment (the bookmark title, or the file name when the bookmark has no title), so a bookmark forProjects/Obsidian/Basesdisplays asBases. A file bookmarked in several groups yields one element per bookmark.
- Each element is a
Valueof typebookmark, which can be isolated and queried on its own.- Why: per-bookmark filtering, grouping and display need access to a single bookmark rather than the whole list.
- How: use list operations, for example
file.bookmarks[0]orfile.bookmarks.map(value.folder()), then call bookmark methods on the isolated value.
bookmark.path(),bookmark.fullPath()andbookmark.full_path()return the full escaped path of the bookmark.- Why: display or filter on the complete location of a bookmark inside the bookmark tree.
- How:
file.bookmarks.map(value.path())orfile.bookmarks[0].path(). The group titles and the bookmark's own title are joined with/; a/inside a title is escaped as//. A bookmark titledBasesinside the groupObsidianinside the groupProjectsreturnsProjects/Obsidian/Bases. The three names are aliases and behave identically. A receiver that is not abookmarkreturns the empty string.
bookmark.folder(),bookmark.parent()andbookmark.parentFolder()return the immediate parent segment of the bookmark, ornullwhen the bookmark lies outside every group.- Why: group or filter by the immediate container without parsing the full path.
- How:
file.bookmarks.map(value.folder()). The parent title is escaped in the same way as a path segment. Coerce the result withvalue.folder() ?? ""when a string is required. A receiver that is not abookmarkreturnsnull. The three names are aliases and behave identically.
file.hasBookmarks()returns a boolean that states whether the current file has at least one bookmark.- Why: filter a view down to bookmarked files without materialising and testing the list.
- How:
file.hasBookmarks()in a formula property or in a Bases filter. It returnsfalsewhen the row's file cannot be resolved or when the Bookmarks core plugin is unavailable.
- The plugin reads the Bookmarks core plugin's internal bookmark tree and registers into the Bases formula registry.
- Why: neither the Bookmarks bookmark tree nor the Bases instance-function registry is part of the public
obsidian.d.tstypings. - How: enable the Bookmarks and Bases core plugins in Settings → Core plugins. Registration runs when the workspace layout is ready, which is after Bases is available. If the plugin exposes no
registerInstanceFunc, no formula is registered at all; if a single registration throws, only that function is skipped; if the Bookmarks plugin is missing, every function returns its empty value.
- Why: neither the Bookmarks bookmark tree nor the Bases instance-function registry is part of the public
- Nested bookmark groups are walked recursively, and the titles of every group on the path are collected.
- Why: vaults model hierarchies with nested bookmark groups, and a bookmark's location is the full chain of groups above it.
- How: nothing to configure. Each group contributes its escaped title to the path; the immediate parent is the last collected group.
- Only bookmarks whose target is a vault file are considered.
- Why: a bookmark that does not target a vault file would otherwise produce a false match.
- How: nothing to configure. A bookmark becomes a value only when its
typeisfileand itspathequals the current file path.
- Every returned bookmark renders as a clickable link in the Bases cell, and selecting it locates the bookmark in the Bookmarks view.
- Why: a bookmark that cannot be acted on is only half useful, and a bookmark nested in a collapsed group is otherwise hard to find. The feedback makes it findable without leaving the tree.
- How: select the link. The label is the bookmark's own segment, rendered without underline and without tooltip in
var(--link-color)(var(--link-color-hover)on hover, falling back to the link colour). Selecting it reveals the Bookmarks view, opens every collapsed group on the path, scrolls the bookmark into view and flashes it as hovered/selected using thebookmark-highlightandis-activeclasses for 2.2 seconds. The Bases cell is not highlighted:mousedownandmouseupare cancelled and the cell is blurred. - Retries: the Bookmarks view renders asynchronously, so the lookup is retried up to three times at 350 ms (380 ms after expanding a group) before giving up, and any highlight left by an earlier selection is cleared first.
- Failures are silent: the plugin returns an empty list,
falseornullrather than throwing, and writes nothing to the developer console.- Why: the internal structures it depends on vary between Obsidian versions, and a thrown error would break the whole Bases view.
- How: nothing to configure. A formula that returns an empty list or
nullin a vault known to have bookmarks indicates that an internal structure is missing.
- Obsidian
1.13.7or later, as declared byminAppVersioninmanifest.json. Developed and tested against Obsidian 1.13.7. - Node.js 18 or later and npm, for development only. The continuous integration workflow builds and lints on Node.js 20.x, 22.x and 24.x.
- The Bookmarks and Bases core plugins, both enabled in Settings → Core plugins. Without them the plugin registers no formula functions and the formulas return empty values.
- Install Node.js 18 or later. npm is bundled with it.
- Clone the repository:
git clone https://github.com/LorenBll/Bookmarks-In-Bases.git- Install the development dependencies from the repository root:
npm installNo configuration files, environment variables or credentials are required. The plugin has no user-facing settings.
- Build the plugin and copy
main.js,manifest.jsonandstyles.cssinto<Vault>/.obsidian/plugins/bookmarks-in-bases/; see Build. - Reload Obsidian and enable Bookmarks-In-Bases in Settings → Community plugins.
- Enable the Bookmarks and Bases core plugins in Settings → Core plugins.
- Create or open a Bases view and add a formula property, or a filter, that uses the functions below.
Examples:
file.bookmarks
file.hasBookmarks()
file.bookmarks.map(value.path())
file.bookmarks.map(value.folder())
file.bookmarks[0].path()
file.bookmarks[0].folder()
file.bookmarks.map(value.path()).contains("Projects/Obsidian/Bases")
Behaviour:
file.bookmarksreturns an empty list when the row's file cannot be resolved, when the Bookmarks core plugin is unavailable, or when the file has no file-type bookmarks. Otherwise it returns the bookmarks of that file, sorted ascending by escaped path and deduplicated by escaped path.file.hasBookmarks()returnstruewhen the file has at least one bookmark andfalseotherwise.bookmark.path(),bookmark.fullPath()andbookmark.full_path()return the full escaped path, including the bookmark's own title.bookmark.folder(),bookmark.parent()andbookmark.parentFolder()return the immediate parent segment, ornullwhen the bookmark is not inside a group.- Selecting a rendered bookmark reveals the Bookmarks view, opens the collapsed groups on its path, scrolls it into view and flashes it for 2.2 seconds. The Bases cell is not highlighted, and the link carries no underline and no tooltip.
Limitations:
- The plugin reads internal, undocumented Bookmarks and Bases structures:
app.internalPlugins.getPluginById("bookmarks"), theitems/_itemsbookmark tree, and the BasesregisterInstanceFuncregistry. An Obsidian update may change them, in which case the plugin registers nothing or returns empty values instead of failing. - Values are deduplicated by escaped path, so two bookmarks that share the same group chain and the same title collapse into a single entry.
- The Bookmarks view exposes no stable per-item attribute. Highlighting therefore also falls back to matching the visible text, and may select the first match when several bookmarks share a title in the same group.
The build compiles src/main.ts into the bundled main.js at the repository root:
npm run dev # watch mode, inline sourcemap, no minification
npm run build # type-check with tsc, then a minified bundle without a sourcemap
npm run lint # ESLint, using eslint-plugin-obsidianmdManual install for testing:
- Run
npm run build. - Copy
main.js,manifest.jsonandstyles.cssinto<Vault>/.obsidian/plugins/bookmarks-in-bases/. - Reload Obsidian and enable Bookmarks-In-Bases in Settings → Community plugins.
Versioning: run npm version patch|minor|major. The version script then runs version-bump.mjs, which copies the new version into manifest.json and adds the version mapped to the current minAppVersion in versions.json. .npmrc sets tag-version-prefix="", so the resulting git tag has no leading v and matches manifest.json. main.js is generated, is not committed, and is attached to the GitHub release instead.
The plugin exposes no user-facing settings. There is no settings tab, no configuration key, and no persisted data: the plugin calls neither loadData nor saveData, and writes no data.json. Behaviour is determined entirely by the current file and the vault's bookmark tree.
minAppVersionis1.13.7, as declared inmanifest.json.- Developed and tested against Obsidian 1.13.7.
isDesktopOnlyisfalse. The plugin uses no desktop-only or Node.js API; it relies on DOM APIs that are present in the mobile app.
0BSD. See LICENSE.
Copyright (C) 2020-2026 by Dynalist Inc.
Report issues at https://github.com/LorenBll/Bookmarks-In-Bases/issues.