Foam Queries
Foam Queries let you show dynamic lists, tables, and counts of notes inside the Markdown preview.
Use them when you want a note to answer questions such as “show my research notes”, “list notes linked to this topic”, or “count notes in this folder”.
For static note embeds, see Note Embeds.
Basic Query
Section titled “Basic Query”Use a foam-query code block:
```foam-queryfilter: "#research"sort: title ASClimit: 10```This renders a list of matching notes in the preview. Query results update as your workspace changes.
If you omit filter, Foam searches all notes:
```foam-queryformat: count```Query Options
Section titled “Query Options”filter: choose which notes to includeselect: choose which fields to display. Default:titleandpathsort: sort results, for exampletitle ASCorbacklink-count DESC. Include the sort field inselect, otherwise it will not be available after projection.limit: show only the firstnmatchesoffset: skip the firstnmatchesformat: render aslist,table, orcount
When you select more than one field, Foam renders a table by default.
Filters
Section titled “Filters”Simple Filters
Section titled “Simple Filters”Use these shortcuts for common cases:
"#tag": notes with that tag"[[note-id]]": notes linked to or from that note (use the same identifier as in wikilinks, e.g. the filename without extension)"/regex/": notes whose path matches the regular expression"*": all notes
Example:
```foam-queryfilter: "[[project-alpha]]"```Structured Filters
Section titled “Structured Filters”Use YAML when you need more control:
```foam-queryfilter: and: - tag: "#research" - not: path: "^/archive/"select: [title, tags, backlink-count]sort: title ASC```Supported filter keys:
tag: notes that have this tag (e.g.tag: "#research")type: notes of this type (e.g.type: "daily-note")path: notes whose path matches this regex (e.g.path: "^/projects/")title: notes whose title matches this regexlinks_to: notes that link to the given note identifier. Use"$current"to refer to the note containing the querylinks_from: notes that are linked from the given note identifier. Use"$current"to refer to the note containing the queryjexl: a Jexl expression evaluated against each note, e.g."resource.tags|length > 2". The expression has access toresource(with fieldstitle,path,type,tags,properties,backlinks,outlinks) and the built-in transformslength,lower,upper. Note: Jexl uses==(not===) and|length(not.length). The previousexpressionfield is deprecated and no longer evaluated.and,or,not: combine filters logicallyexpression: REMOVED. A JavaScript expression that used to be evaluated against each note. Replaced by jexl for security reasons; legacy queries match nothing- Use
"$current"inlinks_toorlinks_fromto query relative to the note containing the query block:
```foam-queryfilter: links_to: "$current"```Displayed Fields
Section titled “Displayed Fields”You can select these fields:
titlepathtypetagsaliasessectionsblocksproperties(useproperties.<name>to pick one)backlink-countoutlink-countbody— the full note text (frontmatter removed, H1 title kept), rendered as markdowncontent— same asbodybut without the H1 title; useful when the title is already shown in another columnsection[Label]— the content of the named section (heading removed), rendered as markdown
Example table:
```foam-queryfilter: "#research"select: [title, tags, backlink-count]sort: backlink-count DESCformat: table```Including note content in results
Section titled “Including note content in results”To show the full text of each matching note, select body or content:
```foam-queryfilter: jexl: "resource.properties.status == 'to_ask'"select: [title, body]```To show just a named section, use section[Label]. Labels may contain spaces:
```foam-queryfilter: jexl: "resource.properties.status == 'to_ask'"format: tableselect: - title - section[Question] - properties.status```When you write select: as a block sequence (each field on its own line, prefixed with -), you can use section[My Label] directly. If you use the inline form select: [...], YAML treats [ and ] as collection delimiters, so quote the value: select: [title, 'section[My Label]'].
Section labels are matched case-sensitively — section[Question] will not match a heading written ## question.
body,content, andsection[...]aren’t supported in VS Code Web — you’ll see an inline warning in their place. Everything else works.
Customising table column headers
Section titled “Customising table column headers”By default the table header shows the field expression. For section[Decision] and properties.Status the wrapper is stripped automatically so you see Decision and Status. To set an explicit label, use the object form for that entry:
```foam-queryfilter: "#decisions"format: tableselect: - title - field: section[Decision] label: Chosen Decision - field: properties.Status label: Question status```Plain strings and { field, label } objects can be mixed in the same select.
Count Queries
Section titled “Count Queries”Use count when you only need the number of matches:
```foam-queryfilter: path: "^/projects/"format: count```JavaScript Queries
Section titled “JavaScript Queries”Use foam-query-js when YAML is not enough. JavaScript queries only run in a trusted workspace. In a trusted workspace, a foam-query-js block runs with the same permissions as the rest of VS Code — it can read and write files, make network requests, and run any code your editor can. Treat blocks the same way you’d treat any script you’d download and run: only enable a trusted workspace for notes you author or trust.
```foam-query-jsconst recentResearch = foam.pages('#research') .sortBy('title') .limit(5) .format('list');
render('Recent research notes:');render(recentResearch);```foam.pages(filter?) returns a query builder. Omit the filter to include all notes.
foam.current is the URI of the note containing the query. Use it to write queries that are relative to the current note:
```foam-query-js// Show all notes that link to this noterender(foam.pages({ links_to: foam.current }).sortBy('title'));``````foam-query-js// Show all notes that this note links torender(foam.pages({ links_from: foam.current }).sortBy('title'));```foam.current is null if no document is active in the editor.
Available builder methods:
where(fn): keep only notes wherefn(note)returns true, e.g..where(n => n.tags.includes('draft'))sortBy(field, direction?): sort by field, direction is'asc'(default) or'desc'limit(n): return at mostnresultsoffset(n): skip the firstnresultsselect(fields): project to the given fields. Each entry can be a plain string (e.g.'title') or an object{ field, label }to customise the table column header, e.g..select(['title', { field: 'properties.Status', label: 'State' }])format(fmt): set the output format ('list','table', or'count')toArray(): return results as a plain array for use in custom logic
Call render(...) to show output in the preview. You can pass a query builder or a plain string.
Using
.wheretogether withbody/content/section[...]is slow on large workspaces — narrow your results with a regularfoam-queryfilter first when you can.
Trust And Limitations
Section titled “Trust And Limitations”foam-query-jsrequires a trusted workspace. In a trusted workspace it runs with full editor permissions — no internal sandbox. If you don’t trust the source of your notes, keep the workspace untrusted.jexlfilters run in any workspace — Jexl is sandboxed by language design (no host globals, no file system, no network).- Queries render in Markdown preview, not directly in the editor.
- Query results link back to the matching notes.