Open-source project
willothy/nvim-cokeline avatar
willothy/nvim-cokeline

nvim-cokeline gives you a bufferline you build yourself out of components

:nose: A Neovim bufferline for people with addictive personalities

634 stars43 forksLuaMIT

At a glance

What is it?
nvim-cokeline is a Neovim bufferline whose design goal is stated against opinionated plugins: it wants to be a general framework for building your ideal bufferline rather than a finished look you can only tweak. The bufferline is assembled from a list of components, each of which supplies text, colours and behaviour, and the sample configurations show how different the result can be.
Who is it for?
Adopt nvim-cokeline if you want a bufferline assembled from parts rather than one with a fixed look, since every component's text, colour and click behaviour is a Lua function you supply. Skip it if you want a polished bufferline out of the box with no configuration, because the plugin is explicitly the opposite of that, and if you need a maintained plugin, because the last tagged release is v0.4.0 from 2023.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 126 days ago.
What is it written in?
Mainly Lua, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The stated goal is against opinionated plugins

nvim-cokeline describes itself as a Neovim bufferline for people with addictive personalities, and the tagline in the header is the joke that runs through the project.

The first paragraph after the tagline is the actual design statement, and it is a rejection of the usual approach. The goal of this plugin is not to be an opinionated bufferline with more or less limited customization options. Rather, it tries to provide a general framework allowing you to build your ideal bufferline, whatever that might look like.

That reframing explains everything else about the plugin. Most bufferline plugins give you a set of options, and the options produce a look the author chose. This one gives you a set of primitives and expects you to compose them.

The features section leads with endlessly customizable, and the claim is that nvim-cokeline aims to be the most customizable bufferline plugin around. The instruction is that if you have an idea in mind of what your bufferline should look like, you should be able to make it look that way, and if you cannot, open an issue and they will try to make it happen.

The README then shows three very different configurations for the same plugin, each with its own screenshot, and points at the wiki for more examples. That is the proof of the claim, and it is also the best way to understand the model before writing anything yourself.

The project is Lua, MIT licensed, with the last push on 2026-06-19 and releases v0.4.0 from 2023-07-02, v0.3.0 from 2022-03-06 and v0.2.0 from 2022-01-01.

A bufferline is a list of components, and each is a small Lua table

The configuration model is the thing to understand, and it is the same across every example.

A component is an object with a `text` key that produces the string to show, and optional keys for the foreground colour, the background, the italic and bold attributes, the underline attribute and an `on_click` handler. The keys can be static values or functions that receive the buffer and return the value, which is what makes the display dynamic.

The first example is the most complete. It pulls a helper from the plugin with `require('cokeline.hlgroups').get_hl_attr`, which reads an attribute out of a highlight group, and uses that to derive the foreground and background from whether the buffer is focused:

lua
  components = {
    {
      text = function(buffer) return ' ' .. buffer.devicon.icon end,
      fg = function(buffer) return buffer.devicon.color end,
    },
    {
      text = function(buffer) return buffer.unique_prefix end,
      fg = get_hex('Comment', 'fg'),
      italic = true
    },
    {
      text = function(buffer) return buffer.filename .. ' ' end,
      underline = function(buffer)
        return buffer.is_hovered and not buffer.is_focused
      end
    },
    {
      on_click = function(_, _, _, _, buffer)
        buffer:delete()
      end
    },
  },

Four components in that snippet: a devicon with its own per-file colour, a unique prefix in the comment colour and italic, the filename with an underline applied when the buffer is hovered but not focused, and a delete button whose click handler calls `buffer:delete()`.

The second example adds a modified indicator and an index, and the third drops the background entirely with `bg = 'NONE'` and uses a different separator. Same primitives, different bufferline.

The `buffer` object handed to those functions is the useful part. It exposes `is_focused`, `is_hovered`, `is_modified`, `filename`, `index`, `unique_prefix` and `devicon`, which is a table with an `icon` and a `color`. So the plugin hands you the state and you decide what to draw.

Rendering keeps the focused buffer in the middle

The dynamic rendering feature is the part that makes the plugin different from a static list, and it addresses a problem every bufferline has.

Even when you have a lot of buffers open, nvim-cokeline is rendered to always keep the focused buffer visible and in the middle of the bufferline. Also, if a buffer does not fit entirely, it still tries to include as much of it as possible before cutting off the rest.

That is a scrolling model rather than a wrapping one. Most bufferlines that overflow will either wrap to a second line or scroll the list so the focused buffer is at one edge. Centring it means the buffers on either side are always in a predictable place relative to the one you are editing, so muscle memory works.

And the second clause is the careful part. A buffer that does not fit is not simply dropped from the edge; as much of it as possible is shown first, and the rest is cut. So you can still see which buffer it is and roughly how long its name is, rather than seeing a hard edge.

There is a gif in the documentation showing the rendering in motion, which is the only way to really understand how it feels, since the point is that the line scrolls as you move between buffers rather than jumping.

The implementation is not described in the documentation, and that is reasonable, since it is a rendering behaviour rather than a configuration surface. What you configure is what each component shows, and the plugin decides where to put the line.

LSP diagnostics change how a component looks

The LSP support feature is a small idea with a clear purpose.

If a buffer has an LSP client attached to it, you can configure the style of a component to change based on how many errors, warnings, infos and hints are reported by the LSP.

