Org Mode is a powerful tool that gives you many ways to mark tasks with relevant information like tags, dates, deadlines, priorities. But once you’ve done that, how do you make all of this information work to your advantage?
The answer: Org Mode’s agenda views, or more specifically, the ability to create your own custom agenda views.
The Org Mode agenda is a special kind of buffer that aggregates headings and TODO items from your Org Mode files to make it much easier to see and act on them all. Custom agenda views are basically saved searches that pull together the corresponding items from your various Org agenda files based on the query you provide.
Agenda views are a key feature of Org Mode and essential for building a personal productivity workflow. In this article, I’ll show you how to build 5 custom Org agenda views that will be useful for this purpose.
¶Tasks With a Specific Tag
In a previous video, I described a task tagging strategy where you define a set of “context tags” which apply to the places, devices, and activities that are relevant to your daily life so that you can tag all tasks with respect to one or more contexts.
This strategy becomes far more useful when you define a custom agenda view that targets a specific context tag or list of context tags that are relevant together.
We start by setting the org-agenda-custom-commands
variable which is a list with a special format understood by the org-agenda
command. If you want more details on this format, use C-h v
(or describe-variable
) to read the built-in documentation on org-agenda-custom-commands
.
For this agenda view, we will be using the tags-todo
agenda type because it is specifically intended for searching all TODO items in your org-agenda-files
list for those with a specific tag. Here is an example configuration to start us off:
(setq org-agenda-custom-commands '(("p" "Planning" tags-todo "+@planning")))
Here we define a custom agenda view titled “Planning” with a hotkey of “p” which uses tags-todo
to look for all TODO items with the @planning
tag, effectively building an agenda view for all of our planning tasks. The "+"
at the beginning of the @planning
tag is a special syntax that says “include all TODO items that contain this tag.
You can search for items which contain multiple tags using additonal "+"
syntax entries, and you can even exclude entries that contain specific tags using the "-"
syntax. For example:
(setq org-agenda-custom-commands '(("p" "Planning" tags-todo "+@planning+@work-@computer")))
This query will return any TODO items that are tagged @planning
and @work
while excluding any of those that also contain @computer
. I’m sure you can think of a few ways that this kind of view might be useful!
¶Untagged Tasks
Another aspect of the context tagging strategy that I described is the need to ensure that all of your TODOs contain at least one context tag so that they will show up when you are working in one of the related contexts. But how do you ensure that all of your tasks are tagged? Thankfully, Org’s agenda views can help with this!
We can use the match pattern syntax provided by the tags-todo
agenda type to find any TODOs with no tags:
(setq org-agenda-custom-commands '(("u" "Untagged Tasks" tags-todo "-{.*}")))
This particular query syntax, "-{.*}"
may seem a little strange, but let’s break it down: the first character is -
which indicates that we are excluding any TODOs with the following tag string. After that we see some characters inside of matching curly braces, { }
, indicating a match pattern. Inside of these braces, we see .*
which means “any character, any number of times”.
Effectively, this pattern means “exclude any TODO item that contains a tag containing any characters”. When you use M-x org-agenda
to execute this agenda entry (mapped to the “u” key), it will show you a list of all TODO items across your org-agenda-files
that have no tags!
Now that you’ve got the list of all untagged tasks, you can use N
and P
or the usual navigation keys to move through the TODO list and then use C-c C-q
(org-set-tags-command
) on each item to set the necessary tags.
Once you’ve added tags to a few items, you can press the letter g
in the view to have it be refreshed to remove the newly tagged items.
¶Combining Agenda views
It might actually be useful to combine the @planning
view we looked at before with the untagged tasks view so that you can scan over these tasks together as part of a planning workflow.
The org-agenda-custom-commands
list format provides a more advanced form which enables multiple agenda sections to be combined into a single view, it just requires each section’s configuration to be expressed as a sub-list of each main agenda view.
Here is such a combined view:
(setq org-agenda-custom-commands '(("p" "Planning" ((tags-todo "+@planning") (tags-todo "-{.*}")))))
Instead of having tags-todo "@planning"
follow the agenda title "Planning"
, we now have a list of lists in its place, each of which specifying the details of an agenda section in the same format as we have been looking at until now.
The first sub-list contains the tags-todo "@planning"
filter text and the second sub-list contains the untagged tasks view configuration, tags-todo "-{.*}"
.= When we execute the "p"
agenda with M-x org-agenda
, we now see two sections in the agenda view corresponding to the queries we configured.
This view can be improved slightly, though. Right now, we just see the details of the filter queries applied to each section right above the TODO items that are returned by the query. What if we wanted to title each section so that we remember what we’re looking at?
Luckily, the org-agenda-custom-commands
format also enables us to customize the values of Emacs Lisp variables which get used during agenda view generation. One specific variable that can be useful here is the org-agenda-overriding-header
variable which controls the display title for a given agenda section.
Let’s take a look at how we can set this value for each agenda section in the Planning view:
(setq org-agenda-custom-commands '(("p" "Planning" ((tags-todo "+@planning" ((org-agenda-overriding-header "Planning Tasks"))) (tags-todo "-{.*}" ((org-agenda-overriding-header "Untagged Tasks")))))))
When we run this agenda view, we will now see the two sections titled with the strings that we supplied to org-agenda-overriding-header
at the time that those sections were generated.
Keep in mind that any Emacs variable can be customized in this section and you can add multiple sub-lists to customize as many variables as you like! We will take a look at a few more examples of this now.
¶Tasks from a Specific File
This next example takes all of the TODO items from a specific Org file (or files) and creates an agenda view containing those items.
It may seem strange to do this because you can just view all of the TODO items from a single file inside of the buffer for that file, but there is actually a good reason for doing this: the efficiency of acting on each item from that file especially when it comes to bulk actions, an extremely efficient workflow that we will talk about in a future video.
So how does this apply to a productivity workflow? I personally think it can be really useful to have an “Inbox” file which is a place where you collect all new tasks, notes, or reminders that occur to you while you’re working.
Instead of opening a specific note file just to write down a task or note, you can just open your Inbox file and write it down. Even better, use Org’s capture template system to make it easy to capture these items directly to the Inbox file without having to open it! We’ll cover the capture system in another article.
Side note: You will want a refiling setup for use with an Inbox file! Items in this file are not meant to be kept here permanently, you will want to move them to other Org files as part of your inbox processing workflow. We will talk about refiling another article.
Let’s take a look at how to set up this kind of agenda view:
(setq org-agenda-custom-commands '(("i" "Inbox" ((todo ".*" ((org-agenda-files '("~/Notes/Inbox.org")) (org-agenda-overriding-header "Unprocessed Inbox Items")))))))
Here, we’re using a different kind of agenda type called todo
which accepts a string for the TODO state(s) that we want to include in the agenda output. Here we once again use the .*
pattern syntax to indicate that we want to include TODO items of any state, even DONE!
The most important part of the configuration is the setting for org-agenda-files
as it dictates which files get consulted for generating the agenda view. Normally you would have org-agenda-files
set to the list of all files that you want included in your agenda views, but here we are focused only on a single file and this customization tells org-agenda
to only consult the Inbox.org
file.
Inbox management could also be a useful thing to do while looking at your planning agenda, so let’s add it there too:
(setq org-agenda-custom-commands '(("p" "Planning" ((tags-todo "+@planning" ((org-agenda-overriding-header "Planning Tasks"))) (tags-todo "-{.*}" ((org-agenda-overriding-header "Untagged Tasks"))) (todo ".*" ((org-agenda-files '("~/Notes/Inbox.org")) (org-agenda-overriding-header "Unprocessed Inbox Items")))))))
¶Daily Agenda
Another useful kind of agenda view is the daily agenda, one that aggregates all TODO items that are relevant for the present day. This includes tasks that are scheduled for the current day (or one that has already passed) and tasks that have a deadline that is coming due within a predetermined number of days.
We can create this kind of agenda view using the agenda
type in our org-agenda-custom-commands
configuration:
(setq org-agenda-custom-commands '(("d" "Daily Agenda" ((agenda "" ((org-agenda-span 'day)))))))
This agenda view is explicitly configured to set org-agenda-span
to the value 'day
so that we only see the current day in the generated view (the default is 'week
which shows the whole week).
If we run this view with M-x org-agenda
and press the letter "d"
, we will see a listing of all the TODO items that are scheduled for today or have deadlines soon.
If we want to have a different range of time for when TODO items with deadlines show up on the agenda, we can set the org-deadline-warning-days
variable, either globally or in the agenda section itself:
(setq org-agenda-custom-commands '(("d" "Daily Agenda" ((agenda "" ((org-agenda-span 'day) (org-deadline-warning-days 7)))))))
This will make TODO items with deadlines coming due within 7 days show up in the daily agenda (the default is 14 days). You should adjust this value depending on how early you want to be notified about things that may be due!
The daily agenda view is a great starting point for reviewing the most important tasks you need to accomplish for the day, potentially first thing in the morning.
Another valuable section you could add to this view is to display any high priority tasks that do not have a date attached. Org TODO items can have an associated priority value (A, B, or C) which can indicate the level of priority for the item. Here’s how you can add a section to the daily agenda to show all tasks marked with the “A” priority level:
(setq org-agenda-custom-commands '(("d" "Daily Agenda" ((agenda "" ((org-agenda-span 'day) (org-deadline-warning-days 7))) (tags-todo "+PRIORITY=\"A\"" ((org-agenda-overriding-header "High Priority Tasks")))))))
In this new section, we use the tags-todo
agenda type to add a filter on the PRIORITY
property for each TODO item. We are adding a filter (using "+"
) which includes any tasks where PRIORITY="A"
and then giving the section a title of “High Priority Tasks”.
Now to set priority on any of your tasks, Use C-c ,
(org-priority
). Personally, though, I don’t even bother with B or C because you really only need to mark a task as “high priority” and let the other types of metadata (dates and tags) serve their own purpose.
¶Weekly Review
The last agenda view that we’ll cover is one for conducting a weekly review to see what you accomplished during the week and look at the scheduled or deadline tasks that you were not able to complete.
This view can also be useful to see where your time was spent if you use Org’s task clocking feature, something that we will discuss in a future video.
There are two important variables to configure before this agenda view will work correctly:
org-log-done
must be set to'time
so that each completed task receives a timestamp indicating when it was moved to theDONE
state.org-agenda-start-with-log-mode
must be set tot
so that the log view is included with the weekly agenda items, showing the exact times when tasks were completed throughout each day (this may also be useful in the Daily Agenda view!)
Let’s take a look at the configuration for the Weekly Review agenda and then we’ll discuss it line by line:
(setq org-log-done 'time) (setq org-agenda-start-with-log-mode t) (setq org-agenda-custom-commands '(("w" "Weekly Review" ((agenda "" ((org-agenda-overriding-header "Completed Tasks") (org-agenda-skip-function '(org-agenda-skip-entry-if 'nottodo 'done)) (org-agenda-span 'week))) (agenda "" ((org-agenda-overriding-header "Unfinished Scheduled Tasks") (org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done)) (org-agenda-span 'week)))))))
This agenda configuration has two sections, one for “Completed Tasks” and the other for “Unfinished Scheduled Tasks”.
The “Completed Tasks” section is an agenda
type view which has org-agenda-span
set to 'week
(this is the default value so it may not be needed in your case!). This will ensure you see agenda items for each day of the present week. It also configures the org-agenda-skip-function
variable to determine which TODO items should be skipped when generating this view.
It is worth looking at the documentation for this variable for more information to give you more ideas on how it could be used, but in this case, we are using the org-agenda-skip-entry-if
filter function to explicitly skip TODO items that match a specific criteria expressed as the symbols 'nottodo
and 'done
: this filters out any task that is not of TODO state DONE
, i.e. it filters out any uncompleted tasks.
The next section also uses org-agenda-skip-function
and org-agenda-skip-entry-if
to filter out any items that are in the DONE
state, meaning that they were completed during the week. This effectively splits our tasks for the week into two buckets, those that were done and those that should have been done because they were scheduled.
Using this view can be a great way to close out the week to get a feeling of accomplishment for what you finished while also taking stock of the things you may need to reschedule or re-evaluate for the following week.
¶Final Configuration and Example Files
I hope these custom agenda views will be helpful for your personal productivity workflow with Org Mode!
You can experiment with the final configuration and example files by downloading and unpacking this ZIP file:
https://systemcrafters.net/files/org-custom-agenda-views.zip
You can also read the final agenda configuration here:
(global-set-key (kbd "C-c o a") #'org-agenda) (setq org-log-done 'time) (setq org-agenda-start-with-log-mode t) (setq org-agenda-custom-commands '(("p" "Planning" ((tags-todo "+@planning" ((org-agenda-overriding-header "Planning Tasks"))) (tags-todo "-{.*}" ((org-agenda-overriding-header "Untagged Tasks"))) (todo ".*" ((org-agenda-files '("~/Notes/Inbox.org")) (org-agenda-overriding-header "Unprocessed Inbox Items"))))) ("d" "Daily Agenda" ((agenda "" ((org-agenda-span 'day) (org-deadline-warning-days 7))) (tags-todo "+PRIORITY=\"A\"" ((org-agenda-overriding-header "High Priority Tasks"))))) ("w" "Weekly Review" ((agenda "" ((org-agenda-overriding-header "Completed Tasks") (org-agenda-skip-function '(org-agenda-skip-entry-if 'nottodo 'done)) (org-agenda-span 'week))) (agenda "" ((org-agenda-overriding-header "Unfinished Scheduled Tasks") (org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done)) (org-agenda-span 'week)))))))