Neovim has become my editor of choice for its flexibility, performance, and hackability. Over time, I’ve refined my setup into a modular and well-organized configuration that’s easy to scale and maintain. In this article, I’ll walk through how I structure my config, explaining the purpose of each directory and how it all fits together.
Directory Structure
Here’s what my Neovim configuration looks like on disk:
├── docs
├── ftplugin
├── images
├── lsp
├── lua
│ ├── configs
│ ├── constants
│ ├── icons
│ ├── keys
│ ├── plugins
│ │ ├── debug
│ │ ├── editor
│ │ ├── lang
│ │ ├── test
│ │ └── ui
│ ├── srandle
│ └── utils
└── snippets
├── base
├── js
└── rust
Let’s break it down:
docs/
: Internal Documentation
I keep Markdown files here to explain parts of my config—setup notes, plugin usage, and rationale behind key decisions. This helps keep the project self-documenting and easier to revisit.
ftplugin/
: Filetype-Specific Settings
This is where I define behaviors that only apply to specific filetypes—like language-specific keybindings or indentation rules for Python, Rust, etc.
images/
: ASCII Art
I use this directory to store ASCII art for Neovim’s startup screen or any themed UI elements.
The lua/
Directory
This is where the bulk of my configuration lives.
lua/configs/
: Advanced Plugin Configurations
Some plugins require more detailed setups than what I can express inline with Lazy’s plugin spec. I offload these to dedicated files here.
lua/constants/
: Global Values and Language Tools
This folder holds config-wide constants, most notably:
languagepacks.lua
: Maps languages to their relevant tools across Mason, LSP, linters, and formatters.pulled.lua
: Extracts values fromlanguagepacks.lua
into reusable variables.
This makes setting up language support across the ecosystem much easier.
lua/icons/
: Icons for UI and Diagnostics
I centralize icon definitions used throughout my UI—status lines, diagnostic signs, file explorers—into this folder.
lua/keys/
: All Keybindings
Instead of mixing mappings across multiple files, I manage all of them here for consistency. It makes it easier to maintain and evolve the UX of my setup.
lua/plugins/
: Plugin Loader
Divided into categories for clarity:
debug/
: Debug Adapter Protocol (DAP) plugins.editor/
: Core editing enhancements (e.g.,treesitter
,nvim-cmp
, and my preferred fuzzy finder:fzf-lua
).lang/
: Language-specific plugins. These are dynamically loaded based onlanguagepacks.lua
.test/
: Plugins for running and managing tests.ui/
: Visual polish—status lines, file explorers, notifications, etc.
lua/srandle/
: Personal Defaults and Plugin Bootstrap
This contains my opinionated settings—like vim.o.scrolloff = 999
—along with the main Lazy.nvim entry point that sets up the plugin manager.
lua/utils/
: Helper Functions
Reusable Lua helpers for working with mappings, options, themes, and plugin glue logic live here.
lsp/
: Language Server Configurations
Neovim now ships with built-in LSP support, and I take full advantage of it. I load default LSP setups using nvim-lspconfig
, and extend or override behavior inside this dedicated lsp/
folder.
Each file here typically contains enhanced configuration per language—like custom on_attach
functions, capabilities, and language-specific tooling integration.
This folder replaces the older lua/servers/
convention I previously used, simplifying LSP maintenance and leveraging Neovim’s native capabilities.
snippets/
: VS Code-Style Snippets for nvim-cmp
I use LuaSnip with nvim-cmp
for autocompletion. My snippets are organized by language in folders like:
base/
: Generic templates available everywhere.js/
: JavaScript/TypeScript-specific snippets.rust/
: Rust development helpers.
Additional Config Files
Outside the main folders, I include some essential project-level configuration files:
.stylelua.toml
: Lua code formatting rules..luacheckrc
: Static analysis for Lua..editorconfig
: Editor consistency across environments.README.md
: Overview of the configuration..gitignore
: Files to exclude from version control.LICENSE.md
: Licensing information.
Conclusion
My Neovim configuration is designed to be clean, modular, and future-proof. Whether I’m tweaking a theme, installing a new plugin, or adjusting an LSP server, everything has a place. Using tools like fzf-lua
, dynamic language packs, and clearly separated folders helps me stay productive and keeps the config easy to maintain.
To give you a glimpse into how this all comes together in practice, here’s a quick visualization of my config size breakdown:
As you can see, the structure remains lean and purposeful. Most of the weight lives in the plugin configuration and utility layers—where it should. This modular design makes the system easier to debug, extend, and reason about over time.
Whether you’re building your first Neovim config or refining an existing one, I hope this walkthrough inspires you to organize your environment in a way that supports how you think and work. Got thoughts or questions? Feel free to reach out—I’d love to hear how others are approaching their setups.