You create a custom Neovim colorscheme by defining highlight groups — colors for syntax tokens, UI elements, and diagnostics — in a Lua file and loading it with colorscheme. The manual route gives full control with the modern nvim_set_hl API; a visual tool like Nvim Colors exports a flat .lua file you can drop into your config in minutes.
What Is a Neovim Colorscheme?
A Neovim colorscheme is a Lua file (or legacy Vim script) that sets highlight groups. Each group maps a UI or syntax element — comments, strings, line numbers, error messages — to foreground color, background color, and optional styles like bold or italic.
Neovim uses Tree-sitter and legacy syntax highlighting to assign tokens to groups like @string, @keyword, and @function. Your colorscheme tells Neovim what each group should look like on screen.
Without a colorscheme, Neovim falls back to defaults that may not match your terminal or taste. A custom theme lets you optimize for readability, brand colors, or a specific mood.
How Do I Create a Colorscheme Manually in Lua?
Manual creation follows four steps: enable true colors, create a Lua file, define highlight groups, and load the theme.
Step 1 — Enable true colors
Neovim needs 24-bit color support for hex values to render correctly:
vim.opt.termguicolors = true
Add this to your init.lua or options.lua before loading any colorscheme.
Step 2 — Create a colorscheme file
Place your theme at ~/.config/nvim/lua/colorschemes/mytheme.lua:
local M = {}
function M.setup()
vim.cmd('hi clear')
if vim.fn.exists('syntax_on') then
vim.cmd('syntax reset')
end
vim.o.background = 'dark'
vim.g.colors_name = 'mytheme'
local colors = {
bg = '#1a1b26',
fg = '#c0caf5',
comment = '#565f89',
string = '#9ece6a',
keyword = '#bb9af7',
function_ = '#7aa2f7',
type = '#2ac3de',
number = '#ff9e64',
}
vim.api.nvim_set_hl(0, 'Normal', { fg = colors.fg, bg = colors.bg })
vim.api.nvim_set_hl(0, 'Comment', { fg = colors.comment, italic = true })
vim.api.nvim_set_hl(0, 'String', { fg = colors.string })
vim.api.nvim_set_hl(0, 'Number', { fg = colors.number })
vim.api.nvim_set_hl(0, 'Keyword', { fg = colors.keyword })
vim.api.nvim_set_hl(0, 'Function', { fg = colors.function_ })
vim.api.nvim_set_hl(0, 'Type', { fg = colors.type })
vim.api.nvim_set_hl(0, 'LineNr', { fg = colors.comment })
vim.api.nvim_set_hl(0, 'CursorLine', { bg = '#24283b' })
vim.api.nvim_set_hl(0, 'Visual', { bg = '#33467c' })
end
return M
Step 3 — Load the colorscheme
In your config:
require('colorschemes.mytheme').setup()
This module pattern is common for themes you publish as plugins or maintain long-term. You can also register a standard :colorscheme command with a colors/mytheme.vim shim that calls setup().
Step 4 — Extend for Tree-sitter and plugins
Real-world themes define dozens of groups. Important Tree-sitter groups include @keyword, @string, @function, @variable, and @comment. Plugin groups like TelescopeBorder, NvimTreeNormal, and DiagnosticError keep your UI consistent.
Use :Inspect on any element under the cursor to see its highlight group, then add a matching nvim_set_hl call. This iterative process is powerful but time-consuming — which is why many developers use a generator instead.
How Does Nvim Colors Speed Up Colorscheme Creation?
Nvim Colors is a free web tool at nvimcolors.com built specifically for Neovim colorscheme creation. Instead of hand-writing highlight definitions, you:
- Pick base background and foreground colors
- Tune syntax groups with live preview
- Export a complete
.luafile - Drop the file into your Neovim config
The generator produces a flat script — not a module — that sets highlights with vim.cmd('highlight ...') when Neovim loads the file. A typical export looks like this:
-- EVERGREEN
-- created on https://nvimcolors.com
vim.cmd('highlight clear')
vim.cmd('syntax reset')
-- Basic UI elements
vim.cmd('highlight Normal guibg=#304b50 guifg=#d6dbdc')
vim.cmd('highlight CursorLine guibg=#415a5e')
vim.cmd('highlight LineNr guifg=#839396')
vim.cmd('highlight Visual guibg=#62777a')
-- Syntax highlighting
vim.cmd('highlight Comment guifg=#849497 gui=italic')
vim.cmd('highlight String guifg=#fc9f9b')
vim.cmd('highlight Keyword guifg=#a2dffd gui=italic')
vim.cmd('highlight Function guifg=#2bdcab')
-- Messages
vim.cmd('highlight DiagnosticError guifg=#ff5c5c')
vim.cmd('highlight DiagnosticWarn guifg=#e2ce3c')
The export also covers popup menus, search highlights, diagnostics, and a handful of plugin groups (Telescope, Copilot). It includes a few Tree-sitter groups for HTML tags (@tag.delimiter, @tag.attribute) alongside classic syntax groups like String and Keyword, which Tree-sitter often links to automatically.
Install an Nvim Colors export
- Download your
.luafile from the generator - Save it as
~/.config/nvim/colors/mytheme.lua(use your chosen theme name) - Add these lines to the end of your
init.lua:
vim.opt.termguicolors = true
vim.cmd('colorscheme mytheme')
- Restart Neovim
You can also browse ready-made presets like nicovim, exord, and evergreen, customize them in the generator, and export your own variant.
For most developers who care about colors but not about maintaining a colorscheme codebase, this workflow is the fastest path from idea to working theme. If you later want a publishable plugin-style theme, refactor the export into a module with nvim_set_hl — the manual section above shows that pattern.
What Highlight Groups Matter Most?
Focus on these groups first — they cover most of what you read during a coding session:
| Group | Purpose |
|---|---|
| Normal | Default text and editor background |
| Comment | Comments — should be muted, not invisible |
| String | String literals |
| Number | Numeric literals |
| Keyword | Language keywords (if, return, const) |
| Function | Function names and calls |
| Type | Types, classes, interfaces |
| LineNr | Line numbers in the gutter |
| CursorLine | Highlight for the current line |
| Visual | Selected text |
| DiagnosticError | Error underlines and signs |
Once these look right, expand to diff highlights, search matches, and plugin-specific groups.
How Do I Test My Colorscheme?
Test across multiple file types and conditions:
- Open
.lua,.ts,.py, and.mdfiles to check syntax coverage - Trigger
:messagesand:checkhealthto see diagnostic colors - Toggle
:set numberand split windows to verify UI groups - View code in bright and dim lighting — contrast matters for long sessions (see our color theory guide)
For Nvim Colors exports, reload with :colorscheme mytheme after editing the file. For module-based themes, run :source ~/.config/nvim/lua/colorschemes/mytheme.lua and call setup() again, or restart Neovim.
How Do I Share or Publish My Theme?
Package your .lua file in a Git repo with a README and install instructions. Popular patterns:
- Local only — keep the file in your dotfiles repo
- Plugin manager — publish as a Neovim plugin on GitHub and install with lazy.nvim or packer
- Nvim Colors presets — start from community presets and share your exported file with teammates
If you want others to install your theme, document the install path (colors/ for flat exports, lua/colorschemes/ for modules) and the termguicolors requirement.
Frequently Asked Questions
What is the easiest way to create a Neovim colorscheme?
The fastest path is a visual generator like Nvim Colors. Pick base colors, tune syntax groups, and download a complete .lua file. Manual creation in Lua gives more control but takes longer.
Do I need to know Lua to make a Neovim colorscheme?
For a from-scratch colorscheme, yes — you define highlight groups with vim.api.nvim_set_hl in a Lua file. Nvim Colors generates a ready-to-use .lua file with vim.cmd('highlight ...') calls so you can skip writing them by hand.
Where do I put a custom Neovim colorscheme file?
For Nvim Colors exports, save the file as ~/.config/nvim/colors/mytheme.lua and load it with vim.cmd('colorscheme mytheme'). For a hand-written module, use ~/.config/nvim/lua/colorschemes/mytheme.lua and require('colorschemes.mytheme').setup().
What highlight groups should a Neovim colorscheme define?
At minimum, define Normal, Comment, String, Number, Keyword, Function, Type, and Identifier. UI groups like LineNr, CursorLine, and Visual matter for daily comfort.
Can I base my colorscheme on an existing theme?
Yes. Fork an existing colorscheme repo or use Nvim Colors presets to start from a palette you like, then customize and export your own variant.