Make custom macros that replace themselves with their value on first run

  • Synthesis is meant to both simplify and improve plenty of other things, thus superseding them.
    • As long as someone is willing to afford the potential overhead in installing, learning, and using it.
      • In other words, configuring is not enough in itself.
  • Smaller focused threads are preferable to long gathering ones.
    • The dedicated subforum is a good-enough gatherer anyway.
      • Tags could be used to further group related threads.
    • Big meals should be eaten in small bites.
    • Baptism in basic lessons is necessary before diving into practical usecases.

I understand that one can use/call any program Logseq supports to do something and then define a “natural” language so that that specific “something” can be achieved using whatever form of natural language is covered in the corresponding definition patterns …

With existing info I can grasp the usefulness for evalonce (as per my previous question, to render a macro inside another macro - ex: [Luni]( {{macro-name var1 var2 etc }} ) so I guess one would write it something like this: {{evalonce [Luni]({{var this monday}})}}?

I can also see the potential to have a macro to create an empty Xcolumns x Yrows Markdown Table where Cells can be edited without going to block edit mode, in a “live” manner. I have no idea yet how that could possibly be triggered ({{evalonce table, 4x3, zeroed}} ??)… Another idea: importing a CSV into a table ??

I am also curious how can this be used to:

  • make a natural language querying environment, where complex queries are created behind the scenes from the natural language provided to the macro… ;
  • creating multi-block “templates” just like we can now call the (single)block templates;
  • call templates from an evalonce so the daily template would just contain an evalonce call that calls Weekday template if it’s a week day, Weekend template if it’s weekend, etc … (?)

I can’t wrap my head around how the above would work and even if my assumptions are correct so I am eagerly waiting for any discussions/examples/videos that anybody would post so we better understand how this works and what is possible.

  • Everything you mentioned is possible.
    • Some of them already tested and working.
    • Some of them needing some integration (e.g. file system stuff).
  • Actually the current problem is the inverse (i.e. how to prioritize among the endless possibilities).
  • But this is not just “a better macro”:
    • Forget things like {{evalonce [Luni]({{var this monday}})}}
    • Should instead look like {{evalonce link for text "Luni" and url (date for this Monday}}
      • Could still compress it to less and shorter words, but that’s not the point.
        • Could even have an old-fashioned macro make a call to Synthesis.
      • But the true power is in synthesizing smaller functionalities to:
        • automate most of the boring part of your work
        • abstract the generation of whole interfaces

Ideally when I write Luni during writing a text the system would have already made it into a markdown link just as we can do with text expanders :Luni goes straight to link but even without having to think about it.

Other automation I can think of is automatically populating some properties of my blocks. I use a dozen block templates to capture anything from Logs to Info to Video to Book to whatnot and I have a certain number of properties that go with each input data and I would like to define somewhere in Logseq:

  • if the block is of type “Info” and any word inside the Header of the block contains something that resembles a namespace, put that as the namespace property, otherwise look for some page-name inside my graph and, if it resembles that page-name, put that as “related-to::” property. If nothing is found in the header, move to the block body… and so on. Automatically filling some of the properties would drastically cut my time setting up each block. Ideally an AI-type thing that learns from previous such cases and helps with this meta-data “guessing” would be the thing to have;

Each user has his/her own set of automating needs and they will still have to learn to implement them if they are niche…

This is a most excellent project. Thanks for making it available to us.

For my use-case I’ll cross-post from another thread

I took a slightly different approach to this problem by reusing code from the custom macros that replace themselves thread.

My use case

  • I wanted a scheduled task to automatically appear on my journal page each day reminding me to take some vitamins.
  • I use Logseq on both mobile and desktop

So I wrote a macro {{schedule-date-today} }that replaces itsself with SCHEDULED: <todays date> when and only when it is evaluated on a journal page. Otherwise it just behaves as a normal macro.


  • I use :default-templates { :journals } to load a template to the daily journal.
  • I use kits to run custom javascript.


 :default-templates { :journals "daily-journal" }
 :macros {
          :schedule-date-today "<div class='kit' data-kit='expandmacro'>||scheduled today||</div>"

- daily journal template
  template:: daily-journal
  template-including-parent:: false
	- TODO {{icon ef63}} Take vitamins

The :schedule-date-today macro directs kits to run the javascript in the code block contained in the first child block of the page expandmacro.

  logseq.kits.setStatic(function expandmacro(div){
      const blockId = div.closest(".ls-block").getAttribute("blockid");
      const block = logseq.api.get_block(blockId);
      const pageId =;
      const page = logseq.api.get_page(pageId);
      const pageIsJournal = page['journal?'];
      if (!pageIsJournal) { console.log('is not journal'); return; }
      // This date is in the org-mode SCHEDULED format
      const date = new Date();
      const day = date.getDate().toString().padStart(2, '0');
      const month = (date.getMonth() + 1).toString().padStart(2, '0'); 
      const year = date.getFullYear();
      const dayOfWeek = date.toLocaleString('en-US', { weekday: 'short' }); 
      const formattedDate = `${year}-${month}-${day} ${dayOfWeek}`;
      const content = block.content;

      const macroStart = content.indexOf("{{" + div.closest(".macro").dataset.macroName);
      const macroEnd = content.indexOf("}}", macroStart) + 2;

      logseq.api.update_block(blockId, content.slice(0, macroStart) + `SCHEDULED: <${formattedDate}>` + content.slice(macroEnd));
  // {{schedule-date-today}}