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.
What is a macro?
Section titled “What is a macro?”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.
The pieces
Section titled “The pieces”- 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.
Set up your first macro
Section titled “Set up your first macro”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.
Step 1: Create the macro choice
Section titled “Step 1: Create the macro choice”- 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.)

Step 2: Build the macro
Section titled “Step 2: Build the macro”- Click Add a step → Run a command and pick
Daily notes: Open today's daily note. - 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.
- Click Add a step → Run an editor command and choose Move cursor to file end.
- 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 builder page
Section titled “The builder page”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.
The steps you can add
Section titled “The steps you can add”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. |
Add a script step
Section titled “Add a script step”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.
Branch with a conditional
Section titled “Branch with a conditional”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:
- 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.
- 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
Section titled “Editor commands”Editor commands manipulate text in the active editor.
Paste with format
Section titled “Paste with format”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 |
The other editor commands
Section titled “The other editor commands”- 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.
User scripts
Section titled “User scripts”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
appobject - The QuickAdd API
- A
variablesobject 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:
- Where a script can live -
.jsfile or note code block, and which folders QuickAdd’s picker can see. - Prompt the user - input prompts, suggesters, yes/no, and checkbox prompts via
quickAddApi; the full method list is in the QuickAdd API. - Read the editor selection -
quickAddApi.utility.getSelection(). - Reach into other plugins - talk to Templater, MetaEdit, or any plugin through
app.plugins.plugins. - Offer several actions from one script - export more than one function and pick at run time.
- Configurable settings, error handling and
abort(), and a shelf of copy-paste recipes.
Pass data between commands: variables
Section titled “Pass data between commands: variables”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.
Run one export directly: Macro::member
Section titled “Run one export directly: Macro::member”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}}runsoption1directly.{{MACRO:MyMacro::start}}runs thestartfunction.
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, andquickadd(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.
Macro settings
Section titled “Macro settings”These are under More settings on the builder page, which opens by itself when one of them is set.
Which day
Section titled “Which day”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.
Run on startup
Section titled “Run on startup”Enable this to run a macro automatically when Obsidian starts. Handy for:
- Creating a daily note automatically
- Setting up your workspace
- Running maintenance tasks
Command palette
Section titled “Command palette”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
Section titled “Show in ribbon”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.
Practical examples
Section titled “Practical examples”Example 1: Log a book to your daily note
Section titled “Example 1: Log a book to your daily note”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`);};Example 2: Create a task with priority
Section titled “Example 2: Create a task with priority”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)};Example 3: Scaffold a research workspace
Section titled “Example 3: Scaffold a research workspace”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};How a sequence runs
Section titled “How a sequence runs”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.
When a macro stops
Section titled “When a macro stops”What stops a macro
Section titled “What stops a macro”A macro stops early in three situations:
- You cancel - press Escape or click Cancel in any prompt.
- A script errors - an unhandled error is thrown in a user script.
- 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.
Best practices
Section titled “Best practices”1. Handle errors
Section titled “1. Handle errors”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 }};2. Check for plugin dependencies
Section titled “2. Check for plugin dependencies”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};3. Use meaningful variable names
Section titled “3. Use meaningful variable names”Descriptive names keep a macro readable:
- ✅
variables.projectName - ✅
variables.meetingDate - ❌
variables.var1 - ❌
variables.temp
4. Keep it modular
Section titled “4. Keep it modular”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.
Troubleshooting
Section titled “Troubleshooting”Common issues
Section titled “Common issues”“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 setparams.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.
Tips and tricks
Section titled “Tips and tricks”- Test incrementally - build the macro one command at a time, testing each.
- Use
console.log- log values to the developer console while debugging. - Keep scripts in your vault - so you can version and back them up.
- Share macros - export and import macro configurations with other users.
- Combine with hotkeys - assign a shortcut to a macro you run often.
See also
Section titled “See also”- Template Choices - for creating new notes
- Capture Choices - for appending to existing notes
- Format Syntax - available placeholders
- QuickAdd API - detailed API documentation
- Examples - pre-built macro examples