# "Unlinked documents" as a virtual page

**URL:** <https://community.silverbullet.md/t/unlinked-documents-as-a-virtual-page/3554>\
**Category:** Tricks & Techniques\
**Created:** [November 16, 2025, 10:35pm UTC](https://community.silverbullet.md/t/unlinked-documents-as-a-virtual-page/3554 "2025-11-16T22:35:32Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![bmblb3](https://community.silverbullet.md/user_avatar/community.silverbullet.md/bmblb3/32/2136_2.png) [@bmblb3](https://community.silverbullet.md/u/bmblb3)\
**Post date:** [November 16, 2025, 10:35pm UTC](https://community.silverbullet.md/t/unlinked-documents-as-a-virtual-page/3554/1 "2025-11-16T22:35:32Z")

</div>

I sync a lot of handwritten pngs from my e-ink to my SB space. I then link some of these pngs (for eg. drawings) in my pages.

The other images (that don’t get linked) take up unnecessary space. So I wrote a virtual page to get a list of these pngs so I may review them & delete if necessary.

```space-lua
virtualPage.define {
  pattern = "udocs:(.+)",
  run = function(_)
    local result = {"# Unlinked documents"}

    local all_docs = query[[
      from index.tag "document"
      where not name.match("js$")
      select name
    ]]

    local linked_docs = query[[
      from index.tag "link"
      where toFile
      select toFile
    ]]

    local linked_set = {}
    for _, doc in ipairs(linked_docs) do
      linked_set[doc] = true
    end

    local unlinked_docs = {}
    for _, doc in ipairs(all_docs) do
      if not linked_set[doc] then
        table.insert(unlinked_docs, doc)
      end
    end
    for _, doc in ipairs(unlinked_docs) do
      local line = {}
      table.insert(line, "- [[")
      table.insert(line, doc)
      table.insert(line, "]]")
      table.insert(result, table.concat(line))
    end

    return table.concat(result, "\n")
  end
}

```

---

<div class="post-metadata">

**Author:** ![Mr.Red](https://community.silverbullet.md/user_avatar/community.silverbullet.md/mr.red/32/821_2.png) [@Mr.Red](https://community.silverbullet.md/u/Mr.Red)\
**Post date:** [November 16, 2025, 11:06pm UTC](https://community.silverbullet.md/t/unlinked-documents-as-a-virtual-page/3554/2 "2025-11-16T23:06:33Z")

</div>

This is useful 👍 Thank You!

---

<div class="post-metadata">

**Author:** ![thepaperpilot](https://community.silverbullet.md/user_avatar/community.silverbullet.md/thepaperpilot/32/1557_2.png) [@thepaperpilot](https://community.silverbullet.md/u/thepaperpilot)\
**Post date:** [November 17, 2025, 2:46pm UTC](https://community.silverbullet.md/t/unlinked-documents-as-a-virtual-page/3554/3 "2025-11-17T14:46:00Z")

</div>

If you move the document collection into its own function, like so:

```lua
unlinked_documents = {}

function unlinked_documents.get()
  local all_docs = query[[
    from index.tag "document"
    where not name.match("js$")
    select name
  ]]

  local linked_docs = query[[
    from index.tag "link"
    where toFile
    select toFile
  ]]

  local linked_set = {}
  for _, doc in ipairs(linked_docs) do
    linked_set[doc] = true
  end

  local unlinked_docs = {}
  for _, doc in ipairs(all_docs) do
    if not linked_set[doc] then
      table.insert(unlinked_docs, doc)
    end
  end

  return unlinked_docs
end

virtualPage.define {
  pattern = "udocs:(.+)",
  run = function(_)
    local result = {"# Unlinked documents"}

    local unlinked_docs = unlinked_documents.get()
    for _, doc in ipairs(unlinked_docs) do
      local line = {}
      table.insert(line, "- [[")
      table.insert(line, doc)
      table.insert(line, "]]")
      table.insert(result, table.concat(line))
    end

    return table.concat(result, "\n")
  end
}

```

You can make a widget button to open the virtual page that mentions how many documents there are:

```auto
${widgets.button("Unlinked Documents (" .. #unlinked_documents.get() .. ")", function() editor.navigate("udocs:Unlinked Documents") end)}

```

 ![Screenshot_20251117-084533](https://community.silverbullet.md/uploads/default/original/2X/e/ed26a78eb61d05172fc22c6652b688c9bfb2373f.png)
