Configure Neovim – Distraction-Free Coding with zen-mode.nvim
Published on Mar 20, 2026

There are days when the editor itself gets in the way. The statusline, the gutter with its line numbers and git signs, the file tree on the left, the diagnostic virtual text floating at the end of every line — all of it is useful most of the time, but sometimes you just want to look at the code. No decorations, no interruptions, just the text in front of you.
That is what zen-mode.nvim is for. It is a plugin by folke — the same person behind which-key.nvim, trouble.nvim, and lazy.nvim — that hides all the surrounding UI when you ask it to, opens your current buffer in a centred floating window, and restores everything exactly as it was when you close it. You press one key to enter, you press the same key to leave. Nothing is permanently changed.
We will also be setting up twilight.nvim, which pairs naturally with zen mode. While zen mode handles the surrounding UI, twilight dims the parts of the buffer you are not actively working on, leaving only the current function or block at full brightness. Together the two plugins create a focused coding environment that is genuinely different from using the editor normally.
Prerequisites
There are no hard dependencies from previous posts for this one. If you have been following the series, your Lazy.nvim setup and plugin folder structure are already in place. If you are dropping in on this post specifically, all you need is Lazy.nvim and nvim-treesitter installed. Twilight uses Treesitter to understand the structure of your code, so it knows which block the cursor is currently inside.
How the Two Plugins Work Together
It is worth being clear about what each plugin does before we write any configuration.
zen-mode.nvim opens a new floating window that occupies a fixed width in the centre of your screen. The area outside the window is dimmed by a configurable backdrop. The surrounding UI — the statusline, the signcolumn, the command line ruler — is hidden for the duration. When you close zen mode, the floating window is dismissed and every option that was changed is restored to its previous value. The rest of your open buffers and split layout are untouched and waiting for you when you return.
twilight.nvim works entirely within the buffer, regardless of whether zen mode is active or not. It uses Treesitter to identify the top-level block that the cursor currently sits inside — a function definition, a class, a table — and keeps that block at full brightness while dimming everything above and below it. As you move the cursor to a different block, the dimming follows. When zen mode is active and twilight is installed, zen mode enables twilight automatically so the two work as one.
Installing the Plugins
Inside lua/plugins/, create a new file called zen.lua.
-- lua/plugins/zen.lua
return {
-- Distraction-free writing environment
{
"folke/zen-mode.nvim",
cmd = "ZenMode",
keys = {
{
"<leader>z",
"<cmd>ZenMode<cr>",
desc = "Toggle zen mode",
},
},
opts = {},
},
-- Dims inactive portions of code using Treesitter
{
"folke/twilight.nvim",
cmd = { "Twilight", "TwilightEnable", "TwilightDisable" },
opts = {},
},
}
Save the file and run :Lazy sync. Both plugins will be installed. Press <leader>z to test immediately. Your buffer should move into a centred floating window with the rest of the screen dimmed around it.
Configuring zen-mode.nvim
The default configuration is fine for a first look, but there is quite a lot we can tune to make it feel exactly right. Let us go through the options that matter.
opts = {
window = {
-- shade the area outside the zen window
-- 0 is fully transparent, 1 is the same as the Normal background
backdrop = 0.90,
-- width of the zen window in columns
-- numbers above 1 are absolute column counts
-- numbers below 1 are a fraction of the editor width
width = 100,
-- height stays at 1 (full height) by default
height = 1,
-- window-local options to apply inside the zen window
options = {
signcolumn = "no", -- hide the git and diagnostic signs
number = false, -- hide line numbers
relativenumber = false, -- hide relative line numbers
cursorline = false, -- hide the cursor line highlight
foldcolumn = "0", -- hide the fold column
list = false, -- hide whitespace characters
},
},
plugins = {
-- enable twilight dimming automatically when zen mode opens
twilight = { enabled = true },
-- hide git signs while in zen mode
gitsigns = { enabled = false },
-- hide diagnostics while in zen mode
diagnostics = { enabled = false },
-- hide global options like the ruler and showcmd
options = {
enabled = true,
ruler = false,
showcmd = false,
laststatus = 0, -- hide the statusline
},
},
},
The window.options table is particularly useful. Any Neovim window-local option (vim.wo.*) can go in here. The options are applied when zen mode opens and reversed when it closes. We are hiding the signcolumn, line numbers, relative numbers, the cursor line, the fold column, and the whitespace list characters — all things that are helpful during development but distracting when you want to read or write without interruption.
The plugins table tells zen mode which other plugins to adjust when it activates. Disabling gitsigns clears the added/changed/removed markers from the gutter. Disabling diagnostics hides the LSP underlines and virtual text. Setting laststatus = 0 makes the statusline disappear entirely. All of these are restored the moment you leave zen mode.
Configuring twilight.nvim
Twilight works well with its defaults, but a couple of options are worth adjusting.
opts = {
dimming = {
-- how dim the inactive code should be (0.0 is invisible, 1.0 is normal)
alpha = 0.25,
-- whether to use the background colour for dimming in addition to the foreground
color = { "Normal", "#ffffff" },
term_bg = "#000000",
-- set this to true to dim invisible characters too
inactive = false,
},
context = 10, -- how many lines of context to show beyond the active block
treesitter = true, -- use Treesitter for smarter block detection
expand = {
-- node types to always show in full even if they are not the active block
"function",
"method",
"table",
"if_statement",
},
},
The alpha value controls how visible the dimmed code is. The default of 0.25 is quite subtle — if you want a stronger effect, try 0.15. The context setting adds a buffer of lines above and below the active block so the transition does not feel too sharp.
The expand list tells Treesitter which node types should always be shown in full when they are the active block. By default this includes functions, methods, tables, and if statements, which covers most of what you will be working in.
You can use twilight independently of zen mode too. :Twilight toggles the dimming without entering zen mode, :TwilightEnable turns it on permanently, and :TwilightDisable turns it off.
Callbacks for Deeper Customisation
zen-mode.nvim has on_open and on_close callbacks that fire when zen mode activates and deactivates. These are useful if you want to adjust things that are not covered by the built-in plugins table — for example, disabling the completion menu while in zen mode or changing the colorscheme temporarily.
opts = {
-- ... window and plugins config from above ...
on_open = function(win)
-- disable blink.cmp while in zen mode
vim.b.completion = false
end,
on_close = function()
-- re-enable blink.cmp when zen mode closes
vim.b.completion = true
end,
},
The win parameter passed to on_open is the window ID of the zen floating window. You can use it with vim.api.nvim_win_set_option if you need to apply options that are not in the standard window.options table.
Putting It All Together
Here is the complete lua/plugins/zen.lua:
-- lua/plugins/zen.lua
return {
-- Distraction-free writing environment
{
"folke/zen-mode.nvim",
cmd = "ZenMode",
keys = {
{
"<leader>z",
"<cmd>ZenMode<cr>",
desc = "Toggle zen mode",
},
},
opts = {
window = {
backdrop = 0.90,
width = 100,
height = 1,
options = {
signcolumn = "no",
number = false,
relativenumber = false,
cursorline = false,
foldcolumn = "0",
list = false,
},
},
plugins = {
twilight = { enabled = true },
gitsigns = { enabled = false },
diagnostics = { enabled = false },
options = {
enabled = true,
ruler = false,
showcmd = false,
laststatus = 0,
},
},
on_open = function(_win)
vim.b.completion = false
end,
on_close = function()
vim.b.completion = true
end,
},
},
-- Dims inactive code blocks using Treesitter
{
"folke/twilight.nvim",
cmd = { "Twilight", "TwilightEnable", "TwilightDisable" },
opts = {
dimming = {
alpha = 0.25,
color = { "Normal", "#ffffff" },
inactive = false,
},
context = 10,
treesitter = true,
expand = {
"function",
"method",
"table",
"if_statement",
},
},
},
}
Verifying the Setup
Restart Neovim and open any file. Press <leader>z. The buffer should slide into a centred floating window, line numbers and git signs should disappear, the statusline should hide, and the code outside the current function block should dim. Press <leader>z again to exit. Everything should be back exactly as you left it.
If twilight is not dimming, check that nvim-treesitter is installed and that a parser is installed for the current filetype. You can install Treesitter parsers with :TSInstall <language> — for example :TSInstall lua for Lua files.
Wrapping Up
zen-mode.nvim and twilight.nvim are small additions to the config but they change the feel of the editor in a meaningful way. Not every session calls for them, but when you want to read through a complex function carefully or write something without distractions, having them available with a single keypress makes a real difference.
This wraps up the core of our Neovim setup. We now have a plugin manager, a statusline, a file explorer, git integration, an LSP, autocompletion, formatting, and a focused writing mode. In future posts we will look at some of the extra plugins that round the experience out further — things like Treesitter, a fuzzy finder, and a terminal integration.
To find the rest of my posts on Neovim, click here.




