A powerful, lightweight markdown note-taking plugin for Neovim that transforms your editor into a comprehensive knowledge management system.
Perfect for developers, researchers, and knowledge workers who want to seamlessly integrate note-taking into their Neovim workflow.
- ✨ Quick Start
- 🚀 Features
- 📦 Installation
- ⚙️ Basic Configuration
- 📖 Usage Guide
- 🔧 Advanced Configuration
- 📄 Template System
- 🛠️ Troubleshooting
- 🤝 Contributing
- 📜 License
1. Install with your favorite plugin manager:
-- lazy.nvim
{
"paris3200/markdown-notes.nvim",
dependencies = { "ibhagwan/fzf-lua" },
config = true,
}
2. Start taking notes immediately:
<leader>nd
- Create today's daily note<leader>nn
- Create a new note<leader>nf
- Find existing notes<leader>ns
- Search note contents
3. Create your first template (optional):
mkdir -p ~/notes/templates
echo "# {{title}}\n\nCreated: {{date}}" > ~/notes/templates/basic.md
That's it! You're ready to start building your knowledge base.
- 📅 Daily Notes - Quick creation and navigation with automatic templating
- 📝 Template System - Flexible templates with variable substitution (
{{date}}
,{{time}}
,{{title}}
, etc.) - 🔗 Wiki-style Links - Create and follow
[[note-name]]
links between notes - 🔄 Smart Renaming - Rename notes and automatically update all references with file preview
- 🔍 Powerful Search - Find notes by filename or content with syntax highlighting
- ↩️ Backlinks - Discover which notes reference the current note
- 🏢 Multi-Workspace Support - Manage multiple independent note vaults
- 🏷️ Tag Management - Search and organize by frontmatter tags
- ⚡ High Performance - Built for speed with fuzzy finding via fzf-lua
- 🎨 Highly Configurable - Customize paths, keybindings, and behavior
- Neovim >= 0.8.0
- fzf-lua - Required for fuzzy finding
lazy.nvim (Recommended)
{
"paris3200/markdown-notes.nvim",
dependencies = { "ibhagwan/fzf-lua" },
config = function()
require("markdown-notes").setup({
-- your configuration here
})
end,
}
use {
"paris3200/markdown-notes.nvim",
requires = { "ibhagwan/fzf-lua" },
config = function()
require("markdown-notes").setup()
end
}
Plug 'ibhagwan/fzf-lua'
Plug 'paris3200/markdown-notes.nvim'
" In your init.lua or after/plugin/markdown-notes.lua
lua require("markdown-notes").setup()
The plugin works out of the box with sensible defaults. Here's a minimal setup:
require("markdown-notes").setup({
vault_path = "~/notes", -- Where your notes live
templates_path = "~/notes/templates", -- Where your templates live
dailies_path = "~/notes/daily", -- Where daily notes go
})
All keybindings use <leader>n
as the prefix for easy discovery:
Keybinding | Action | Description |
---|---|---|
<leader>nd |
Daily note (today) | Create/open today's daily note |
<leader>ny |
Daily note (yesterday) | Open yesterday's daily note |
<leader>nt |
Daily note (tomorrow) | Open tomorrow's daily note |
<leader>nn |
New note | Create a new note |
<leader>nc |
New note from template | Create note with template selection |
<leader>nf |
Find notes | Search and open existing notes |
<leader>ns |
Search notes | Search within note contents |
<leader>nl |
Insert link | Search for note and insert wiki-link |
<leader>np |
Insert template | Insert template at cursor |
<leader>ng |
Search tags | Find notes by frontmatter tags |
<leader>nb |
Show backlinks | Show notes linking to current note |
<leader>nr |
Rename note | Rename note and update all references with preview |
<leader>nw |
Pick workspace | Switch between workspaces |
gf |
Follow link | Follow link under cursor |
💡 Tip: All keybindings can be customized in your configuration.
Daily notes are the heart of many note-taking workflows. Start your day by creating today's note:
<leader>nd → Creates/opens today's daily note (e.g., 2025-01-15.md)
If you have a Daily.md
template, it will be automatically applied. Otherwise, a basic note with frontmatter is created.
<leader>nn → Create a new note with your default template
<leader>nc → Choose from available templates
<leader>nf → Fuzzy find notes by filename (with live preview)
<leader>ns → Search inside note contents (with syntax highlighting)
<leader>ng → Search by frontmatter tags
<leader>nl → Search for a note and insert [[wiki-link]]
Press Ctrl+L to just insert the link
Press Enter to open the note
gf → Follow the link under cursor
<leader>nb → Show all notes that link to current note (backlinks)
<leader>nr → Rename current note and update all references
Smart Renaming: When you rename a note that has links pointing to it, markdown-notes.nvim will:
- Show you a preview of all files that will be updated
- Let you browse through them with fzf-lua
- Update all
[[note-name]]
and[[note-name|display text]]
references automatically - Handle files in subdirectories correctly
💡 Tip: You can disable the preview and use a simple confirmation dialog by setting
ui.show_rename_preview = false
in your configuration.
Templates make your notes consistent and save time:
<leader>np → Insert template at cursor position
Example template (~/notes/templates/meeting.md
):
---
title: {{title}}
date: {{date}}
tags: [meetings]
---
# {{title}}
**Date:** {{datetime}}
**Attendees:**
## Agenda
## Notes
## Action Items
- [ ]
Command | Description |
---|---|
:MarkdownNotesRename [name] |
Rename current note and update references |
:MarkdownNotesWorkspaceStatus |
Show current workspace |
:MarkdownNotesWorkspacePick |
Switch workspace with fuzzy finder |
:MarkdownNotesWorkspaceSwitch <name> |
Switch to specific workspace |
require("markdown-notes").setup({
-- Core paths
vault_path = "~/notes",
templates_path = "~/notes/templates",
dailies_path = "~/notes/daily",
weekly_path = "~/notes/weekly",
notes_subdir = "notes",
-- Template settings
default_template = "basic", -- Auto-apply this template to new notes
-- UI behavior
ui = {
show_rename_preview = true, -- Show file preview when renaming notes with links
},
-- Custom template variables
template_vars = {
date = function() return os.date("%Y-%m-%d") end,
time = function() return os.date("%H:%M") end,
datetime = function() return os.date("%Y-%m-%d %H:%M") end,
title = function() return vim.fn.expand("%:t:r") end,
yesterday = function() return os.date("%Y-%m-%d", os.time() - 86400) end,
tomorrow = function() return os.date("%Y-%m-%d", os.time() + 86400) end,
-- Add your own custom variables
author = function() return "Your Name" end,
project = function() return vim.fn.getcwd():match("([^/]+)$") end,
},
-- Customize keybindings
mappings = {
daily_note_today = "<leader>nd",
daily_note_yesterday = "<leader>ny",
daily_note_tomorrow = "<leader>nt",
new_note = "<leader>nn",
new_note_from_template = "<leader>nc",
find_notes = "<leader>nf",
search_notes = "<leader>ns",
insert_link = "<leader>nl",
insert_template = "<leader>np",
search_tags = "<leader>ng",
show_backlinks = "<leader>nb",
follow_link = "gf",
rename_note = "<leader>nr",
},
})
Workspaces allow you to manage multiple independent note vaults simultaneously. Perfect for separating work notes, personal notes, and project-specific documentation.
-- Base configuration (becomes your default workspace)
require("markdown-notes").setup({
vault_path = "~/notes",
templates_path = "~/notes/templates",
default_workspace = "personal", -- Optional: specify default
})
-- Work workspace
require("markdown-notes").setup_workspace("work", {
vault_path = "~/work-notes",
templates_path = "~/work-notes/templates",
dailies_path = "~/work-notes/standups",
default_template = "work-note",
template_vars = {
project = function() return "Current Project" end,
sprint = function() return "Sprint-" .. os.date("%U") end,
},
})
-- Research workspace
require("markdown-notes").setup_workspace("research", {
vault_path = "~/research/papers",
templates_path = "~/research/templates",
dailies_path = "~/research/lab-notes",
default_template = "research-paper",
})
- Switch workspaces: Use
<leader>nw
to pick from available workspaces - Persistent context: All commands use the active workspace until you switch
- Independent settings: Each workspace has its own paths, templates, and variables
~/notes/ # Main vault
├── templates/ # Your templates
│ ├── Daily.md # Auto-applied to daily notes
│ ├── meeting.md # Meeting template
│ └── project.md # Project template
├── daily/ # Daily notes
│ ├── 2025-01-15.md
│ └── 2025-01-16.md
└── notes/ # Regular notes
├── project-ideas.md
└── learning-resources.md
Templates are markdown files with special {{variable}}
syntax that gets substituted when creating notes.
Variable | Output | Example |
---|---|---|
{{date}} |
Current date | 2025-01-15 |
{{time}} |
Current time | 14:30 |
{{datetime}} |
Date and time | 2025-01-15 14:30 |
{{title}} |
File name without extension | meeting-notes |
{{yesterday}} |
Yesterday's date | 2025-01-14 |
{{tomorrow}} |
Tomorrow's date | 2025-01-16 |
Add custom variables in your configuration:
require("markdown-notes").setup({
template_vars = {
author = function() return "Your Name" end,
project = function() return vim.fn.getcwd():match("([^/]+)$") end,
week = function() return os.date("Week %U") end,
uuid = function() return vim.fn.system("uuidgen"):gsub("\n", "") end,
},
})
Daily Note Template (templates/Daily.md
):
---
title: Daily Note - {{date}}
date: {{date}}
tags: [daily]
---
# {{date}} - Daily Notes
## 🎯 Today's Goals
- [ ]
## 📝 Notes
## 🔄 Tomorrow's Prep
- [ ]
Meeting Template (templates/meeting.md
):
---
title: {{title}}
date: {{date}}
tags: [meeting]
attendees: []
---
# {{title}}
**Date:** {{datetime}}
**Attendees:**
## 📋 Agenda
## 📝 Discussion Notes
## ✅ Action Items
- [ ]
## 🔗 Links
Solution: Install fzf-lua dependency:
-- lazy.nvim
dependencies = { "ibhagwan/fzf-lua" }
-- packer.nvim
requires = { "ibhagwan/fzf-lua" }
Checklist:
- Verify
templates_path
exists and contains.md
files - Check template syntax uses
{{variable}}
(not{variable}
) - Ensure template file permissions are readable
Solution: Ensure you're using the correct link format:
- ✅
[[note-name]]
or[[note-name.md]]
- ❌
[note-name](note-name.md)
(standard markdown links)
Solutions:
- Use
.gitignore
to exclude non-note files from searches - Consider splitting large vaults into multiple workspaces
- Ensure fzf-lua is properly configured
Check your configuration:
:lua print(vim.inspect(require("markdown-notes").config))
Check current workspace:
:MarkdownNotesWorkspaceStatus
We welcome contributions! Here's how to get started:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Make your changes
- Test your changes (run tests if applicable)
- Commit your changes (
git commit -m 'feat: add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
# Clone your fork
git clone https://github.com/yourusername/markdown-notes.nvim.git
cd markdown-notes.nvim
# Install luacheck for linting (optional but recommended)
luarocks install luacheck
# Run tests
make test
# Run linting
make lint
# Fix linting issues with detailed output
make lint-fix
This project includes several development tools accessible via the Makefile:
make test
- Run all tests using plenary.nvimmake lint
- Run luacheck linter on source and test filesmake lint-fix
- Run luacheck with detailed output for fixing issuesmake clean
- Clean up temporary filesmake check-deps
- Verify required tools are installed
The project uses GitHub Actions for CI, which automatically runs both tests and linting on all pull requests.
- Use GitHub Issues for bug reports and feature requests
- Include your Neovim version and plugin configuration
- Provide steps to reproduce the issue
MIT License - see LICENSE file for details.
Happy note-taking! 📝
If you find this plugin useful, consider ⭐ starring the repository!