I have been using Roam Research for taking notes for years. One of the crucial features I like in Roam is linked references, also known as “backlinks”: you create links to new or existing pages as you write by wrapping the page name in [[ ]], and along with creating the forward link, you get references back to your writing on each of those pages. This frees your mind from the “Where should I file this note?” question — it doesn’t really matter where you’re taking the note. As long as you link relevant pages, your writing appears in all those places.

For the illusion to work, I don’t want to just have “links” to the original writing — I want the references to form the content of a page by extending the native page content. And that’s more or less how Roam does it.

Roam isn’t perfect though. While it does embed the linked references, it sometimes collapses them. In general, the collapse/expand state of each bullet is something Roam tracks — it remembers when you collapse or expand a block, and the state syncs across sessions and devices. That works fine for normal blocks, but I can’t, for the life of me, figure out how Roam decides to expand or collapse a linked reference. I often find that reference sections are collapsed without me explicitly doing it. I can, of course, expand them manually, but for me this breaks the whole illusion that the linked references are in fact part of the page.

In the absence of predictable collapsing behaviour, I prefer to force linked references to always be expanded. Unfortunately, this is not an option in Roam’s settings. The Command Palette Plus feature of the Workbench plugin does provide an “Expand Reference Children” command that makes it easier, but it still needs a manual step.

Fortunately, Roam supports user-provided JavaScript, so I found a way to automate it:

  1. Create a page in Roam (if not already present) called [[roam/js]]
  2. Create a new block on this page and enter: {{[[roam/js]]}}
  3. Nest a JavaScript code block under it with this content:

    (() => {
      const expandRefs = () => {
        document.querySelectorAll(".rm-reference-item .block-expand").forEach((btn) => {
          btn.dispatchEvent(new MouseEvent("contextmenu", { bubbles: true, button: 2 }));
          const menu = document.querySelector(
            '.bp3-transition-container:not([style*="display: none;"]) .bp3-popover-content > div > ul'
          );
          Array.from(menu?.children || [])
            .find((e) => e.innerText === "Expand all")
            ?.childNodes[0]?.click();
        });
      };
         
      const run = () => setTimeout(expandRefs, 800);
         
      run();
      window.addEventListener("hashchange", run);
    })();
    

The script runs on page load and on every URL hash change (Roam’s navigation mechanism). After an 800ms delay to let Roam finish rendering, it queries all reference item expand buttons and, for each one, simulates a right-click to trigger Roam’s popover menu before programmatically clicking the “Expand all” item. It’s a bit hacky, but it’s been working well for me in Roam v0.13.7.