README.md

May 4, 2026 · View on GitHub

Slimline logo
slimline.nvim

neovim: v0.10.1+ code size license

Another Neovim statusline written in Lua.

The goal is to provide a visual pleasing and efficient statusline. It started with writing my own statusline. Reason for writing was to learn more about the Neovim ecosystem and having exactly what I want, function and aesthetic wise.

In the meantime it is a quite generic and configurable alternative to other popular statuslines.

Impressions

Here are some screenshots that might be a bit outdated. See recipes for config examples.

s1 s30 s5 s17 s13 s18 s20 s19 s9 s21 s10 s16 s6 s23 split stealth

Components

ComponentDescription
modeCurrent mode. Automatically sets vim.opt.showmode = false
pathFilename and the relative path as well as modified / read-only info. The directory path will be truncated. Can be disabled and configured
gitBranch and Diff infos (requires gitsigns, mini.git (branch only), mini.diff (diff only), vim-gitgutter (diff-only) or vim-fugitive (branch only).
diagnosticsvim.diagnostic infos. This component is event driven and will not poll the information on every statusline draw.
filetype_lspFile type and attached LSPs. Attached LSPs are evaluated event driven on LSP attach / detach events. LSP names can be mapped to custom names or disabled using configs.filetype_lsp.map_lsps.
progressFile progress in %, overall number of lines as well as the cursor column (byte index and virtual if they differ)
recordingRegister being used when recording a macro
searchcountSearch result occurences
selectioncountVisually selected characters/lines or matrix

Which components to show in which section (left, right, center) can be configured. The components entries accept function calls and strings so that you can create custom comonents. See Custom components for an introduction.

Components have a flow direction which means that components on the left have their primary part on the left side and components on the right have their primary part on their right side.

Contributing

Feel free to create an issue/PR if you want to see anything else implemented.

Installation

lazy.nvim

{
    -- Calls `require('slimline').setup({})`
    "sschleemilch/slimline.nvim",
    opts = {}
},

mini.deps

MiniDeps.now(function()
    MiniDeps.add('sschleemilch/slimline.nvim')
    require('slimline').setup({})
end)

Neovim's 0.12+ built in plugin manager vim.pack

vim.pack.add({
  "https://github.com/sschleemilch/slimline.nvim",
})
require("slimline").setup({})

Optional dependencies:

You'll also need to have a patched nerd font for icons and separators.

Tip

You can decide whether you would like to have a global statusline or one for each split by setting vim.opt.laststatus accordingly in your settings.

If you decide to not use a global one then you can configure via components_inactive what will be rendered in the inactive one. By default, all components will be shown. Inactive components will use the secondary highlighting for primary parts.

split shows an example using default options.

Configuration

Default Options
{
  bold = false, -- makes primary parts bold

  -- Global style. Can be overwritten using `configs.<component>.style`
  style = 'bg', -- or "fg"

  -- Component placement
  components = {
    left = {
      'mode',
      'path',
      'git',
    },
    center = {},
    right = {
      'diagnostics',
      'filetype_lsp',
      'progress',
    },
  },

  -- Inactive components
  -- Uses all `components` by default.
  -- E.g. for only showing `path`:
  -- components_inactive = {
  --   left = { 'path' },
  --   right = {},
  -- },
  components_inactive = {},

  -- Component configuration
  -- `<component>.style` can be used to overwrite the global 'style'
  -- `<component>.sep` can be used to overwrite the global 'sep.left' and `sep.right`
  -- `<component>.hl = { primary = ..., secondary = ...}` can be used to overwrite global ones
  -- `<component>.follow` can point to another component name to follow its style (e.g. 'progress' following 'mode' by default). Follow can be disabled by setting it to `false`
  -- `<component>.trunc_width` can be used to hide a component completely once the window width drops below that value
  configs = {
    mode = {
      verbose = false, -- Selects the `verbose` format
      hl = {
        normal = 'Type',
        visual = 'Keyword',
        insert = 'Function',
        replace = 'Statement',
        command = 'String',
        other = 'Function',
      },
      format = {
        ['n'] = { verbose = 'NORMAL', short = 'N' },
        ['v'] = { verbose = 'VISUAL', short = 'V' },
        ['V'] = { verbose = 'V-LINE', short = 'V-L' },
        ['\22'] = { verbose = 'V-BLOCK', short = 'V-B' },
        ['s'] = { verbose = 'SELECT', short = 'S' },
        ['S'] = { verbose = 'S-LINE', short = 'S-L' },
        ['\19'] = { verbose = 'S-BLOCK', short = 'S-B' },
        ['i'] = { verbose = 'INSERT', short = 'I' },
        ['R'] = { verbose = 'REPLACE', short = 'R' },
        ['c'] = { verbose = 'COMMAND', short = 'C' },
        ['r'] = { verbose = 'PROMPT', short = 'P' },
        ['!'] = { verbose = 'SHELL', short = 'S' },
        ['t'] = { verbose = 'TERMINAL', short = 'T' },
        ['U'] = { verbose = 'UNKNOWN', short = 'U' },
      },
    },
    path = {
      trunc_width = 60,
      directory = true, -- Whether to show the directory
      -- truncates the directory path. Can be disabled by setting `truncate = false`
      truncate = {
        chars = 1, -- number of characters for each path component
        full_dirs = 2, -- how many path components to keep unshortened
      },
      icons = {
        folder = ' ',
        modified = '',
        read_only = '',
      },
    },
    git = {
      trunc_width = 120,
      icons = {
        branch = '',
        added = '+',
        modified = '~',
        removed = '-',
      },
    },
    diagnostics = {
      trunc_width = 75,
      workspace = false, -- Whether diagnostics should show workspace diagnostics instead of current buffer
      icons = {
        ERROR = ' ',
        WARN = ' ',
        HINT = ' ',
        INFO = ' ',
      },
      severity = {
        -- vim.diagnostic.SeverityFilter options
        min = vim.diagnostic.severity.HINT,
      },
      hl = {
        error = 'DiagnosticError',
        warn = 'DiagnosticWarn',
        hint = 'DiagnosticHint',
        info = 'DiagnosticInfo',
      },
    },
    filetype_lsp = {
      trunc_width = 95,
      -- Map lsp client names to custom names or ignore them by setting to `false`
      -- E.g. { ['tsserver'] = 'TS', ['pyright'] = 'Python', ['GitHub Copilot'] = false }
      map_lsps = {},
      lsp_sep = ',', -- separator between attached LSPs
    },
    selectioncount = {
      hl = {
        primary = 'Special',
      },
      icon = '󰈈 ',
    },
    searchcount = {
      hl = {
        primary = 'Special',
      },
      icon = ' ',
      -- Options to be passed to vim.fn.searchcount, see :h searchcount
      options = {
        recompute = true,
      },
    },
    progress = {
      follow = 'mode',
      column = false, -- Enables a secondary section with the cursor column
      icon = ' ',
    },
    recording = {
      icon = ' ',
      hl = {
        primary = 'Special',
      },
    },
  },

  -- Spacing configuration
  spaces = {
    components = ' ', -- string between components
    left = ' ', -- string at the start of the line
    right = ' ', -- string at the end of the line
    padding = ' ', -- component inner padding. Only relevant for style 'bg'
  },

  -- Seperator configuartion
  sep = {
    hide = {
      first = false, -- hides the first separator of the line
      last = false, -- hides the last separator of the line
    },
    left = '', -- left separator of components
    right = '', -- right separator of components
  },

  -- Global highlights
  hl = {
    base = 'Normal', -- highlight of the background
    base_inactive = 'Normal', -- highlight of the background when inactive
    primary = 'Normal', -- highlight of primary parts (e.g. filename)
    secondary = 'Comment', -- highlight of secondary parts (e.g. filepath)
  },

  -- Hide statusline on filetypes
  disabled_filetypes = {},
}

Highlights

Slimline creates highlight groups with the base highlights chosen in the hl section of the config. The default ones should be a safe choice to work well with most colorschemes but of course you can adapt them to your liking. Depending on the chosen style (fg or bg) the color will be used as a foreground or as a background color.

Note

When using a transparent colorscheme and using style=bg it means that the actual background will be used as a foreground color for text. Since a transparent theme has no background color, Slimline will fall back to #000000 for dark themes and to #ffffff for white themes

Instead of specifying the base highlight group in the config you can also tweak target highlight groups directly. Make sure to do that before slimline's setup().

Internal highlight groups
Highlight GroupDescription
SlimlineBackground of the line (active)
SlimlineInactiveBackground of the line (inactive)
SlimlineModeSecondarySecondary mode content (mode has no secondary content but progress has, which is following mode)
SlimlineModeNormalNormal mode
SlimlineModeNormalSepNormal mode separator
SlimlineModeNormalSep2SecNormal mode separator to secondary
SlimlineModeVisualVisual mode
SlimlineModeVisualSepVisual mode separator
SlimlineModeVisualSep2SecVisual mode separator to secondary
SlimlineModeInsertInsert mode
SlimlineModeInsertSepInsert mode separator
SlimlineModeInsertSep2SecInsert mode separator to secondary
SlimlineModeReplaceReplace mode
SlimlineModeReplaceSepReplace mode separator
SlimlineModeReplaceSep2SecReplace mode separator to secondary
SlimlineModeCommandCommand mode
SlimlineModeCommandSepCommand mode separator
SlimlineModeCommandSep2SecCommand mode separator to secondary
SlimlineModeOtherOther mode
SlimlineModeOtherSepOther mode separator
SlimlineModeOtherSep2SecOther mode separator to secondary
SlimlinePathPrimaryPath primary
SlimlinePathPrimarySepPath primary separator
SlimlinePathPrimarySep2SecPath primary separator to secondary
SlimlinePathSecondaryPath secondary
SlimlinePathSecondarySepPath secondary separator
SlimlineGitPrimaryGit primary
SlimlineGitPrimarySepGit primary separator
SlimlineGitPrimarySep2SecGit primary separator to secondary
SlimlineGitSecondaryGit secondary
SlimlineGitSecondarySepGit secondary separator
SlimlineFiletype_lspPrimaryFiletype lsp primary
SlimlineFiletype_lspPrimarySepFiletype lsp primary separator
SlimlineFiletype_lspPrimarySep2SecFiletype lsp primary separator to secondary
SlimlineFiletype_lspSecondaryFiletype lsp secondary
SlimlineFiletype_lspSecondarySepFiletype lsp secondary separator
SlimlineProgressPrimaryProgress primary
SlimlineProgressPrimarySepProgress primary separator
SlimlineProgressPrimarySep2SecProgress primary separator to secondary
SlimlineProgressSecondaryProgress secondary
SlimlineProgressSecondarySepProgress secondary separator
SlimlineRecordingPrimaryRecording primary
SlimlineRecordingPrimarySepRecording primary separator
SlimlineRecordingPrimarySep2SecRecording primary separator to secondary
SlimlineRecordingSecondaryRecording secondary
SlimlineRecordingSecondarySepRecording secondary separator
SlimlineSearchcountPrimarySearchcount primary
SlimlineSearchcountPrimarySepSearchcount primary separator
SlimlineSearchcountPrimarySep2SecSearchcount primary separator to secondary
SlimlineSearchcountSecondarySearchcount secondary
SlimlineSearchcountSecondarySepSearchcount secondary separator
SlimlineSelectioncountPrimarySelectioncount primary
SlimlineSelectioncountPrimarySepSelectioncount primary separator
SlimlineSelectioncountPrimarySep2SecSelectioncount primary separator to secondary
SlimlineSelectioncountSecondarySelectioncount secondary
SlimlineSelectioncountSecondarySepSelectioncount secondary separator
SlimlineDiagnosticsHintDiagnostic hints
SlimlineDiagnosticsHintSepDiagnostic hints sep
SlimlineDiagnosticsInfoDiagnostic infos
SlimlineDiagnosticsInfoSepDiagnostic infos sep
SlimlineDiagnosticsWarnDiagnostic warnings
SlimlineDiagnosticsWarnSepDiagnostic warnings sep
SlimlineDiagnosticsErrorDiagnostic errors
SlimlineDiagnosticsErrorSepDiagnostic errors sep

Recipes

Pure

s20 s21

{
  style = 'fg',
  bold = true,
  configs = {
    path = {
      hl = {
        primary = 'Label',
      },
    },
    git = {
      hl = {
        primary = 'Function',
      },
    },
    filetype_lsp = {
      hl = {
        primary = 'String',
      },
    },
  },
}

Rainbow

s23

{
  configs = {
    path = {
      hl = {
        primary = 'Define',
      },
    },
    git = {
      hl = {
        primary = 'Function',
      },
    },
    filetype_lsp = {
      hl = {
        primary = 'String',
      },
    },
  },
}

Minimal

s9 s10

{
    style = "fg",
    spaces = {
        components = '',
        left = '',
        right = ''
    }
}

Slashed

s13

{
    spaces = {
        components = "",
        left = "",
        right = "",
    },
    sep = {
        hide = {
            first = true,
            last = true,
        },
        left = "",
        right = "",
    },
}

Bubble chain

s17

{
    spaces = {
        components = "─",
        left = "─",
        right = "─",
    },
},

And adding fill chars stl Neovim option:

vim.opt.fillchars = {
	stl = "─",
}

3 letter non-shifting mode

{
    configs = {
        mode = {
            format = {
                ['n'] = { short = 'NOR' },
                ['v'] = { short = 'VIS' },
                ['V'] = { short = 'V-L' },
                ['\22'] = { short = 'V-B' },
                ['s'] = { short = 'SEL' },
                ['S'] = { short = 'S-L' },
                ['\19'] = { short = 'S-B' },
                ['i'] = { short = 'INS' },
                ['R'] = { short = 'REP' },
                ['c'] = { short = 'CMD' },
                ['r'] = { short = 'PRO' },
                ['!'] = { short = 'SHE' },
                ['t'] = { short = 'TER' },
                ['U'] = { short = 'UNK' },
            },
        },
    }
}

Stealth

stealth

Uses the default StatusLine and StatusLineNC base highlights that mimics the default statusline. Also sets the primary and secondary to those highlight groups which makes it very subtle. Removes unneeded spaces for most efficient space.

{
    style = 'fg',
    hl = {
        primary = 'StatusLine',
        secondary = 'StatusLineNC',
        base = 'StatusLine',
        base_inactive = 'StatusLineNC',
    },
    configs = {
        progress = {
            follow = false,
        },
    }
    spaces = {
        components = '',
        left = '',
        right = ''
    }
}

Custom components

The components part of the config accepts function calls. This opens the door extending Slimline with your own content.

Warning

This section uses internal APIs. Since I am not committing to stable internal APIs yet, it can change! Be carfeul when using it. The section will be updated accordingly when interfaces change though.

Let's create a center component using a function like this directly in the config:

opts = {
    components = {
        center = {
            function ()
                return "Hello World"
            end
        },
    }
}

It will render to something like this (depending on your colorscheme):

c1

If you want to use internal render functionality of a component (here of the path component) you can do it like that:

function(active)
    return Slimline.highlights.hl_component(
        { primary = 'Hello', secondary = 'World' },
        Slimline.highlights.hls.components['path'],
        Slimline.get_sep('path'),
        'right', -- flow direction (on which side the secondary part will be rendered)
        active, -- whether the component is active or not
        'fg' -- style to use
    )
end,

Warning

The component to use the highlights and seperator from needs to be configured in your components since slimline only creates highlights for used ones.

It will now render to that (depending on the config)

c4

Of course you can use Slimline* highlight groups on your own to create your own styled component

The following lua table is required to be passed if you want to use hl_component():

hl = {
    primary = {
        text = '',
        sep = '',
        sep2sec = '',
    },
    secondary = {
        text = '',
        sep = '',
    }
}

Similar plugins