Roam Research Docs · Developer documentation
window object in the browser under roamAlphaAPI
roamAlphaAPI.ui.callout.addType / .removeType — register custom callout types for the picker menu, styled entirely via CSS
roamAlphaAPI.data.block.addComment — programmatic block commenting with reply-string or reply-markdown
roamAlphaAPI.data.ai.getGraphGuidelines now includes userDisplayPage field with the requesting user's display page content
roamAlphaAPI.components render functions
window.roamAlphaAPI .components renderBlock:open? optional zoom-start-after-uid optional
open? optional
true = force open the block (show the children if exist)
false = force close the block (even if the block has children, they are not shown)
zoom-start-after-uid optional
zoom-path? is true
... for everything until passed in uid
window.roamAlphaAPI .components renderString
renderString
[[Page Title]] like links for pages that do not exist, those links will not work. Please try not to do that
string
[[Page Title]] like links for pages that do not exist, those links will not work. Please try not to do that
el
const newNode = document.createElement('div');
const wrap = document.getElementById('right-sidebar');
// insert our new node after the wrap element in the DOM tree
wrap.insertBefore(newNode, wrap.firstChild)
window.roamAlphaAPI.ui.components.renderString(
{
el: newNode,
string: "Hello this is via [[Roam Alpha API]]'s `renderString` which ((-PAiIlJ14))"})
q, pull, and variant API functions now have a timeout of 20 seconds
Query and/or pull expression took too long to run. when you run into the timeout limit
pin-to-top? parameter to window.roamAlphaAPI.ui.rightSidebar**.pinWindow**
pin-to-top?
pin-to-top? is passed, it should be either true or false
.pinWindow on it again but without specifying an explicit pin-to-top?, nothing changes
true, then we pin the specified window to the top of the sidebar. Visually this will make the pin look red. new sidebar windows will be added below it
window.roamAlphaAPI.ui.rightSidebar**.getWindows**'s return value will contain pinned-to-top?: true if a window is pinned to the top
window.roamAlphaAPI .ui .rightSidebar functions
type: "search-query"
search-query-str parameter instead of block-uid
// Add a window that searches for "API"
window.roamAlphaAPI.ui.rightSidebar
.addWindow({window:
{type:'search-query' ,'search-query-str':'API'}})
disable-hotkey
.addCommand
disable-hotkey
default-hotkey
default-hotkey
.addCommand + the new parameters
.removeCommand
onunload - if you call via extensionAPI, will be cleaned automatically on unload
window.roamAlphaAPI .ui .components
renderBlock: zoom-path?optional
zoom-path?optional
zoom-path? is true, it shows the zoom path i.e. view which looks similar to how linked refs look
const newNode = document.createElement('div');
const wrap = document.getElementById('right-sidebar');
// insert our new node after the wrap element in the DOM tree
wrap.insertBefore(newNode, wrap.firstChild)
window.roamAlphaAPI.ui.components.renderBlock(
{
"uid": '6-P4ZEbIY',
"el": newNode,
// optional args below
// open? is for if you want to force open/close the block
// if not passed, uses whatever the normal open state of that block is in the db/graph
"open?": false,
// zoom-path? : if you want to show the zoomable path of the block too
"zoom-path?": true,
// optional addition in zoom-path? mode: path compacts to ... for everything until passed in uid
"zoom-start-after-uid": "ImSvJvm1_"
})
renderPage
renderPage
zoom-path? for block or hide-mentions? for page), you can use this interchangeably with renderBlock
hide-mentions? )
uid
el
hide-mentions?
renderSearch
renderSearch
search-query-str in a given DOM node.
rm-search-query. Also uses the existing rm-query class
{{[[search]]}} or {{[[search]]: Bret Victor}}
search-query-str
el
closed?
group-by-page?
hide-paths?
config-changed-callback
const newNode = document.createElement('div');
const wrap = document.getElementById('right-sidebar');
// insert our new node after the wrap element in the DOM tree
wrap.insertBefore(newNode, wrap.firstChild)
window.roamAlphaAPI.ui.components.renderSearch(
{"search-query-str": 'Bret Victor',
"closed?": false,
"group-by-page?": false,
"hide-paths?": false,
"config-changed-callback": (config) => {console.log("new-config", config);},
el: newNode})
e.dataTransfer.getData() for drag events
roamAlphaAPI.data.page.create or the older roamAlphaAPI.createPage) now throws errors (in the promise) if a page with the provided title already exists or if a page with the provided uid already exists
roamAlphaAPI.data.page.update or the older roamAlphaAPI.updatePage) now throws errors (in the promise) if we're trying to change to a title that already exists in the db
roamAlphaAPI.data.block.move or the older roamAlphaAPI.moveBlock) now throws errors (in the promise) if blocks corresponding to parent-uid or uid do not exist
error.message being a certain string
window.roamAlphaAPI.ui.filters ->
.getPageFilters
.getPageLinkedRefsFilters
.getSidebarWindowFilters
.setPageFilters
.setPageLinkedRefsFilters
.setSidebarWindowFilters
roam.ui.filters
get-page-filters
get-page-linked-refs-filter
get-sidebar-window-filters
set-page-filters
set-page-linked-refs-filters
set-sidebar-window-filters
applied-science.js-interop
.pull_many
.fast
.setBlockFocusAndSelection/Parameters::/location/window-id
setTimeout hacks will be necessary to ensure that a write took place before doing another related write.. you can just await for it.
nil or null or undefined.
.catch() for errors (or the just use try catch blocks in case you are using await)
true)
window.roamAlphaAPI.ui
.setBlockFocusAndSelection
.mainWindow
.openDailyNotes
.getOpenPageOrBlockUid
.leftSidebar
.open
.close
location
parent-uid
order
'last' to append
block
uid
string
open
true by default (if not passed)
children-view-type
bullet
numbered
document
block-view-type
outline
horizontal-outline
popout
tabs
comment
side
vertical
text-align
left
center
right
justify
heading
0 (no heading styling)
1
2
3
page
uid
title
children-view-type
bullet
numbered
document
window
collapsed?
pinned?
type
.getWindows to see the types in action
block-uid
block-uid, mentions-uid, and page-uid in the returned window object.
order
window.roamAlphaAPI
.data
.q
q, pull, and variant API functions now have a timeout of 20 seconds
Query and/or pull expression took too long to run. when you run into the timeout limit
query
& args
window.roamAlphaAPI.data.q(
`[:find ?b ?s
:where
[?e :block/uid ?b]
[?e :block/string ?s]]`);
.pull
pattern to a collection of entities, building a map for each entity.
q, pull, and variant API functions now have a timeout of 20 seconds
Query and/or pull expression took too long to run. when you run into the timeout limit
pattern
"[*]"
"[:block/string {:block/children ...}]"
eid
:db/id
24
"[:node/title \"hello world\"]"
[":block/uid", "xyz"]
window.roamAlphaAPI.data.pull("[*]", [":block/uid", "xyz"])
window.roamAlphaAPI.data.pull("[:block/string {:block/children ...}]", "[:block/uid \"xyz\"]")
.pull_many
.pull but for multiple entities
q, pull, and variant API functions now have a timeout of 20 seconds
Query and/or pull expression took too long to run. when you run into the timeout limit
pattern
pattern
eids
eids
roamAlphaAPI.data.pull_many("[*]", [[":block/uid", "_fM7pkQEa"], [":block/uid", "kZHsZniZs"]]);
.fast
.fast use experimental clojurescript to javascript conversion to speed up read access. They tend to be around 33% faster and more comparable to running it in pure clojurescript.
obj[":block/string"]
:block/string
"[:block/string :as "string"]"
obj.string
q, pull, and variant API functions now have a timeout of 20 seconds
Query and/or pull expression took too long to run. when you run into the timeout limit
.q
.search
search-str required
search-blocks optional
true
search-pages optional
true
hide-code-blocks optional
false
limit optional
300
1000
pull optional
[:block/string :node/title :block/uid]
roamAlphaAPI.data.search({"search-str": "my query"})
.semanticSearchEnabled
true when embeddings are enabled and a user is signed in.
roamAlphaAPI.data.async.semanticSearch, which throws when semantic search isn't available.
roamAlphaAPI.data.semanticSearchEnabled()
boolean
.roamQuery
.search but for Roam's native query syntax (the same syntax used in {{[[query]]: ...}} blocks).
uid of an existing query block to use its stored settings
query string directly with optional display settings
{total: number, results: Array} where results use the specified pull pattern
uid (String) - Block uid of an existing query block. Uses the block's stored display settings.
query (String) - Direct query string, e.g. "{and: [[project]] [[active]]}". Required if no uid.
groupByPage (Boolean, default: true) - Group results by page. Query mode only.
nestUnderParent (Boolean, default: false) - Collapse child matches under parent. Query mode only.
sort (String) - Sort type. Query mode only.
groupByPage is true: "page-most-recent" (default), "page-title", "page-created-date", "daily-note"
groupByPage is false: "created-date" (default), "edited-date", "daily-note-date"
sortOrder (String, default: "desc") - "asc" or "desc". Query mode only.
offset (Integer, default: 0) - Number of results to skip.
limit (Integer | null, default: 20) - Max results. Pass null for all results.
pull (String, default: "[:block/string :node/title :block/uid]") - Pull pattern for results.
await window.roamAlphaAPI.data.roamQuery({query: "{and: [[project]] [[active]]}"})
await window.roamAlphaAPI.data.roamQuery({uid: "abc123def"})
.async
.async are equivalent to the non async versions, except they return promises.
.q
.pull
.pull_many
.search
.semanticSearch
roamAlphaAPI.data.semanticSearchEnabled() first.
search-str required
k optional
25
200
search-blocks optional
true
search-pages optional
true
hide-code-blocks optional
roamAlphaAPI.data.async.semanticSearch({"search-str": "my query"})
{type, uid, topUids}
type: "chunk", "block", or "page"
uid: the hit's primary block uid (the first of topUids)
topUids: the hit's top-level block uids
.fast
.q
.backend
.backend (currently only q) run against the backend (off thread).
.q
.addPullWatch
window
.roamAlphaAPI
.data
.addPullWatch(
"[:block/children :block/string {:block/children ...}]",
'[:block/uid "02-21-2021"]',
function a(before, after) { console.log("before", before, "after", after); })
.removePullWatch
const pullPattern = '[:block/children :block/string {:block/children ...}]';
const entity = '[:node/title "Testing Page 2"]';
const testFn = function a(before, after) { console.log("before", before, "after", after);};
// first of all, you'd need to add it like the following
window
.roamAlphaAPI
.data
.addPullWatch(
pullPattern,
entity,
testFn);
console.log("added pull watch")
// MAIN: how to remove pull watches
window
.roamAlphaAPI
.data
.removePullWatch(
pullPattern,
entity,
testFn);
console.log("Removed pull watch")
.undo
.redo
.block
create
location
parent-uid required
order required
block
string required
uid optional
open optional
heading optional
text-align optional
children-view-type optional
block-view-type optional
window
.roamAlphaAPI
.createBlock(
{"location":
{"parent-uid": "01-21-2021",
"order": 0},
"block":
{"string": "test"}})
(ns demo.usage
(:require
[reagent.core :as r]
[roam.block :as block]))
(defn create-block-btn [_]
[:span
{:draggable true
:style {:border "1px solid black"
:cursor "pointer"
:padding "5px"}
:on-click (fn [evt] (block/create
{:location {:parent-uid "f8cXfDIRn"
:order 0}
:block {:string "Carthago delenda est"}}))}
"create block"])
(ns demo.usage
(:require
[reagent.core :as r]
[roam.block :as block]))
(defn create-block-btn [_]
[:span
{:draggable true
:style {:border "1px solid black"
:cursor "pointer"
:padding "5px"}
:on-click (fn [evt] (block/create
{:location {:parent-uid "f8cXfDIRn"
:order 0}
:block {:string "Carthago delenda est"}}))}
"create block"])}}
move
block
uid required
location
parent-uid required
order required
window
.roamAlphaAPI
.moveBlock(
{"location":
{"parent-uid": "01-21-2021",
"order": 0},
"block":
{"uid": "f8cXfDIRn"}})
(ns demo.usage
(:require
[reagent.core :as r]
[roam.block :as block]))
(defn move-block-btn [_]
[:span
{:draggable true
:style {:border "1px solid black"
:cursor "pointer"
:padding "5px"}
:on-click (fn [evt] (block/move
{:location {:parent-uid "f8cXfDIRn"
:order 0}
:block {:uid "VCuWBrulO"}}))}
"move block"])
(ns demo.usage
(:require
[reagent.core :as r]
[roam.block :as block]))
(defn move-block-btn [_]
[:span
{:draggable true
:style {:border "1px solid black"
:cursor "pointer"
:padding "5px"}
:on-click (fn [evt] (block/move
{:location {:parent-uid "f8cXfDIRn"
:order 0}
:block {:uid "VCuWBrulO"}}))}
"move block"])}}
update
block
uid required
string optional
open optional
heading optional
text-align optional
children-view-type optional
block-view-type optional
window
.roamAlphaAPI
.updateBlock({"block":
{"uid": "f8cXfDIRn",
"string": "Love"}})
(ns demo.usage
(:require
[reagent.core :as r]
[roam.block :as block]))
(defn update-block-btn [_]
[:span
{:draggable true
:style {:border "1px solid black"
:cursor "pointer"
:padding "5px"}
:on-click (fn [evt]
(block/update
{:block {:uid "VCuWBrulO"
:string "Love"}}))}
"update block"])
(ns demo.usage
(:require
[reagent.core :as r]
[roam.block :as block]))
(defn update-block-btn [_]
[:span
{:draggable true
:style {:border "1px solid black"
:cursor "pointer"
:padding "5px"}
:on-click (fn [evt]
(block/update
{:block {:uid "VCuWBrulO"
:string "Love"}}))}
"update block"])}}
delete
block
uid required
window
.roamAlphaAPI
.updateBlock({"block":
{"uid": "f8cXfDIRn"}})
(ns demo.usage
(:require
[reagent.core :as r]
[roam.block :as block]))
(defn update-block-btn [_]
[:span
{:draggable true
:style {:border "1px solid black"
:cursor "pointer"
:padding "5px"}
:on-click (fn [evt]
(block/update
{:block {:uid "VCuWBrulO"}}))}
"delete block"])
(ns demo.usage
(:require
[reagent.core :as r]
[roam.block :as block]))
(defn update-block-btn [_]
[:span
{:draggable true
:style {:border "1px solid black"
:cursor "pointer"
:padding "5px"}
:on-click (fn [evt]
(block/update
{:block {:uid "VCuWBrulO"}}))}
"delete block"])}}
fromMarkdown
location
parent-uid required
order required
markdown-string required
window.roamAlphaAPI.data.block.fromMarkdown({
location: { "parent-uid": "4VuwigG1O", "order": "first" },
"markdown-string": "# Hello\n\n- Item 1\n- Item 2\n - Nested"
});
reorderBlocks
parent-uid and an array of all the direct children of that block, and reorders the blocks according to the order provided in the array.
location
parent-uid required
blocks
parent-uid, and no other blocks, with no duplicates, listed in order
roamAlphaAPI.data.block.reorderBlocks({
location: {'parent-uid': 'ihu5eUofL'},
blocks: ['QCE0cNNNL','IATKcVmWE','nC22orMO4']})
addComment
block-uid
reply-string
reply-markdown, not both)
reply-markdown
reply-string, not both)
`javascript
// Single reply
window.roamAlphaAPI.data.block
.addComment({"block-uid": "abc123",
"reply-string": "This is a comment"})
// Markdown reply (parsed into siblings)
window.roamAlphaAPI.data.block
.addComment({"block-uid": "abc123",
"reply-markdown": "First block\n- Nested child"})`
.page
create
January 21st, 2021 will create a new daily note if it does not yet exist
page
title required
uid optional
children-view-type optional
fromMarkdown
page
title required
uid optional
children-view-type optional
markdown-string required
window.roamAlphaAPI.data.page.fromMarkdown({
page: {
title: "My New Page"
},
"markdown-string": "# Heading\n\n- Item 1\n- Item 2"
})
update
page
uid required
title optional
children-view-type optional
delete
page
uid required
addShortcut
uid required
index
roamAlphaAPI.data.page.addShortcut("12-11-2025");
roamAlphaAPI.data.page.addShortcut("12-11-2025", 4);
removeShortcut
uid required
roamAlphaAPI.data.page.removeShortcut("12-11-2025")
.user
upsert
user-uid
display-name
.ai experimental
.ui
.getFocusedBlock
.commandPalette callback, even if the block has lost focus in the DOM.
{
block-uid: "YnatnbZzF"
window-id: "BBG4fFwolaVlT5FZQdzAI7P40aB3-body-outline-04-15-2021"
}
.setBlockFocusAndSelection
location parameter)
selection parameter
selection is not present, defaults to placing the cursor at the end of the string
end is specified, it becomes a selection, otherwise it becomes a cursor placement before the start element (0-indexed)
location
block-uid
window-id
window-id
.rightSidebar/.getWindows
selection
start
end
selection is not present, defaults to placing the cursor at the end of the string
selection is present, start is mandatory
end is specified, it becomes a selection, otherwise it becomes a cursor placement before the start element (0-indexed)
end is less than start , then both are treated as the value of end
window.roamAlphaAPI.ui.setBlockFocusAndSelection(
{location: window.roamAlphaAPI.ui.getFocusedBlock(),
selection: {start: 3,
end: 7}})
.mainWindow
.openBlock
openBlock({block: {uid: "10-16-2021"}}) opens the daily note page for October 16th, 2021
block
uid required
// open(zoom into) a block in the main window
window.roamAlphaAPI.ui.mainWindow
.openBlock({block:
{uid: "v9eHoHwqS"}})
.openPage
page
title
uid
// open a page in the main window using uid
window.roamAlphaAPI.ui.mainWindow
.openPage({page:
{uid: "RZVuh3aZN"}})
// open a page in the main window using it's title
window.roamAlphaAPI.ui.mainWindow
.openPage({page:
{title: "test-new"}})
.openDailyNotes
.getOpenView
// Page outline
{ type: "outline", uid: "Vfht187T1", title: "My Page Title" }
// Block outline (zoomed into a block)
{ type: "outline", uid: "abc123xyz", "block-string": "Some block content" }
// Daily notes log
{ type: "log" }
// Graph view
{ type: "graph" }
// Diagram
{ type: "diagram", uid: "diagram-uid" }
// PDF viewer
{ type: "pdf", uid: "pdf-block-uid" }
// All pages search
{ type: "search" }
// Custom component
{ type: "custom", id: "component-id", args: [] }
.getOpenPageOrBlockUid
// returns a uid, which is a string like the one below
'Vfht187T1'
.focusFirstBlock
.leftSidebar
.open
.close
.rightSidebar
.open
window.roamAlphaAPI.ui.rightSidebar.open()
.close
window.roamAlphaAPI.ui.rightSidebar.close()
.getWindows
window.roamAlphaAPI.ui.rightSidebar.getWindows()
pinned-to-top?: true in the .getWindows output
pin-to-top? parameter in .pinWindow
.addWindow
window
type
block-uid
type = "search-query", then you need to pass search-query-str parameter instead of block-uid
// Add a window that searches for "API"
window.roamAlphaAPI.ui.rightSidebar
.addWindow({window:
{type:'search-query' ,'search-query-str':'API'}})
order
//Add a block
window.roamAlphaAPI.ui.rightSidebar
.addWindow({window:
{type:'block' ,'block-uid':'1fP8LY5ED'}})
//Add a page
window.roamAlphaAPI.ui.rightSidebar
.addWindow({window:
{type:'outline' ,'block-uid':'cArVJL_vg'}})
//Add mentions of a block
window.roamAlphaAPI.ui.rightSidebar
.addWindow({window:
{type:'mentions' ,'block-uid':'vutDCPD8G'}})
// Add a window that searches for "API"
window.roamAlphaAPI.ui.rightSidebar
.addWindow({window:
{type:'search-query' ,'search-query-str':'API'}})
.removeWindow
window
type
block-uid
type = "search-query", then you need to pass search-query-str parameter instead of block-uid
.expandWindow
window
type
block-uid
type = "search-query", then you need to pass search-query-str parameter instead of block-uid
.collapseWindow
window
type
block-uid
type = "search-query", then you need to pass search-query-str parameter instead of block-uid
.pinWindow
window
type
block-uid
type = "search-query", then you need to pass search-query-str parameter instead of block-uid
pin-to-top?
pin-to-top? is passed, it should be either true or false
.pinWindow on it again but without specifying an explicit pin-to-top?, nothing changes
true, then we pin the specified window to the top of the sidebar. Visually this will make the pin look red. new sidebar windows will be added below it
.unpinWindow
window
type
block-uid
type = "search-query", then you need to pass search-query-str parameter instead of block-uid
.setWindowOrder
window
type
block-uid
type = "search-query", then you need to pass search-query-str parameter instead of block-uid
order
.filters
.addGlobalFilter
title
type
.removeGlobalFilter
title
type
.getGlobalFilters
.getPageFilters
page
title or uid is required)
uid
title
window.roamAlphaAPI.ui.filters.getPageFilters(
{
"page":
{
"title": "test"
}
})
.getPageLinkedRefsFilters
page
title or uid is required)
uid
title
window.roamAlphaAPI.ui.filters.getPageLinkedRefsFilters(
{
"page":
{
"title": "test"
}
})
.getSidebarWindowFilters
window
type
block-uid
.getSidebarWindowFilters
window.roamAlphaAPI.ui.filters.getSidebarWindowFilters(
{
"window":
{
"block-uid": "WYlc2nIO9",
"type": "outline"
}
})
.setPageFilters
page
title or uid is required)
uid
title
filters
.getPageFilters)
includes
removes
window.roamAlphaAPI.ui.filters.setPageFilters(
{
"page": {"title": "test"},
"filters": {"includes": ["March 11th, 2022"]}
})
// the following clears the filters
window.roamAlphaAPI.ui.filters.setPageFilters(
{
"page": {"title": "test"},
"filters": {}
})
.setPageLinkedRefsFilters
page
title or uid is required)
uid
title
filters
.getPageLinkedRefsFilters)
includes
removes
window.roamAlphaAPI.ui.filters.setPageLinkedRefsFilters(
{
"page": {"title": "test"},
"filters": {"includes": ["Author"]}
})
// the following clears the filters
window.roamAlphaAPI.ui.filters.setPageLinkedRefsFilters(
{
"page": {"title": "test"},
"filters": {}
})
.setSidebarWindowFilters
window
type
block-uid
filters
.getSidebarWindowFilters)
includes
removes
window.roamAlphaAPI.ui.filters.setSidebarWindowFilters(
{
"window":
{
"block-uid": "WYlc2nIO9",
"type": "outline"
},
"filters": {"includes": ["Author"]}
})
// the following clears the filters
window.roamAlphaAPI.ui.filters.setSidebarWindowFilters(
{
"window":
{
"block-uid": "WYlc2nIO9",
"type": "outline"
},
"filters": {}
})
.commandPalette
.addCommand
label, will not add a second command, but will update the first command with the new callback.
label
"RoamRS: Start review session"
callback
disable-hotkey
default-hotkey
["ctrl-c", "ctrl-m"] for going to next block
disable-hotkey is absent or false, no hotkey is set up but user can customize it from settings. So, most commands should NOT have default-hotkey
window.roamAlphaAPI.ui
.commandPalette
.addCommand({label: 'hi',
callback: () => console.log('Hello World!')})
default-hotkey
window.roamAlphaAPI.ui
.commandPalette
.addCommand({label: 'example1',
callback: () => console.log('Hello World!'),
"disable-hotkey": false,
// this is the default hotkey, and can be customized by the user.
// in most cases, you DO NOT want to be setting a default hotkey
"default-hotkey": "ctrl-cmd-l"})
window.roamAlphaAPI.ui
.commandPalette
.addCommand({label: 'example2',
callback: () => console.log('Hello World2!'),
// this is the default hotkey, and can be customized by the user
// in most cases, you DO NOT want to be setting a default hotkey
"default-hotkey": ["ctrl-c", "ctrl-x"]})
.removeCommand
label from the Command Palette
label
.addCommand
window.roamAlphaAPI.ui
.commandPalette
.removeCommand({label: 'hi'})
.slashCommand
.addCommand
label, will not add a second command, but will update the first command with the new callback.
label: string
display-conditional: function, optional
context but without the indexes which should return true if the command should be shown or false if not.
context
{
block-uid: "YnatnbZzF",
window-id: "BBG4fFwolaVlT5FZQdzAI7P40aB3-body-outline-04-15-2021",
indexes: [1 10]
}
callback: function
context when the user selects the command in block context menu.
window.roamAlphaAPI.ui.slashCommand.addCommand({
label: "Quick Test",
'display-conditional': (args) => {
console.log("display:", args);
},
callback: (args) => {
console.log("Callback received:", args);
return "It works! 🎉";
}
});
.removeCommand
label from the Block Context Menu
label: string
.addCommand
.multiselect
.getSelected
// Returns an array of selected blocks with their uid and window-id
[
{ "block-uid": "Vfht187T1", "window-id": "main-window" },
{ "block-uid": "abc123xyz", "window-id": "main-window" }
]
// Empty array if no blocks are selected
[]
.individualMultiselect
getSelectedUids
cmd-m)
window.roamAlphaAPI.ui.getSelectedUids()
.blockContextMenu
.addCommand
label, will not add a second command, but will update the first command with the new callback.
label: string
"RoamRS: Start review session"
display-conditional: function, optional
block-context which should return true if the command should be shown or false if not.
block-context
{
block-string: "Todos"
block-uid: "YnatnbZzF"
heading: null
page-uid: "04-15-2021"
read-only?: false
window-id: "BBG4fFwolaVlT5FZQdzAI7P40aB3-body-outline-04-15-2021"
}
callback: function
block-context when the user selects the command in block context menu.
roamAlphaAPI.ui.blockContextMenu.addCommand(
{label: "Debug: Console Log",
'display-conditional':
(e) => e['block-string'].includes("Test Block"),
callback: (e)=>console.log(e)
}
)
.removeCommand
label from the Block Context Menu
label: string
.addCommand
.pageContextMenu
.addCommand
.blockContextMenu description, this is identical but for the page context menu when right clicking on titles.
label: string
"RoamRS: Start review session"
display-conditional: function, optional
page-context which should return true if the command should be shown or false if not.
page-context
{
page-uid: "YnatnbZzF"
page-title: "title"
window-id: "BBG4fFwolaVlT5FZQdzAI7P40aB3-body-outline-04-15-2021"
}
callback: function
page-context when the user selects the command in block context menu.
roamAlphaAPI.ui.pageContextMenu.addCommand(
{label: "Debug: Console Log",
callback: (e)=>console.log(e)
}
)
.removeCommand
label from the Block Context Menu
label: string
.addCommand
.pageLinkContextMenu
.addCommand
label: string
display-conditional: function, optional
ref-context which should return true if the command should be shown or false if not.
ref-context
{
page-uid: "YnatnbZzF",
page-title: "title"
}
callback: function
ref-context when the user selects the command in block context menu.
roamAlphaAPI.ui.pageLinkContextMenu.addCommand(
{label: "Debug: Console Log",
callback: (e)=>console.log(e)
}
)
.removeCommand
label from the Block Context Menu
label: string
.addCommand
.pageRefContextMenu
.addCommand
.blockContextMenu description, this is identical but for the page ref context menu when right clicking on a page reference.
label: string
display-conditional: function, optional
ref-context which should return true if the command should be shown or false if not.
ref-context
{
ref-uid: "YnatnbZzF"
block-uid: "xyz" // containing block uid
window-id: "BBG4fFwolaVlT5FZQdzAI7P40aB3-body-outline-04-15-2021"
indexes: [0 9] //outer indexes
type: "attribute"
}
type
[[test]]
test::
#test
#[[Test]]
[t]([[Test]])
callback: function
ref-context when the user selects the command in block context menu.
roamAlphaAPI.ui.pageRefContextMenu.addCommand(
{label: "Debug: Console Log",
callback: (e)=>console.log(e)
}
)
.removeCommand
label from the Block Context Menu
label: string
.addCommand
.blockRefContextMenu
.addCommand
.blockContextMenu description, this is identical but for the block ref context menu when clicking on a block reference.
label: string
"RoamRS: Start review session"
display-conditional: function, optional
ref-context which should return true if the command should be shown or false if not.
ref-context
{
ref-uid: "YnatnbZzF"
block-uid: "YnatnbZzF" // containing block uid
window-id: "BBG4fFwolaVlT5FZQdzAI7P40aB3-body-outline-04-15-2021"
indexes: [0 9] //outer indexes
}
callback: function
ref-context when the user selects the command in block context menu.
roamAlphaAPI.ui.blockRefContextMenu.addCommand(
{label: "Debug: Console Log",
callback: (e)=>console.log(e)
}
)
.removeCommand
label from the Block Context Menu
label: string
.addCommand
.msContextMenu
addCommand
label
display-conditional
callback
window.roamAlphaAPI.ui.msContextMenu.addCommand(
{
"label": "test",
"callback": () => { console.log("hey") }
}
)
removeCommand
label
window.roamAlphaAPI.ui.msContextMenu.removeCommand(
{"label": "test"}
)
.graphView
addCallback
type parameter, you can request to only trigger callbacks on a specific type of graphs.
label
callback
context when the user selects the command in the Command Palette
context:
cytoscape holds a reference to the Cytoscape graph object
elements is an array of the nodes and edges in the graph
type is "page" | "all-pages"
{ cytoscape: Core {_private: {…}},
elements: [
{id: "eTCpkG-HI", name: "B", weight: 7}
{id: "05-04-2021", name: "May 4th, 2021", weight: 10}
{id: "FrW4nHLat", name: "A", weight: 7}
{id: "eTCpkG-HI-FrW4nHLat", source: "eTCpkG-HI", target: "FrW4nHLat"}
{id: "eTCpkG-HI-eTCpkG-HI", source: "eTCpkG-HI", target: "eTCpkG-HI"}
{id: "05-04-2021-eTCpkG-HI", source: "05-04-2021", target: "eTCpkG-HI"}
],
type: "page" }
type
page | all-pages to trigger on, if undefined, the callback triggers on all graphs
removeCallback
label
label
addCallback
wholeGraph
roamAlphaAPI.ui.graphView.wholeGraph.addCallback({
"label": "test",
"callback": (x) => {
console.log(x);
}
})
roamAlphaAPI.ui.graphView.wholeGraph.removeCallback({"label": "test"});
roamAlphaAPI.ui.graphView.wholeGraph.setExplorePages(['a']);
const x = roamAlphaAPI.ui.graphView.wholeGraph.getExplorePages();
console.log(x);
roamAlphaAPI.ui.graphView.wholeGraph.setMode("Whole Graph");
roamAlphaAPI.ui.graphView.wholeGraph.setMode("Explore");
.components
renderBlock
uid
el
open? optional
true = force open the block (show the children if exist)
false = force close the block (even if the block has children, they are not shown)
zoom-path?optional
zoom-path? is true, it shows the zoom path i.e. view which looks similar to how linked refs look
zoom-start-after-uid optional
zoom-path? is true
... for everything until passed in uid
const newNode = document.createElement('div');
const wrap = document.getElementById('right-sidebar');
// insert our new node after the wrap element in the DOM tree
wrap.insertBefore(newNode, wrap.firstChild)
window.roamAlphaAPI.ui.components.renderBlock(
{
"uid": '6-P4ZEbIY',
"el": newNode,
// optional args below
// open? is for if you want to force open/close the block
// if not passed, uses whatever the normal open state of that block is in the db/graph
"open?": false,
// zoom-path? : if you want to show the zoomable path of the block too
"zoom-path?": true,
// optional addition in zoom-path? mode: path compacts to ... for everything until passed in uid
"zoom-start-after-uid": "ImSvJvm1_"
})
renderPage
zoom-path? for block or hide-mentions? for page), you can use this interchangeably with renderBlock
hide-mentions? )
uid
el
hide-mentions?
renderSearch
search-query-str in a given DOM node.
rm-search-query. Also uses the existing rm-query class
{{[[search]]}} or {{[[search]]: Bret Victor}}
search-query-str
el
closed?
group-by-page?
hide-paths?
config-changed-callback
const newNode = document.createElement('div');
const wrap = document.getElementById('right-sidebar');
// insert our new node after the wrap element in the DOM tree
wrap.insertBefore(newNode, wrap.firstChild)
window.roamAlphaAPI.ui.components.renderSearch(
{"search-query-str": 'Bret Victor',
"closed?": false,
"group-by-page?": false,
"hide-paths?": false,
"config-changed-callback": (config) => {console.log("new-config", config);},
el: newNode})
renderString
[[Page Title]] like links for pages that do not exist, those links will not work. Please try not to do that
string
[[Page Title]] like links for pages that do not exist, those links will not work. Please try not to do that
el
const newNode = document.createElement('div');
const wrap = document.getElementById('right-sidebar');
// insert our new node after the wrap element in the DOM tree
wrap.insertBefore(newNode, wrap.firstChild)
window.roamAlphaAPI.ui.components.renderString(
{
el: newNode,
string: "Hello this is via [[Roam Alpha API]]'s `renderString` which ((-PAiIlJ14))"})
unmountNode
el
.react
Block
uid
open optional
true = force open the block (show the children if exist)
false = force close the block (even if the block has children, they are not shown)
zoomPath optional
zoomPath is true, it shows the zoom path i.e. view which looks similar to how linked refs look
zoomStartAfterUid optional
zoomPath is true
... for everything until passed in uid
const { Block } = window.roamAlphaAPI.ui.react;
// Basic usage
<Block uid="6-P4ZEbIY" />
// Force closed
<Block uid="6-P4ZEbIY" open={false} />
// With zoom path
<Block
uid="6-P4ZEbIY"
zoomPath={true}
zoomStartAfterUid="ImSvJvm1_"
/>
Page
uid optional
uid or title is required)
title optional
uid or title is required)
hideMentions optional
true, hides the linked references section at the bottom of the page
const { Page } = window.roamAlphaAPI.ui.react;
// By UID
<Page uid="page-uid-123" />
// By title
<Page title="My Page" />
// Hide mentions
<Page uid="page-uid-123" hideMentions={true} />
Search
searchQueryStr
closed optional
true, the view is collapsed
groupByPage optional
true, groups search results by their parent page
hidePaths optional
true, hides the block paths in results
onConfigChange optional
const { Search } = window.roamAlphaAPI.ui.react;
// Basic search
<Search searchQueryStr="Bret Victor" />
// Grouped by page with callback
<Search
searchQueryStr="Bret Victor"
groupByPage={true}
onConfigChange={(config) => console.log('Config changed:', config)}
/>
// Hide paths
<Search
searchQueryStr="TODO"
hidePaths={true}
/>
BlockString
[[page links]], ((block refs)), and other Roam formatting. The rendered content is not editable.
string
const { BlockString } = window.roamAlphaAPI.ui.react;
// Render text with page link
<BlockString string="Hello [[World]]" />
// Render text with block reference
<BlockString string="See also: ((abc123def))" />
// Render formatted text
<BlockString string="This is **bold** and __italic__" />
.callout
.addType
.rm-callout--{type} for color and .rm-callout--{type} .rm-callout__icon for the icon. A built-in default icon is shown as fallback until extension CSS loads.
type
[!type] callout syntax
null
window.roamAlphaAPI.ui.callout
.addType({type: "recipe"})
roam/css or extension stylesheet:
/* Emoji icon */
.rm-callout--recipe {
--callout-color: #f778ba;
}
.rm-callout--recipe .rm-callout__icon::before {
content: "🍪";
font-family: initial;
}
.removeType
type
null
window.roamAlphaAPI.ui.callout
.removeType({type: "recipe"})
.util
.generateUID
window.roamAlphaAPI.util.generateUID()
.pageTitleToDate
"June 16th, 2022", any non daily note title string will return nil, instead of a date
.dateToPageTitle
.dateToPageUid
06-16-2022)
.generateUID if you are programmatically generating a daily note page and need the uid ahead of time
.platform
.isDesktop #property
.isMobileApp #property
.isMobile #property
max-width: 450px
.isIOS #property
.isPC #property
.isTouchDevice #property
.graph
.name #property
.type #property
"hosted" or "offline"
.isEncrypted #property
.file
.upload
roamAlphaAPI.util.uploadFile, prefer using the new version, but the old function will not be removed
file
toast #optional
hide
false
roamAlphaAPI.file.upload({file: new File([""], "test"), toast: {hide: true}})
roamAlphaAPI.file.upload({file: new File([""], "blah")})
.get
fetch, but .get handles decrypting the file for encrypted graphs, and fetches the original file name and file type metadata for creating the file object
url
.upload, or from a block
roamAlphaAPI.file.get({url: "https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2Ftest103%2FGVfB6XBcMR.pdf?alt=media&token=0e0495c7-fbe9-4e13-b9a2-9ffb2c32cd26"})
.delete
url
.upload, or from a block
undefined
roamAlphaAPI.file.delete({url: "https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2Ftest103%2FVxMoWmo8pI.jpeg?alt=media&token=39af97c0-32f5-46f9-a198-90481b29d974"})
.user
.uid
roamAlphaAPI.user.uid()
// pull all info about a user
roamAlphaAPI.pull("[*]", [":user/uid", window.roamAlphaAPI.user.uid()]);
isAdmin
depot
getInstalledExtensions
{ccc+ccc-roam-pdf-2:
{id: 'ccc+ccc-roam-pdf-2',
name: 'Roam PDF Highlighter 2',
enabled: false,
version: '1' //version 'DEV' is for developer loaded extensions
}
...}
.constants
.corsAnywhereProxyUrl
fetching the url, instead fetch (roamAlphaAPI.constants.corsAnywhereProxyUrl + "/" + url)
let urlToFetch = "https://google.com"
await fetch(`${roamAlphaAPI.constants.corsAnywhereProxyUrl}/${urlToFetch}`)
.then(a=>a.text())
https://roamresearch.com
markdown version · view in Roam Research · exported 2026-07-03