A Neovim plugin for seamless integration with Notion's API, allowing you to edit Notion pages directly within Neovim using markdown syntax.
███╗ ██╗ ██████╗ ████████╗██╗ ██████╗ ███╗ ██╗ ███╗ ██╗██╗ ██╗██╗███╗ ███╗
████╗ ██║██╔═══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║ ████╗ ██║██║ ██║██║████╗ ████║
██╔██╗ ██║██║ ██║ ██║ ██║██║ ██║██╔██╗ ██║ ██╔██╗ ██║██║ ██║██║██╔████╔██║
██║╚██╗██║██║ ██║ ██║ ██║██║ ██║██║╚██╗██║ ██║╚██╗██║╚██╗ ██╔╝██║██║╚██╔╝██║
██║ ╚████║╚██████╔╝ ██║ ██║╚██████╔╝██║ ╚████║ ██╗██║ ╚████║ ╚████╔╝ ██║██║ ╚═╝ ██║
╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═══╝ ╚═══╝ ╚═╝╚═╝ ╚═╝
- Native Notion editing - Edit Notion pages directly in Neovim buffers
- Robust sync system - Diff-based synchronization with intelligent pagination and retry logic
- Markdown support - Full markdown syntax with rich text formatting (bold, italic, code, links)
- Smart debouncing - Prevents API abuse with configurable sync delays
- Image support - Embed and edit images with captions using markdown syntax
- Block preservation - Maintains Notion block structure and ordering
- Rate limiting handling - Automatic retry logic for API rate limits
- Cross-platform - Works on macOS, Linux, and Windows
- Debug mode - Detailed timing information for performance analysis
- Neovim 0.8+
- plenary.nvim
- Notion API integration token
- Notion database ID
Using lazy.nvim:
{
'ALT-F4-LLC/notion.nvim',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
require('notion').setup({
-- Secure token retrieval (recommended)
notion_token_cmd = {"doppler", "secrets", "get", "--plain", "NOTION_TOKEN"},
-- Or rely on NOTION_TOKEN environment variable (no config needed)
database_id = 'your_database_id_here', -- or set NOTION_DATABASE_ID env var
debug = false, -- Enable debug timing info
sync_debounce_ms = 1000, -- Minimum time between syncs
})
end
}
- Create a Notion integration at https://www.notion.so/my-integrations
- Copy the integration token
- Create or find a database in Notion and copy its ID from the URL
- Share the database with your integration
The database ID is in the URL when viewing your database:
https://www.notion.so/workspace/DATABASE_ID?v=...
require('notion').setup({
notion_token_cmd = nil, -- Command to retrieve token (e.g. {"doppler", "secrets", "get", "--plain", "NOTION_TOKEN"})
database_id = nil, -- Database ID (or use NOTION_DATABASE_ID env var)
page_size = 10, -- Number of pages to retrieve in lists
debug = false, -- Show detailed timing information
sync_debounce_ms = 1000, -- Minimum milliseconds between syncs (prevents API abuse)
})
Security First: Tokens cannot be hardcoded in configuration. Choose one of these secure methods:
Option 1: Environment Variable (Simple)
export NOTION_TOKEN="your_token_here"
Option 2: Secret Management Command (Recommended)
require('notion').setup({
-- Using Doppler
notion_token_cmd = {"doppler", "secrets", "get", "--plain", "NOTION_TOKEN"},
-- Using 1Password CLI
notion_token_cmd = {"op", "read", "op://vault/notion/token"},
-- Using AWS Secrets Manager
notion_token_cmd = {"aws", "secretsmanager", "get-secret-value", "--secret-id", "notion-token", "--query", "SecretString", "--output", "text"},
-- Using any custom command
notion_token_cmd = {"your-secret-tool", "get", "notion-token"},
database_id = 'your_database_id_here',
})
- Create pages:
:Notion create <title>
- Create and immediately edit new pages - Browse and edit:
:Notion edit
- Select from existing pages to edit - Save changes:
:w
- Automatically syncs changes back to Notion - Delete pages:
:Notion delete
- Browse and archive pages - Open in browser:
:NotionBrowser
- Open current page in browser
:Notion create <title>
- Create page and open for editing (title required):Notion edit [page_id]
- Browse and select page for editing in Neovim:Notion delete
- Browse and delete (archive) pages
:NotionBrowser
- Open current buffer's page in browser:NotionSync
- Manually sync current buffer (backup for:w
)
:NotionCreate <title>
- Create page (title required):NotionEdit [page_id]
- Edit pages:NotionDelete
- Delete pages
When you edit a Notion page:
-
Automatic buffer setup - Page opens as a markdown buffer with auto-sync
-
Rich formatting support:
**bold**
and*italic*
text`inline code`
formatting# ## ###
headers- [ ]
and- [x]
todo items-
bulleted lists1.
numbered listscode blocks

