Harrison Milburn295 downloadsPlan projects from your Markdown tasks with a dependency graph, Gantt chart with critical path, kanban board, and cost tracking.
Project Planner turns the tasks you already have in your Obsidian vault into a plan you can actually run a project from — a dependency graph, a Gantt chart with a real critical path, a kanban board, and time-and-materials costing.
Nothing lives in a proprietary database. Every task, date, dependency, person and cost is read from and written back to plain Markdown, in the same Tasks and Dataview syntax you were already using. Delete the plugin and your notes still read exactly the same.
Most task plugins are good at capture and weak at planning. They will tell you what is due on Thursday. They will not tell you which of the eleven things due on Thursday is the one that, if it slips, moves your delivery date.
Project Planner is built around dependencies rather than dates:
| Plugin | Required? | Why |
|---|---|---|
| Dataview | Yes | Reads inline - [ ] checkbox tasks out of your vault. The views refuse to render without it. |
| Tasks | Optional | Adds Create task and Edit task to the node context menu, using the Tasks plugin's own modal. |
Obsidian 1.8.0 or newer. Works on desktop and mobile — no Node or Electron APIs are used.
Settings → Community plugins → Browse, search for Project Planner, install and enable.
main.js, manifest.json and styles.css from the
latest release.<your-vault>/.obsidian/plugins/project-planner/.Write two ordinary tasks in any note, and give one an ID the other can point at:
- [ ] Draw up the frame 🆔 frame 🛫 2026-03-02 📅 2026-03-06
- [ ] Weld the frame ⛔ frame 📅 2026-03-11
- [ ] Paint it ⛔ weld
- [ ] Ship it 🆔 ship ⛔ paint
Open the graph icon in the ribbon. You get four connected nodes. Open the Gantt icon and you get four bars — the two with dates drawn where you put them, the two without drawn dashed, placed the day after whatever blocks them.
Turn on Critical path and the chain that decides the finish date lights up.
Two kinds of task, both first-class, both read into the same model.
Any - [ ] line anywhere in your vault, read via Dataview.
- [ ] Fit the sensor loom 🆔 loom ⛔ frame 🛫 2026-03-02 📅 2026-03-06 #electrical 🔼
Statuses come from the checkbox itself: [ ] to do, [/] in progress,
[x] done, [-] cancelled.
A whole note becomes one task when its frontmatter is tagged task. Use these
when a task needs somewhere to put detail.
---
tags:
- task
start: 2026-03-02
due: 2026-03-06
blockedBy: [frame]
progress: 40
---
Dependencies and metadata can be written as Tasks-plugin emoji or as Dataview inline fields, and the two can be mixed freely across a vault. Project Planner reads whichever a line already uses and writes back in the same style, so it never reformats notes into a house style you did not choose.
- [ ] Emoji style 🆔 a1 ⛔ b2 📅 2026-03-06
- [ ] Dataview style [id:: a1] [dependsOn:: b2] [due:: 2026-03-06]
The dependency graph, on a pannable ReactFlow canvas with automatic dagre layout.
Each task is a node showing its text, status, tags, priority and progress. Node controls:
| Control | Action |
|---|---|
| Status circle | Cycle the task's completion status |
| Star | Star or unstar the task (⭐) |
| Arrow | Open the note the task lives in |
| ⋮ | Context menu — create a child task, edit, set finance, delete |
| Tag chips | Click × to remove a tag, + Add tag to add one |
Wiki-links in task text render as real links and open the note they point at.
Drag from a node handle to another node to create a dependency; the ⛔/🆔
metadata is written into both notes. Click an edge and press Delete to remove
it, which strips the reference from the vault.
Chain healing: deleting a task in the middle of A → B → C hands B's
blockers to whatever was waiting on B, so A → C survives rather than the
chain quietly falling apart.
Per-edge styling: right-click an edge to override its line style individually, on top of the global Bezier / Straight / SmoothStep setting.
Toggle Critical path to compute float across the whole plan — the gap between when each task finishes and the latest it could finish without moving the delivery date. Tasks with zero float are the critical path and are highlighted in both the graph and the Gantt. This is the set worth arguing about when a deadline is at risk; everything else has room to move.
The right-hand filter panel carries:
| Filter | Description |
|---|---|
| Search | By task text, ID or tag, with a live suggestion list (↑/↓/Enter/Escape) |
| Show dependencies / dependents | Pull the upstream and/or downstream chain of a search hit into view |
| Status | Multi-select — to do, in progress, done, cancelled |
| Include labels | Show only tasks carrying at least one selected tag |
| Exclude labels | Hide tasks carrying any selected tag |
| Files / folders | Restrict to selected notes or folders |
| Only starred | Show only starred tasks |
Filter presets save any combination by name, restore it in one click, and can be inserted straight into a note as an embed block.
Tasks with no dependencies in either direction are moved to a left-hand panel rather than scattered across the canvas as noise. Search it, then drag any task onto the canvas to work with it. Connect it to something and it stays on the canvas for good.
Tasks sharing a project are drawn inside a labelled group container, so a large vault reads as a handful of projects rather than one undifferentiated cloud.
The same tasks on a timeline.
A chart that only drew dated tasks would be nearly empty in a real vault, so every task is placed:
| The task has | The bar is |
|---|---|
Start (🛫, or ⏳ as fallback) and due (📅) |
Drawn between them |
| Start only | One day long |
| Due only | Drawn from when its blockers finish up to the deadline — the window available |
| Neither | Starts the day after its last blocker finishes, lasts one day |
Because inference walks the dependency graph, a chain of undated tasks spreads out day by day in the order the graph says they must happen.
Inferred bars are drawn as dashed outlines and nothing is written to your notes while they stay that way. Turn a proposal into a real date by dragging or resizing the bar, or use Apply suggested dates to write every dashed bar at once and get a count of what changed.
| Action | Result |
|---|---|
| Drag a bar sideways | Moves start and due together, keeping the length |
| Drag either edge | Changes that date alone |
| Click a task name | Selects the row and lights up its whole dependency chain |
| Drag the grip | Reorders rows; the order is saved |
| + on a row | Writes a new task directly below it, in the same note |
| Link on a row | Starts a dependency — click the task it should block |
Dragging snaps to whole days and a bar can never be shorter than one day.
Named days marked across the whole chart — "Design freeze", "Release 1.0" — drawn as a diamond in their own band with a dashed line down the timeline. Milestones are not tasks: no note, no duration, no dependencies, and nothing is ever written to your vault. Past milestones draw grey, today's orange, future ones purple. The timeline always stretches far enough to reach them.
Three ways a plan quietly goes wrong, flagged on the rows and counted under the chart:
| Warning | Meaning |
|---|---|
| Overdue | Finished on paper before today, and not marked done |
| Stalled | Should have started, and has not been touched |
| Backwards dependency | A blocker finishes after the work waiting on it |
Only dates you actually wrote are judged — flagging the chart's own guesses would be noise.
Export the chart as a picture for a report or a slide. This is deliberately not a screenshot: it is a separate drawing of the same plan, laid out for paper. The whole timeline is fitted to the page width so nothing runs off the edge; it uses four print-safe colours; it drops weekend bands, day ticks and dependency arrows, which are the first things to become noise at page size; and it makes the parts that carry meaning bigger — title, span, group headings, milestone names, and a legend listing only what the chart actually uses. Today is marked only when today falls inside the plan.
Where the Gantt answers when, the board answers what state is this in — and dragging a card is what changes the answer.
| Group by | Columns | Dragging writes |
|---|---|---|
| Status | To do, In progress, Done, Cancelled | The checkbox |
| Due date | Late, Today, Tomorrow, Rest of this week, Next week, Later, No date | The 📅 due date |
| Person | One per name in [people:: …], plus Unassigned |
The task's people |
| Tag | One per tag in use, plus No tag | The task's tags |
| Project | One per project | read only |
| Priority | 🔺 ⏫ 🔼 🔽 ⏬, plus No priority | read only |
| Note | One per note holding tasks | read only |
Dropping a card into a date column writes a real due date, so the card stays where you put it. Dropping one into a person's column makes it theirs alone; dropping it into Unassigned removes the people entirely. Read-only groupings show a padlock and refuse cards from other columns, but cards can still be reordered within them.
Card order is one list for the whole board, so a card dragged to another column keeps the position you dropped it in, and changing the grouping does not throw your arrangement away. Columns can be folded away, remembered per grouping. The + in a column header adds a task already carrying whatever that column stands for.
Time-and-materials planning, off by default — enable it under Settings → Project Planner → Finance.
A task consumes hours; those hours are worked by named people in some proportion; each person sits at a grade with a chargeout rate; and materials sit on top.
Rates live in a note you own, not in plugin settings, so you can link to them
and keep them in version control. Default path Finance/People and rates.md.
| Grade | Rate |
| ---------- | ---- |
| Principal | 145 |
| Senior | 110 |
| Person | Grade | Rate |
| ----------- | --------- | ---- |
| Alice Smith | Principal | |
| Cara Diaz | Senior | 130 |
Tables are found by their column headers, not the headings above them, so
you can retitle, reorder and write whatever prose you like around them. A
person's own Rate overrides their grade's. Rows that cannot be read are
dropped and reported with their line number.
- [ ] Fit the loom [hoursPerDay:: 6] [people:: Alice Smith 60%, Bob Jones 40%] [costs:: Loom kit 240, Travel 85]
| Field | Meaning |
|---|---|
hours |
Explicit total hours — wins over everything |
hoursPerDay |
Per-task override of the global default |
people |
Who is on it, and their share |
costs |
Materials and expenses |
Synonyms are accepted (totalHours/estimatedHours → hours,
allocations/who → people, expenses/materials → costs), and shares
may be written 60%, 60 or 0.6. The canonical spelling is written back.
Hours come from an explicit total if there is one, otherwise from the length of
the task's bar × its hours-per-day. Rates resolve person-first, then grade.
Name matching is on meaning, not typing — Alice Smith, alice smith and
[[Alice Smith]] are one person.
What it refuses to fake:
Headline tiles (total, labour, materials, hours, tasks priced, tasks with no finance), the labour/materials split, a breakdown by person, grade, project, tag, status or note, cost over time by week or month, the ten biggest costs each clickable through to the Gantt, and a Problems section listing everything that could not be worked out.
A fenced project-planner block renders a fully interactive graph inline.
```project-planner
{
"filter": {
"onlyStarred": true,
"selectedStatuses": ["todo", "in_progress"]
},
"config": {
"height": 300,
"showMinimap": false,
"showFilterPanel": false
}
}
```
An empty body ({}) shows the full graph with defaults.
Filter keys: selectedTags, excludedTags, selectedStatuses,
selectedFiles, searchQuery, traversalMode
(match/upstream/downstream/both), onlyStarred.
Config keys: height, showMinimap, showFilterPanel,
showPresetsPanel, showUnlinkedPanel, showStatusCounts.
Rather than writing these by hand, use Insert current filter as embedded graph from the command palette, or the insert button on a saved filter preset — both generate correct JSON.
Blocks tagged
```tasks-mapfrom before the rename are still rendered, so notes written against the old plugin keep working.
A checkbox line has nowhere to put detail. With companion notes enabled,
creating a task also creates a note for it and turns the task's text into a
link to that note. The note is deliberately not tagged task — that would
make it a second task in its own right and draw a duplicate node.
Create notes for existing tasks retrofits the whole vault at once, after asking for confirmation and telling you how many notes it will write.
One undo history, owned by the plugin and shared across every view — so the board can take back an edit the Gantt made, and vice versa. Row reorders, card moves, date drags, milestone changes, people reassignments and tag edits all step back through it.
Vault writes are optimistic: the UI updates immediately and rolls back if the write fails.
Everything Project Planner reads and writes. Every emoji field has a Dataview
equivalent in [field:: value] or (field:: value) form.
| Concept | Emoji | Dataview | Note-task frontmatter |
|---|---|---|---|
| Task ID | 🆔 abc |
[id:: abc] |
— (the note is the identity) |
| Blocked by | ⛔ abc or ⛔ a,b,c |
[dependsOn:: abc] |
blockedBy: / dependsOn: |
| Due date | 📅 2026-03-06 |
[due:: 2026-03-06] |
due: |
| Start date | 🛫 2026-03-02 |
[start:: 2026-03-02] |
start: |
| Scheduled | ⏳ 2026-03-02 |
[scheduled:: …] |
scheduled: |
| Created / done / cancelled | ➕ ✅ ❌ |
— | — |
| Priority | 🔺 ⏫ 🔼 🔽 ⏬ |
— | — |
| Starred | ⭐ |
— | — |
| Progress | — | [progress:: 40] or 40% |
progress: |
| Parent | — | [parent:: abc] |
parent: |
| Tags | #tag |
— | tags: |
| Hours | — | [hours:: 12] |
hours: |
| Hours per day | — | [hoursPerDay:: 6] |
hoursPerDay: |
| People | — | [people:: Alice 60%, Bob 40%] |
people: |
| Costs | — | [costs:: Loom kit 240] |
costs: |
Out-of-range progress is clamped rather than rejected — someone who typed 120 meant "finished". Progress above zero displays as in-progress whatever the checkbox says, but 100% is never auto-promoted to done: finishing the work and declaring the task closed are different claims, and only the second is yours to make.
| Command | Description |
|---|---|
| Open graph view | The dependency graph |
| Open Gantt view | The timeline |
| Open board view | The kanban board |
| Open task finance | The finance dashboard (when finance is enabled) |
| Create the rates note | Creates and opens the rates note (when finance is enabled) |
| Create notes for existing tasks | Retrofit companion notes across the vault |
| Insert current filter as embedded graph | Write an embed block into a note |
Ribbon icons are provided for the graph, Gantt, board and — when enabled — finance views.
| Group | Settings |
|---|---|
| Display | Show priorities, show tags, show status counts |
| Layout | Direction (horizontal/vertical), edge style (Bezier/Straight/SmoothStep), SmoothStep corner radius |
| Tag appearance | Palette (Rainbow, Ocean, Forest, Sunset, Mono) plus per-tag colour pinning, with Reset all |
| Task relations | How dependencies are written: CSV, individual, or Dataview |
| Gantt | Working days, critical path, schedule warnings, row order and collapsed rows |
| Board | Group-by, card order, collapsed columns |
| Finance | Enable, rates note path, default hours per day, currency, include suggested dates |
| Companion notes | Enable, and the folder to write them to |
| Language | English, Nederlands, 简体中文 |
| Advanced | Debug visualisation overlays |
Project Planner began life as a fork of Tasks Map by Nico Klaassen, and it owes that project its foundation: the graph canvas, the dual inline/note task model, the Dataview integration and the filtering core all started there. Credit where it is due — that groundwork is what made the rest possible, and it remains MIT licensed with the original copyright notice intact.
It has since grown into a substantially different plugin. Tasks Map visualises tasks as a graph. Project Planner is a project-planning tool that happens to include that graph. Added since the fork:
The name changed because "Tasks Map" no longer described it. This is a planner that reads your tasks, not a map of them.
npm install
npm run dev
| Command | Purpose |
|---|---|
npm run dev |
esbuild watch build |
npm run build |
Typecheck + production bundle |
npm run build:deploy |
Build, then copy into a local vault |
npm test |
Jest |
npm run lint |
ESLint over src/**/*.{ts,tsx} |
npm run lint:css |
Stylelint |
npm run format |
Prettier check |
src/lib/ is pure, framework-free logic — scheduling, critical path, finance,
costing, Gantt rows, filtering and graph traversal — and is where the unit
tests point. React components stay presentational.
Set a deploy target in .obsidian-plugin-dir or $OBSIDIAN_PLUGIN_DIR;
deploy.mjs copies main.js, manifest.json and styles.css and never
touches your vault's data.json.
Contributions are welcome — please open an issue or PR.
MIT.
Copyright © 2025 Nico Klaassen (original Tasks Map plugin) and © 2025–2026 Harrison Milburn (Project Planner). The original copyright notice is retained as the MIT License requires.