So the colour of a component becomes a function of the diagnostic state of the buffer it belongs to. That is the sort of thing that is trivial to add once the component model accepts functions for its style keys, and it is the clearest demonstration of why the function-based design pays off.

In a plugin with only static options, adding this feature means a new option like `lsp_error_color`, and the same option has to be repeated for every style key. Here it is one function that returns different values, and the same function can drive the foreground, the background, the boldness or anything else you have already defined.

The documentation shows a gif of the styling changing as diagnostics come and go, and the feature list places it between the dynamic rendering and the buffer pick, which is a sensible order, since all three are about how the line reacts to state rather than about what it contains.

The LSP integration is also a good example of the plugin working with the rest of a Neovim configuration rather than around it. The buffer object already knows whether a client is attached, and the plugin exposes the diagnostic counts, so a component can make a decision that a user of another plugin, like a statusline, would make in the same way.

Buffer picking assigns letters and lets you focus or close by typing

The buffer pick feature is the one that changes how you use the line, and it works by giving each buffer a letter you can type.

You can focus and close any buffer by typing its pick_letter. Letters are assigned by filename by default, so foo.txt gets the letter f, and by keyboard reachability if the letter is already assigned to another buffer.

So the scheme is not positional. The first buffer gets the letter matching its own name, and when two buffers want the same letter the second one is given a different one chosen by keyboard reachability, meaning the plugin picks a letter that is close on the keyboard to the one you would expect.

The point of that is that the letters become a spatial memory. If a buffer is called foo.txt you press f. If something else already holds f, the next buffer gets a letter near f on the keyboard layout, so you are not hunting for an arbitrary key.

The example configuration shows the pieces you need to wire this up, pulling the picking state from the plugin's mappings module:

lua
local is_picking_focus = require('cokeline.mappings').is_picking_focus
local is_picking_close = require('cokeline.mappings').is_picking_close

and then using those in a component's text function so that the line can show a different appearance while a pick is in progress. The example uses `vim.g.terminal_color_1` and `vim.g.terminal_color_3` for a red and a yellow, which is a neat trick for getting colours that follow your terminal palette rather than a hard-coded one.

The combination of pick, close and delete is the workflow. The delete button calls `buffer:delete()`, and the pick mode can focus or close, so the same bufferline provides three ways to act on a buffer without leaving the keyboard.

The repository is a small Lua plugin with modern tooling

The top-level entries describe a compact plugin with a fairly modern toolchain, which is worth noting because the release tags are old.

There is a lua/ directory for the plugin code, a doc/ directory for the generated documentation, and the usual project files: CHANGELOG.md, LICENSE, README.md, a .gitignore and a .github/ directory for the workflows.

Then the linting and formatting configuration, which is the most telling set of entries. There is a .luacheckrc for luacheck, a selene.toml for selene, a stylua.toml for stylua, and a .luarc.json for the Lua language server. So the project uses two linters, a formatter and a language server configuration.

There is also neovim.yml, which is the file used to run the plugin's own test suite in a Neovim instance, a common pattern for plugins that need to exercise the editor itself.

The README is generated with panvimdoc, which you can see from the include comments at the top and around the sections that should not appear in the documentation site. The markdown comments mark which parts of the README belong in the docs and which are only for GitHub, so the two versions differ without being maintained twice.

The last push was on 2026-06-19, which is a good deal more recent than the v0.4.0 tag from July 2023. So the plugin is being worked on, but the tagged releases are sparse and the plugin is distributed through a plugin manager rather than a release feed, which is the usual arrangement for Neovim plugins.

The topics include hacktoberfest, so the project takes October contributions, and the whole thing is a good example of a plugin whose value lies in its extension points rather than in its defaults.

Editorial conclusion

Adopt nvim-cokeline if you want a bufferline assembled from parts rather than one with a fixed look, since every component's text, colour and click behaviour is a Lua function you supply. Skip it if you want a polished bufferline out of the box with no configuration, because the plugin is explicitly the opposite of that, and if you need a maintained plugin, because the last tagged release is v0.4.0 from 2023. Verify first that you are comfortable writing Lua for the components, that the plugin's requirements are met on your Neovim version, and which of the several example configurations you are starting from, because they differ in more than appearance.

Frequently asked questions

What is nvim-cokeline and how is it configured?

It is a Neovim bufferline built as a general framework rather than an opinionated look. You call `require('cokeline').setup({...})` with a `components` list, and each component is a table with a `text` key plus optional `fg`, `bg`, `italic`, `bold`, `underline` and `on_click` keys, where the style keys can be functions receiving the buffer and returning the value.

How does the nvim-cokeline buffer pick work?

You focus or close any buffer by typing its pick_letter. Letters are assigned by filename by default, so foo.txt gets the letter f, and by keyboard reachability if that letter is already assigned to another buffer. You can read the picking state with `require('cokeline.mappings').is_picking_focus` and `is_picking_close` to style the line while a pick is active.

What special rendering and integration does nvim-cokeline provide?

Rendering is dynamic: with many buffers open, the focused buffer is kept visible and in the middle of the bufferline, and a buffer that does not fit entirely still has as much of it shown as possible before the rest is cut off. For LSP support, if a buffer has an LSP client attached you can configure a component's style to change based on how many errors, warnings, infos and hints are reported.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. willothy/nvim-cokeline on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/willothy-nvim-cokeline.svg)](https://hysenlabs.com/projects/willothy-nvim-cokeline)

Was this useful?