ExBase is a proof of concept that exposes objects in a note's frontmatter array or in a .base file itself to Obsidian's built-in Bases views. It does not register a custom Bases view or create proxy files.
Obsidian Bases indexes files, not arbitrary objects. ExBase bridges that boundary with an in-memory emulation layer over Obsidian's Vault and MetadataCache read APIs. Native Bases receives virtual TFile entries whose complete state comes from the source note's frontmatter or the base file's own entries.
If you like what exbase does, you can support its development with a donation:
| Currency | Address |
|---|---|
| BTC | 1GdViZLJpCFJJ9PVfFkHsK4LsGdbVghtz2 |
| BCH | qz4hp4fved5jq0pwnv69d4hez5f9m7dh0v4hh8pfgz |
| ETH | 0x49e826930ee9819Cc92c339a7DCBa9f43A516332 |
AI tokens are expensive nowadays.
Vaults for testing all features:
Add an exbase-entries array to a note:
---
exbase-version: 1
exbase-path: Virtual tasks
exbase-entries:
- id: first
xb-content: "# First item\n\nThis Markdown body is stored in frontmatter."
xb-ctime: 2026-08-01T09:00:00Z
xb-mtime: 2026-08-09T12:00:00Z
name: First item
status: active
score: 10
- id: second
xb-basename: second
xb-content: "# Second item"
name: Second item
status: paused
score: 4
---Enable the plugin. ExBase publishes the array items as in-memory files and refreshes them when source metadata changes. The array property can be changed under Settings → ExBase.
By default, Expose entries as loaded files makes entry files visible at their virtual paths in Obsidian's file manager, loaded-file collection, and other plugins. Source notes remain regular Markdown files. Disable this patch under Settings → ExBase → Patches to keep entries out of those APIs; Bases and ExBase's virtual read APIs continue to work.
Frontmatter entries are hidden from the file explorer by default. Enable Show frontmatter entries in the file explorer under Settings → ExBase to surface them under virtual folders named after their source notes. This also makes them available to other plugins through the explorer-facing vault APIs. The option requires Expose entries as loaded files and is disabled while that patch is off.
File operations in ExBase-managed folders follow the virtual entry model:
- Create a Markdown file to append an entry to the source note.
- Rename, copy, or delete an entry to update its source array item. Entries cannot be dragged to another folder, and managed folders cannot contain nested folders.
- Rename, copy, or delete the managed folder to perform the same operation on its physical source note.
By default, the source note remains in file lists exposed to Bases and its configured entries property remains available in metadata. Enable Exclude notes containing entries under Settings → ExBase to show only virtual entries in those file lists.
ExBase caches scanned entries in IndexedDB so startup does not re-derive every entry from source frontmatter. Sources whose content is unchanged since the last scan are served from the cache; everything else is re-scanned and the cache is refreshed.
The cache is validated per source file (modification time and size) and never overrides the vault, which remains the source of truth. Writes are batched and flushed asynchronously; a crash before a flush simply causes the affected sources to be rescanned on the next startup. The cache is per vault and is wiped automatically when the cache schema or the configured array property changes.
Without Choose source for new base entries, Obsidian creates each new Base item as a regular Markdown file. Enable the setting under Settings → ExBase to add a Source control to built-in Base toolbars. The selection is stored per Base view:
- Default keeps Obsidian's regular Markdown file creation.
- This Base stores entries in the
.basefile itself (see below). - ExBase note appends entries to the configured entries array of a chosen Markdown note.
With ExBase note selected, creating an item from the Base appends a new virtual note to the selected note's frontmatter array instead of creating a regular file. ExBase applies the Base's initial properties and adds the managed xb-ctime, xb-mtime, and xb-size metadata.
Positive file.hasTag(...) filters on the Base are also applied to new entries as their tags property, so the entry remains visible in that Base.
The Source control is not available in embedded ```base code blocks: they have no backing file to store entries in, so their New button keeps Obsidian's default behavior.
Add an exbase-entries array (the configured array property) at the top level of a .base file:
filters: ...
views:
- type: table
name: Table
exbase-entries:
- id: first
xb-content: "# First item"
name: First item
status: activeExBase publishes these items as entries of that base. The key survives Obsidian's own rewrites of the .base file (column reorders, filter edits, view changes). With This Base selected in Source, the New button appends entries to this array; reading, renaming, copying, deleting, and editing properties in the base view all write back to the .base file.
Base-stored entries are private to their base:
- They appear in that base's views, but not in the file explorer, global search, graph, or other plugins.
- Other bases do not list them as results; they remain reachable from other bases through relation properties. Disable Scope base entries to their base under Settings → ExBase → Patches to let every base list them.
- Entries already stored in a base are always shown in that base, even if Source is set to Default or ExBase note — the source option only steers where new entries go.
- Enable Show base entries in the file explorer under Settings → ExBase to surface them under a virtual folder named after the base. This also makes them available to other plugins and requires Expose entries as loaded files.
Clicking an entry in a base opens it in the Markdown editor (the rendered virtual note), not the base view; this is the Open base entries in the editor patch, enabled by default.
Base-stored entries live at virtual paths beneath a folder named after the base file, so the standard Bases filter works on file.path. For a base at Projects/Apollo.base the entries are at Projects/Apollo/...; to show only its entries (or only note entries elsewhere), filter on the path prefix:
- Only this base's entries:
file.path.startsWith("Projects/Apollo/") - Only note (frontmatter) entries, excluding all base-stored entries: keep them out by excluding every base folder you use, for example
!file.path.startsWith("Projects/Apollo/").
Because the virtual folder name derives from the base file name, renaming the base moves the folder and requires updating the filter.
The same special properties (exbase-path, exbase-version, xb-*) apply as for note-stored entries.
To delete a custom property or formula from a .base file, enable Remove custom base properties under Settings → ExBase, right-click its column header, and select Remove column.
Remove column is available for:
- Formulas declared in the
.basefile'sformulassection. - Columns with a property configuration explicitly saved in the
.basefile'spropertiessection, such as a custom display name.
It is not available for:
- Note properties that Bases inferred from note frontmatter but that have no saved configuration in the
.basefile. - Built-in file properties, such as
file.nameorfile.mtime, unless the.basefile contains an explicit property configuration for that column. - Any column that is only referenced by a view's order, sort, summary, or grouping configuration.
Removing an eligible column deletes its definition and its references from the Base; it does not delete property values from notes.
Enable Edit relations in bases under Settings → ExBase → Patches to add searchable note selection to configured Base properties. The patch is disabled by default and currently supports Obsidian 1.12.x.
Open a Base, select Properties, and open a note property. Set its Base-local type to Relation, then configure Target and Multiple. A column's context menu also provides Relation in this view for quickly enabling or disabling the relation. The Properties list, column header, edit form, and cells reflect the active view's setting.
ExBase stores this assignment in that view's relations list in the .base
file, not in .obsidian/types.json. The same property can therefore be a
Relation in one Base view and use its normal note property type, such as List,
in another:
relations:
- property: participants
target: exbase-people.base#People
multiple: trueIts target must be a vault folder, a .base file, or a .base file plus view
fragment, such as People, People.base, or People.base#Directory. A Base
target uses its first view unless a view fragment is supplied. Targets without
a leading slash are resolved relative to the Base containing the relation, with
the vault root as a fallback. Prefix a target with / to resolve it from the
vault root, for example
/Directories/People.base#Directory or /People. These are vault paths;
filesystem paths such as C:\Notes\People.base are not supported. The note
Properties editor shows an inline error when a Base file or view cannot be
resolved. It continues to manage the property's global fallback type
independently.
Add the configured property as a column in a built-in Base table, then select its cell. ExBase displays existing values as native-style link pills and opens Obsidian's suggestion menu as you type. Enter or mouse selection adds the highlighted note, Escape closes the editor, and Backspace in an empty input removes the last relation. Multiple relations remain open after selection so more notes can be added. Select a relation pill to open its note or virtual entry; Ctrl-click or Cmd-click opens it in a new tab.
Relation values remain ordinary Obsidian Markdown links in frontmatter for both physical notes and virtual ExBase entries:
project: "[[Projects/Apollo|Apollo]]"
participants:
- "[[People/Ada Lovelace|Ada Lovelace]]"
- "[[People/Grace Hopper|Grace Hopper]]"
- "[[ExBase demo/People/Contacts/Alan Turing|Alan Turing]]"Relation values rendered in Base cells use normal Obsidian internal file links, including links to ExBase virtual entries. Newly selected relations use the note name as a link alias, so pills show the name instead of the full path.
Links use the vault's configured link format. Existing unresolved links remain visible and removable. Relation changes use the built-in Bases change callback, so they participate in native Base updates and undo instead of writing frontmatter independently.
Virtual ExBase entries are selectable from folder, vault-wide, and Base targets. Legacy encoded ExBase relation references remain readable and are rendered as normal internal file links.
Two-way relations, inverse columns, note creation from suggestions, and rollups are not yet implemented.
Each virtual file exposes the source's parsed exbase-version and exbase-path properties. Version 1 is supported; a missing, invalid, or unsupported version is exposed as unknown. Set exbase-path to place entries in a custom vault folder. These special properties are hidden from the source file's exposed metadata and are read-only on virtual files; change their stored values in the source note or .base file.
Edit an ordinary property such as name, status, or score directly in a built-in Base view. ExBase intercepts Obsidian's frontmatter write, updates the corresponding array item in the source note, and refreshes the virtual entry. Adding and deleting properties through a Base are supported as well.
Create a normal Base and filter it to the virtual paths, for example:
filters:
and:
- file.inFolder("Virtual tasks")
views:
- type: table
name: Array entriesDefine formulas once under Settings → ExBase → Global formulas. Select the
plus button, enter a property name and a Bases formula, and the definition is
saved in the plugin's data.json. Use the remove button beside a definition to
delete it.
Global formulas are enabled by default. Turn off Enable global formulas to stop exposing the definitions in Bases without deleting them. Custom formula functions remain available while global formulas are disabled.
Each complete definition is available in every Base as a formula property. For
example, a global formula named total is exposed as formula.total and can be
added to a view like a formula declared in that Base's formulas section.
Changes refresh open Bases immediately. An incomplete row is saved but is not
exposed until both fields have values. If a Base declares a formula with the
same name, the Base-local formula takes precedence.
ExBase adds custom functions to built-in Bases formulas.
Each custom formula function is enabled by default and has its own toggle under Settings → ExBase → Custom formula functions. If a function fails at runtime, ExBase disables only that function for the current session and marks its toggle with an alert icon. Fix the formula, then enable the function again to clear the failure quarantine and retry it without reloading Obsidian. Other custom functions and global formulas remain available.
getFilesByPathRegex(pattern) accepts one regular expression string and returns
file links for matching vault paths, in the same list-like form as
file.backlinks.
formulas:
projectFiles: 'getFilesByPathRegex("^Projects/.*\\.md$")'noteContent(pathOrLink, maxCharacters?) accepts a vault path or internal link
and an optional non-negative character limit. It returns the Markdown body
without YAML frontmatter. Note content is cached and refreshed when files
change.
formulas:
preview: 'noteContent(file.path, 160)'
linkedContent: 'formula["sameNameNote2"].map(noteContent(value))'taskCount(path, mode) counts Markdown tasks in a vault file or ExBase entry.
The mode must be
"total", "completed", "cancelled", "todo", or "inprogress".
Completed tasks use [x], cancelled tasks use [-], todo tasks use [ ], and
in-progress tasks use [/]. Other custom task statuses count only toward the
total.
formulas:
openTasks: 'taskCount(file.path, "todo") + taskCount(file.path, "inprogress")'Custom functions are registered in src/formulas/custom-functions, where more
function definitions can be added without changing the evaluator patch.
The following xb- array keys describe the virtual file and are not exposed as note properties:
xb-basename: virtual filename without the.mdextension.xb-content: Markdown body returned byVault.readandVault.cachedRead.xb-ctime: creation time as epoch milliseconds or a date string.xb-mtime: modification time as epoch milliseconds or a date string.xb-size: optional reported byte size. It is calculated from generated content by default.
All other keys become the virtual file's frontmatter properties, so names such as mtime remain available for user data. ExBase combines exbase-path with xb-basename, the item's id, or its array position. When exbase-path is omitted or invalid, ExBase derives the folder from the source note. Renaming a virtual file updates xb-basename on its source entry.
In Base formulas, access these values by their stored frontmatter key, for example file.properties["namePrefix"], note.namePrefix, or namePrefix. Enable Property aliases in formulas under Settings → ExBase to also access properties by Base display names, such as file.properties["notePrefix"]. Alias support updates immediately when the setting or a Base configuration changes. Aliases shared by different stored properties across Base files are ignored to avoid ambiguous results.
Virtual note properties are writable from built-in Base views. File metadata fields with the xb- prefix must still be edited in the source frontmatter array, except xb-basename, which is updated when the virtual file is renamed.
Large source notes are indexed incrementally: ExBase derives entries and publishes file events in small batches that yield to the app between batches, so creating or editing a source note with thousands of entries stays responsive. During bulk work, a status bar item shows progress as ExBase: indexing entries processed / total, mirroring Obsidian's own indexing indicator; it appears only when a reconcile processes at least 500 units of work.
Obsidian operations that require a physical adapter-backed file, including opening the virtual Markdown body in an editor, deleting the virtual file, attachments, and resource URLs, are not emulated.
The implementation patches public read and file-enumeration methods at runtime because Obsidian does not expose an official virtual-file provider API. The optional relation editor also patches Obsidian's internal metadata-widget dispatch because the public API does not expose custom Base cell editors. Compatibility can therefore vary between Obsidian releases. Failed patches are disabled automatically and marked in settings. Automatic patch retries under Settings → ExBase → Patches controls how many times ExBase retries each failed patch or global-formula refresh with base-2 delays of 1, 2, 4, 8, 16, and 32 seconds; set it to 0 to disable automatic retries. Formula refresh failures escalate to the custom-functions patch failure flow only after their retries are exhausted. ExBase then groups patch failures that occur together into one notice; select Try Enable to restore and retry every listed patch, or Dismiss to leave them disabled.
Developer settings are hidden by default. To reveal the Developer Settings section, select the Troubleshooting heading under Settings → ExBase three times within five seconds.
Developer commands are disabled by default. After revealing the settings, enable
Enable developer commands to expose Generate test note, Generate test
base, and Generate test bases with relations in the command palette.
Generate test base creates a .base file with entries stored in its own
exbase-entries array, exercising the base entry source.
Generate test bases with relations creates two .base files whose entries
cross-link each other through relation properties (team and project),
with each view's relations config targeting the other base.
All generated entries include xb-ctime, xb-mtime, and xb-size metadata;
xb-size is the UTF-8 byte size of the generated xb-content.
Index virtual entries is also available in Developer Settings. It is enabled by default and caches entries in Obsidian so other plugins can discover them.
The Troubleshooting section remains visible. Troubleshooting logs are disabled by default; enable Enable troubleshooting logs there to write ExBase warnings and errors to the developer console. The setting takes effect immediately and does not require reloading the plugin.
The command asks how many entries to create and defaults to 1000. Press Enter
to accept the selected value. It creates and opens a new ExBase note containing
random properties, Markdown content, and tasks for every entry. Existing test
notes are preserved by adding a numeric suffix to the generated filename.
npm install
npm run dev
npm run build
npm run lint
npm test
npm run test:watch