Code buttons - simple way to execute JavaScript inside Markdown codeblock

The topic about interactive programming and mini apps inside Logseq seems super exciting nowadays.
Here is a quick way to execute JavaScript code, which is located inside Markdown codeblocks.

Thanks to @mentaloid for work and inspiration in Edit and run javascript code inside Logseq itself. My requirements are a bit simpler - just execute that code block with a button. If you are seeking for Jupyter-like notebook experience and/or more programming languages support, above topic might be better suited! Go credit him as well, if you like the overall topic.

Specific goals:

  • vanilla Logseq - no plugin
  • performance - no need to watch DOM with MutationObserver
  • convention-driven: Place code block as first child block under code button
  • encapsulation: based on official Web components standard, mini app in Logseq

Getting started

1. Put code for the CodeButton web component (Attachment 1) in custom.js. Then create a macro for ease of use:

 :macros {
          :button "<div is='code-button' name=$1 class=$2></div>"

1a. Restart.
2. Write in a block content {{button Click}}.
3. Create child block with content

logseq.api.show_msg("Hello world", "info");

4. Get greeting like in above picture.

More examples

// Insert childblock (current block ID of codeblock available via this.uuid)
// this.target_uuid has ID of block containing the button.
// this.ev is the fired click event.
logseq.api.insert_block(this.uuid, "new kid on the block")

// simple query
const results = logseq.api.q("[[MyPage]]");

// advanced query
const results = logseq.api.datascript_query(`[
:find (pull ?b[*]) 
  [?p :block/name "mypage"]
  [?b :block/refs ?p]]`);

Invoking the macro with quiet

{{button "Simple query",quiet}}

will suppress code execution popup.


  • To save you an app crash with logseq.api.show_msg :slight_smile: : Don’t feed it with complex objects. Use console.log or write blocks instead. In other words: e.g. no logseq.api.show_msg(this.ev, "info");.
  • TODO: Post about web components in Logseq as mini app, current limitations in Logseq

Attachment 1

class CodeButton extends HTMLDivElement {
  constructor() {
    const style = document.createElement("style");
    style.textContent = `
      button.button-style {
          display: inline-block;
          outline: none;
          cursor: pointer;
          padding: 0 10px;
          background-color: #fff;
          border-radius: 0.25rem;
          border: 1px solid #0070d2;
          color: #0070d2;
          font-size: 13px;
          line-height: 30px;
          font-weight: 400;
          text-align: center;
	  user-select: none;
      button.button-style::before {
          content: "➤ ";
      button.button-style:hover {
          background-color: #f4f6f9; 

      `<button class="button-style">${this.getAttribute("name") || "Click"}</button>`,

  code_from_childblock(ele) {
    const ele_block_uuid = ele
    if (!ele_block_uuid) return;

    // Convention: Codeblock is first child of button block.
    const first_child_block_uuid =
    if (!first_child_block_uuid) return;

    const content = logseq.api.get_block(first_child_block_uuid)?.content;
    if (!content) return;

    const regex =
    const code = content.match(regex)?.[1];
    if (!code) return;

    return {
      target_uuid: ele_block_uuid,
      uuid: first_child_block_uuid,

  handleClick(ev) {
    const AsyncFunction = async function () {}.constructor;
    const { code, } = this.code_from_childblock( ?? {};
    if (!code) {
      logseq.api.show_msg("No code block found", "error", { timeout: 5000 });
    try {
      AsyncFunction('"use strict";' + code).call({ ev, });
      if (!this.classList.contains("quiet"))
        logseq.api.show_msg("Executed code", "success", { timeout: 2000 });
    } catch (er) {
      logseq.api.show_msg(`Code error: ${er.message}`, "error", {
        timeout: 5000,
      throw er;

  connectedCallback() {

customElements.define("code-button", CodeButton, { extends: "div" });

Looks very nice, thanks for sharing. Going to experiment with it while searching for a good setup and workflow. (Just starting out with logseq)

Thanks. This is awesome.

I use this every day as a core component of my workflow.

Could you describe what your buttons do in your workflow?