images
-
Robust sync - Only modified blocks are updated with automatic retry on failures
-
Instant feedback - Success/error notifications after each save
The plugin uses a sophisticated sync system that:
- Intelligent pagination - Handles large documents by fetching all blocks efficiently
- Compares existing vs new content to identify changes with precise diff algorithm
- Only updates modified blocks with automatic retry on API failures
- Preserves block order using Notion's positioning API
- Scales efficiently with document size through smart pagination
- Rate limiting resilience - Automatic backoff and retry for API limits
Typical sync times:
- Small edits (1-2 blocks): 200-800ms
- Large documents: Performance scales with changed content, not total size
- Pagination and retries add minimal overhead while ensuring reliability
Set these instead of hardcoding in config:
NOTION_TOKEN
- Your Notion integration tokenNOTION_DATABASE_ID
- The ID of your Notion database
require('notion').setup({
debug = true, -- Shows detailed timing for each operation
})
Debug output shows:
- API request timing
- Diff calculation performance
- Block operation details
- Total sync time
-
"Notion token not configured"
- Set
NOTION_TOKEN
environment variable or configurenotion_token_cmd
- Set
-
"Database ID not configured"
- Set
database_id
in config orNOTION_DATABASE_ID
environment variable
- Set
-
"Too soon! Wait Xms before next sync"
- Debouncing prevents API abuse - wait or adjust
sync_debounce_ms
- Debouncing prevents API abuse - wait or adjust
-
Slow sync performance
- Enable debug mode to see timing breakdown
- Performance depends on Notion API response time (typically 200-600ms per request)
- Large pages may require multiple API calls due to pagination (normal behavior)
-
Temporary sync failures
- Plugin automatically retries failed requests once
- Rate limiting is handled with intelligent backoff
- Enable debug mode to see retry attempts and timing
The plugin exposes a Lua API:
local notion = require('notion.api')
-- Create a new page and open for editing
notion.create_page('My Page Title')
-- Edit a page by ID in Neovim buffer
notion.edit_page('page-id-here')
-- Browse and select pages for editing
notion.list_and_edit_pages()
-- Delete (archive) pages with selection UI
notion.delete_page()
-- Sync current buffer to Notion
notion.sync_page()
-- Open current buffer's page in browser
notion.open_current_page_in_browser()
-- Legacy browser-based functions
notion.open_page('search query')
notion.list_pages()
notion.open_page_by_url('https://notion.so/...')
- Fetch existing blocks from Notion page with intelligent pagination
- Convert buffer content to Notion block format
- Calculate precise diff between existing and new blocks
- Delete changed blocks that no longer match
- Insert new blocks at correct positions using
after
parameter - Preserve unchanged blocks for optimal performance
- Retry on failures with automatic backoff for rate limiting
heading_1
,heading_2
,heading_3
- Markdown headersparagraph
- Regular text with rich formattingbulleted_list_item
- Bulleted listsnumbered_list_item
- Numbered liststo_do
- Checkbox itemscode
- Code blocks with language detectionimage
- Images with captions (both external URLs and Notion-hosted files)
Built-in protection against API limits and failures:
- Debouncing - Minimum time between syncs (default: 1000ms)
- Per-page sync state tracking - Prevents concurrent syncs
- Graceful handling of rapid save attempts
- Automatic retry logic - Intelligent backoff for API rate limits
- Pagination handling - Efficiently fetches all blocks from large pages
- Failure recovery - Retry mechanisms for temporary API issues
APACHE 2.0