description: A page generated dynamically by code rather than stored as a file.
Virtual pages are read-only pages that don't exist as files in your space. Instead, they are generated dynamically when you navigate to them. This is useful for building pages whose content is computed on-the-fly — for example, pages that show all objects with a particular tag.
You define a virtual page by calling virtualPage.define with a Lua pattern and a function. When someone navigates to a page name matching the pattern, SilverBullet calls your function instead of loading a file from disk.
The function receives the captured groups from the pattern as arguments, and returns the markdown content to display.
virtualPage.define {
pattern = "greeting:(.+)",
run = function(name)
return "# Hello, " .. name .. "!\nWelcome to this virtual page."
end
}
Navigating to greeting:World renders a page with the heading "Hello, World!" — but no file is created.
Virtual pages are always read-only. The editor disables editing controls automatically.
The most commonly used virtual pages are tag pages. When you click a hashtag like #project, SilverBullet navigates to tag:project, which is a virtual page that lists all objects with that tag — grouped by type (pages, tasks, items, data, etc.).
Tag pages are defined in the standard library and work out of the box. You can override the default tag page by defining your own virtualPage.define with the pattern tag:(.+).
Navigating to a page named uri:https://example.com/page fetches and displays the content from that URL. This is useful for pulling in external markdown content.
Here's a more complete example that queries the object index:
virtualPage.define {
pattern = "recent:(%d+)",
run = function(count)
local n = tonumber(count)
local pages = query[
from index.tag "page"
order by lastModified desc
limit n
](
from index.tag "page"
order by lastModified desc
limit n
)
local result = "# " .. count .. " Most Recent Pages\n"
for _, page in ipairs(pages) do
result = result .. "* [" .. page.name .. "](" .. page.name .. ")\n"
end
return result
end
}
Navigate to recent:10 to see the 10 most recently modified pages.
Patterns can capture multiple groups, each passed as a separate argument:
virtualPage.define {
pattern = "lookup:(.+):(.+)",
run = function(type, id)
return "# " .. type .. "\nLooking up: " .. id
end
}
Virtual page definitions are stored in the config system under the virtualPages key. When a page is being created, SilverBullet fires the editor:pageCreating event. The standard library's event listener checks all registered patterns against the page name. If a match is found, the corresponding run function is called, and its return value becomes the page content with read-only permissions.
See also: API/event, API/config