Skip to content

Macros

A macro chains several QuickAdd actions into one command you can run from the palette or a hotkey. Instead of running a template, then a capture, then a script by hand, a macro runs them in order and passes data from one step to the next. Reach for a macro when a single choice isn’t enough. Use it to:

  • Ask a question once and reuse the answer across several steps
  • Run your own JavaScript to talk to the Obsidian API or another plugin
  • Branch the workflow based on what you picked or what a script returns
  • Kick off a routine automatically when Obsidian starts

Macros are QuickAdd’s most capable - and most technical - choice type. You don’t need to be a programmer to start: the walkthrough below uses no code at all. The deeper sections assume you’re comfortable with a little JavaScript.

A macro is a list of commands that run one after another. Each macro is paired with a macro choice, the entry that shows up in the QuickAdd menu and gives you something to trigger.

  • Macro choice - the trigger that appears in the QuickAdd menu.
  • Macro - the actual sequence of commands that runs.
  • Commands - the individual steps (Obsidian commands, scripts, AI prompts, and more).
  • Variables - data that one command sets and a later command reads, all within a single run.

A Capture or Template choice can grow into a macro: Add a step at the bottom of its settings turns it into a macro that runs it first. See Do more afterwards on the Capture page.

We’ll build a tiny macro with no code: it opens today’s daily note and drops your cursor at the end, ready to type. Three commands, run as one.

  1. In Settings → QuickAdd, click New choice → Run a sequence of steps. The Macro Builder opens as a page of the settings window; set Name to Open daily note. To reopen the builder later, click the gear on the choice’s row (on a phone, ⋮ → Configure). Going back saves it. (Before QuickAdd 2.30.0, the builder is a dialog; click its name at the top to rename it.)

The sequence builder page: the line saying what the macro does, three numbered steps, Add a step and More settings

  1. Click Add a step → Run a command and pick Daily notes: Open today's daily note.
  2. Click Add a step → Wait to add a wait of 100 ms. The command step doesn’t wait for the daily note to open, so without the pause the cursor moves before the note is there. If the cursor still ends up in the wrong note, click the number under Wait and wait longer.
  3. Click Add a step → Run an editor command and choose Move cursor to file end.
  4. Go back to save it, then run it: command palette → QuickAdd: Run → Open daily note.

Your daily note opens and the cursor sits at the end of the file, ready for the next line - all three steps in a single command. Assign the choice a hotkey (the ⚡ icon, or Obsidian’s Hotkeys settings) once it behaves the way you want.

The page opens with one line that says what the macro does, made from its steps: “Runs ‘Daily notes: Open today’s daily note’, waits 100 ms, …”. It follows every change you make below it.

Under Steps, each step is a numbered row: its name, and under it what it does (“Adds a line at the bottom of Inbox”, “Runs streaks.js”, “Waits 100 ms”). A row’s gear opens its settings; a Create or Add row opens that note step’s own page. Drag a row by its handle to reorder it, or focus the handle and press the up and down arrow keys. The trash can removes the step.

Add a step opens a menu of the steps a macro can hold. The macro’s settings (one-page input, Which day, Run on startup, the command palette, the ribbon and the icon) are under More settings, which opens by itself when one of them is set. See Macro settings.

Pick a step from Add a step. Add as many as you like, in any order.

Step What it does
Create a note Create a note from a template. It is added as a new Template choice inside the macro, and its page opens so you can set it up.
Add to a note Write into a note. It is added as a new Capture choice inside the macro, and its page opens so you can set it up.
Open a note Open an existing file at a formatted path. Supports all format syntax ({{DATE}}, {{VALUE}}, and so on), with tab and split options and a View (as saved, source mode, reading view or Live Preview). It only opens files that already exist (it won’t create one).
Link it Link the note an earlier step wrote ({{NOTE}}) on a new line in the current note. Its settings pick another note, where the link goes, and whether to copy the link too.
Run Templater Run Templater’s Replace templates over the note an earlier step wrote ({{NOTE}}), or over the note its settings name. Does nothing without Templater.
Run a script Run your own JavaScript to reach the Obsidian API, do complex work, or integrate with other plugins. See Add a script step.
Run a command Run any Obsidian command, for example Daily notes: Open today's daily note or Toggle reading view.
Run an editor command Manipulate text in the active editor: copy, cut, paste, paste with format, select the line or a link on it, and move the cursor. See Editor commands.
Ask AI Run an AI prompt to generate or process content. Offered while online features are on; set up a provider first.
Run a choice Run another of your QuickAdd choices - a template, capture, or another macro - so you can reuse existing work and build modular workflows.
If Branch the run based on live data. See Branch with a conditional.
Wait Pause for a set number of milliseconds, useful when a previous step needs time to finish.

Macros don’t contain JavaScript directly. Your code lives either in a .js file inside your vault or in a ```js code block inside a note, and the macro simply runs it. The note option is handy on mobile, where Obsidian cannot open .js files - see User Scripts.

Create a script file such as scripts/my-macro.js, or a note such as Scripts/my-macro.md with your code in a ```js (or ```javascript) block. QuickAdd runs the first matching JavaScript block in a note and ignores the surrounding prose.

To add it, click Add a step → Run a script. That opens QuickAdd’s script picker (not your operating system’s file picker). It lists the .js files and notes-with-a-code-block that Obsidian has already discovered, so it can’t reach files outside the vault or hidden from Obsidian’s index. Each entry shows its full path, and you can search by folder, so same-named scripts such as several view.js files are easy to tell apart.

To run a specific exported function, type the script with the function after :: and press Enter: my-macro::start for scripts/my-macro.js. If two .js files share that name, use its vault path instead; for a note, type its vault path, for example Scripts/my-macro.md::start.

If the script exports more than one function and you don’t name one, QuickAdd asks which export to run. You can also set an output variable name so later commands can reuse the result.

A script step says which file it runs under its name (“Runs my-macro.js”); hover it for the full path. A macro made from the Run a script preset starts with a step that has no file yet: it says No file chosen and offers Choose file, which opens the same script picker. Once the step has a file, its gear opens the script’s settings, starting with Script file and a Change button. See The script step for what each state means and what changing the file resets.

Good to know:

  • To insert text into a note, don’t write it in a script. Use a Template or Capture choice and run it from the macro with Add to a note, Create a note or Run a choice. That’s the intended way to write content, and no YAML frontmatter is required.
  • If your script calls the API of another plugin, that plugin must be installed and enabled in your vault. You don’t need any extra plugin just to run user scripts.

A conditional command lets your macro take one path or another without writing boilerplate JavaScript. Each conditional has:

  • Condition mode - compare a macro variable, or run a script that returns true/false.
  • Variable comparisons - test a variable with operators like equals, contains, less than, greater than, or a basic truthiness check. The value type (text, number, boolean) controls how the two sides are compared. A variable that nothing in the run has set counts as empty: Is falsy takes the Then branch, and every other operator takes the Else branch.
  • Script mode - point to a JavaScript file in your vault (with an optional exported function) that returns a boolean. The script gets the same parameters as any user script, including your macro variables and params.abort.
  • Branch editors - the commands that run when the condition passes (Then) or fails (Else). Each branch is a full command sequence, so you can nest more conditionals or reuse any command type.

To add one:

  1. Click Add a step → If in the macro (or in any branch). The condition’s settings open; define the condition there. The step’s gear opens them again later.
  2. Use the branch buttons to set the steps that run for the Then and Else outcomes. Each branch opens as a page over the macro, led by the If step’s line, with its own steps and Add a step; go back to return to it. (Before QuickAdd 2.30.0, a branch opens in a dialog with Save and Cancel.)

The macro runs the matching branch in order, then continues with the rest of the macro. Branch commands share the same variable map as the outer macro, so they can read or update variables for later steps.

Editor commands manipulate text in the active editor.

Paste with format preserves rich formatting when you paste from an external source. Unlike the standard paste, which handles plain text only, it:

  • Detects HTML in your clipboard
  • Converts it to Markdown using Obsidian’s built-in conversion
  • Preserves formatting like links, bold, italics, headers, and lists
  • Falls back gracefully to plain text when no HTML is available

What that looks like in practice:

You copy You paste
A formatted link from a webpage [Link Text](https://example.com)
Text with bold/italic bold and italic preserved
A bulleted list A proper Markdown list
A table from a website A Markdown table
  • Copy / Cut / Paste - standard clipboard operations.
  • Select active line - select the whole line the cursor is on.
  • Select link on active line - find and select a link on the current line.
  • Move cursor to file start / file end - jump to the beginning or end of the file.
  • Move cursor to line start / line end - jump to the beginning or end of the current line.

A user script extends a macro with custom JavaScript, written either in a .js file or in a ```js code block inside a note. Scripts have access to:

  • The Obsidian app object
  • The QuickAdd API
  • A variables object for passing data between commands

The basic shape is an exported async function - QuickAdd calls it with a params object that carries everything you need:

module.exports = async (params) => {
// Destructure the parameters
const { app, quickAddApi, variables } = params;
// Your code here
console.log("Hello from my macro!");
// Set a variable for use in later commands
variables.myResult = "Some value";
};

Everything else about writing scripts lives in the User Scripts reference:

Every command in a macro shares one temporary variable map for the current run. A user script can write params.variables.bookTitle, and a later Template or Capture command can read it back as {{VALUE:bookTitle}}.

For the full rules - named VALUE prompts, empty values, AI Assistant output variables, and the executeChoice boundary - see Variables and data flow.

When a script exports several functions, QuickAdd normally asks which one to run. You can skip that prompt by naming the function:

  • {{MACRO:MyMacro::option1}} runs option1 directly.
  • {{MACRO:MyMacro::start}} runs the start function.

When a macro has more than one user script, Macro::member picks the script that uniquely exports the requested member across all scripts in the macro. QuickAdd resolves it like this:

  • If exactly one script exports the member, QuickAdd uses it.
  • If no script exports the member, QuickAdd stops and shows an error.
    • Exception: if the macro has no user-script commands at all, QuickAdd can’t satisfy member access - it logs a warning and returns an empty result instead of stopping the macro.
  • If several scripts export the member, QuickAdd stops and lists the conflicting script names instead of guessing.
  • Exception: the convention keys settings, entry, and quickadd (which many scripts export as metadata rather than entrypoints) resolve to the first script that exports them and show a one-time notice pointing at the selector form below, rather than stopping. Use the selector if you need a different script.

When there’s a conflict, target a specific script by name:

  • {{MACRO:MyMacro::Script 1::option1}}

The selector uses the macro command name shown in the editor. If two user-script commands share the same name, rename one before using the selector form.

These are under More settings on the builder page, which opens by itself when one of them is set.

Same Which day setting as a Template. Child choices in the macro inherit that day, so a weekly pack can ask once and then write every template for last week.

Enable this to run a macro automatically when Obsidian starts. Handy for:

  • Creating a daily note automatically
  • Setting up your workspace
  • Running maintenance tasks

Add to command palette is the same switch as the lightning bolt in the choice list. Once it is on, and Which day isn’t Ask each time, Also add “Name (pick a day)” registers a second command that asks which day before the macro runs. See Command palette on the Template page.

Show in ribbon adds an icon to Obsidian’s ribbon that runs the macro, with the choice’s icon and name. It saves as soon as you flip it. To put a button that runs it in a note instead, see Buttons in notes.

Prompt for a book name and write it into today’s daily note (using the MetaEdit plugin):

module.exports = async (params) => {
const { quickAddApi: { inputPrompt }, app } = params;
// Get book name from user
const bookName = await inputPrompt("📖 Book Name");
// Get MetaEdit plugin
const { update } = app.plugins.plugins["metaedit"].api;
// Format today's date
const date = window.moment().format("YYYY-MM-DD");
// Update the daily note
await update("Book", bookName, `Daily Notes/${date}.md`);
};

Ask for a task and a priority, then hand them to a later Template command as variables:

module.exports = async (params) => {
const { quickAddApi, app, variables } = params;
// Get task details
const task = await quickAddApi.inputPrompt("Task description:");
const priority = await quickAddApi.suggester(
["🔴 High", "🟡 Medium", "🟢 Low"],
["high", "medium", "low"]
);
// Set variables for use in template
variables.taskDescription = task;
variables.taskPriority = priority;
variables.taskCreated = new Date().toISOString();
// Create task note using template (in next macro command)
};

Chain several operations: create a folder structure for a topic, then set variables for a later template step to fill an overview note.

module.exports = async (params) => {
const { quickAddApi, app, variables } = params;
// Get research topic
const topic = await quickAddApi.inputPrompt("Research topic:");
// Create folder structure
const vault = app.vault;
const researchFolder = `Research/${topic}`;
// Check if folder exists
if (!await vault.adapter.exists(researchFolder)) {
await vault.createFolder(researchFolder);
await vault.createFolder(`${researchFolder}/Sources`);
await vault.createFolder(`${researchFolder}/Notes`);
}
// Set variables for template
variables.researchTopic = topic;
variables.researchFolder = researchFolder;
// Next commands in macro will create the overview note
};

A sequence runs one step at a time, in order. A step that creates a note or adds to one runs exactly as a Template or Capture choice would, and the note it ends on becomes the run note, {{NOTE}}, for the steps after it. A step that links to, opens, or runs Templater on {{NOTE}} works on that note. When a step stops the run, the steps after it do not run.

A macro stops early in three situations:

  1. You cancel - press Escape or click Cancel in any prompt.
  2. A script errors - an unhandled error is thrown in a user script.
  3. A script aborts on purpose - params.abort() is called.

When a macro stops:

  • Every remaining command is skipped.
  • A message is logged explaining why.
  • For your own cancel and for explicit aborts, no error dialog appears.
  • For a script error, the full error and stack trace are kept for debugging.

Wrap script work in try/catch so a failure is visible and stops the rest of the macro:

module.exports = async (params) => {
try {
// Your code here
} catch (error) {
console.error("Macro error:", error);
new Notice(`Macro failed: ${error.message}`);
throw error; // Re-throw to stop remaining macro commands
}
};

Confirm a required plugin is present before you use it:

module.exports = async (params) => {
const { app } = params;
const requiredPlugin = app.plugins.plugins["plugin-id"];
if (!requiredPlugin) {
new Notice("Required plugin not found!");
return;
}
// Continue with plugin operations
};

Descriptive names keep a macro readable:

  • ✅ variables.projectName
  • ✅ variables.meetingDate
  • ❌ variables.var1
  • ❌ variables.temp

Break a complex macro into smaller, reusable parts:

  • Put distinct operations in separate scripts.
  • Reuse existing choices with Run a choice steps.
  • Keep each script focused on a single purpose.

“Syntax error: unexpected identifier”

  • Usually a JavaScript syntax error in your script.
  • Check for a missing semicolon, bracket, or quote.
  • See issue #417 for detailed solutions.

“Cannot read property of undefined”

  • A plugin or API you’re reaching for doesn’t exist.
  • Add a null check before you use a plugin’s API.
  • Make sure the plugin is enabled before you run the macro.

Variables not passing between commands

  • Use a named placeholder such as {{VALUE:sharedName}}, or set params.variables.sharedName, for values later steps need.
  • Make sure the script runs before the command that reads its variables.
  • See Variables and data flow for the full model.

Macro not appearing in command palette

  • Make sure the macro choice is enabled in settings.
  • Restart Obsidian if you just created the macro.
  • Check that QuickAdd is enabled in Community Plugins.
  1. Test incrementally - build the macro one command at a time, testing each.
  2. Use console.log - log values to the developer console while debugging.
  3. Keep scripts in your vault - so you can version and back them up.
  4. Share macros - export and import macro configurations with other users.
  5. Combine with hotkeys - assign a shortcut to a macro you run often.