Neovim Explained: How to Build a Plugin-Free Nvim Config That Unlocks Builtin Features

What is Neovim?

Neovim (aka Nvim) is a radical refactor of Vim. It aims to take Vim, improve the code, and add more features. They are mostly compatible but have slightly different defaults. The settings in this Neovim tutorial should also work with the latest Vim.

Neovim is a text editor, but it can be configured with plugins to be closer to an IDE. Plugins are a big subject we are not going to cover in this tutorial, we are aiming to get a minimal working setup with zero bloat.

There is a lot of out of date and bad information about Neovim and Vim on the web. Everything in this tutorial is current for Neovim version v0.4.4 and carefully researched. I am using the latest stable .appimage on Linux Centos 8 and GNOME Terminal. It should also work on Windows Subsystem for Linux.

Nvim config without using plugins

If you want a great text editor out of the box then you will find yourself banging your head against the wall with Neovim. Many of the factory default settings are weird, and many features are not enabled. In this article, I will show you how to make Neovim awesome without resorting to using any plugins.

All the settings I am going to suggest are built right into Neovim, you just need to create a config file at ~/.config/nvim/init.vim and paste them into it.

If you want to know what a current setting is you can type:

:set [name of variable]?

For example, :set linebreak? will show you the current linebreak setting, either linebreak or nolinebreak.

Change the escape key to jk

Maybe the most important key in Neovim is the escape key, you use it constantly to move from insert mode to normal mode. The physical position of the escape key in modern keyboards in not easy to reach, I strongly suggest you change escape to be jk, this is very easy to use for touch typing:

 inoremap jk <Esc>
 tnoremap jk <C-\><C-n>

Give your document window a title

Neovim uses your terminal to run. By default, your Neovim terminal window is not given a title. If you have different windows open with different documents then you have no way of knowing which to select in your windows manager. This is obvious nonsense we can put a stop to:

set title

Tabs vs spaces. Use spaces.

It is 2020 and we have all collectively agreed to have the tab key create spaces, anything else would have been insane. How many spaces each tab key creates is a personal preference, but I am going for 2. Neovim also has an option that controls indentation, so we should use the same value for that:

 set expandtab
 set tabstop=2
 set shiftwidth=2

Soft wrapping, it’s complicated

Ideally, when we edit a document we want to view it with a line length of about 80-100 characters, anything more makes it hard to scan.

If hard line breaks are used then the document is prevented from being flexibly wrapped by other software. This software would be much better served if it could be allowed to soft wrap the lines depending on screen size and resolution. Unfortunately, configurable soft wrapping is a problem for Neovim.

Neovim can only soft wrap to the width of the current window

Neovim soft wraps to the width of the screen, and unless you want to enter a nightmare of configuration then you are just going to have to live with that. To make matters worse, it cuts words as it wraps. To make the best of this bad situation we can do two things:

  • Make our edit window take half the screen. This should make the line length fit within 80-100 characters depending on your screen size and resolution. In Gnome this is very easy, just hit the super key and the left or right arrow.
  • Set Neovim to not split words when soft wrapping, but split on words:
set linebreak

I suggest you keep an open terminal to the right and Neovim to the left, then you should experience a deep zen-like feeling of minimalism. You never need to leave this sacred safe space again.

Use vscode-neovim to soft wrap Markdown documents

One solution for flexible soft wrapping Markdown documents is to use the vscode-neovim extension in Visual Studio Code. Add this to your settings.json:

  "[markdown]": {
    "editor.wordWrap": "wordWrapColumn",

but remember the dangers of leaving the safe space.

Use the system clipboard

On some systems, Neovim will not have support for the system clipboard available. It relies on using an external package to integrate.

To test your system, inside Neovim move to a line with some text on and press "+yy. The + sign means use the system clipboard. Try to ctrl-v it into some other text editor. If it doesn’t work you are most likely missing the external clipboard package that Neovim needs. If you are using xorg it is probably the xclip package. For example, in Centos 8:

sudo dnf install xclip

If you have problems, consult the manual using :h clipboard, it lists all the external clipboard tools that can be used.

The idea of having a separate + register for the system clipboard is so you don’t constantly overwrite your system clipboard with every yank and deletion. For example, if you copy a link from the browser then delete a line to make room for it, the clipboard will be empty. I don’t find this a problem and don’t want to have to type "+ in front of commands, if you agree then you can have the system + register be used by default:

set clipboard=unnamedplus

Enable the spell checker

Neovim has a great spell checker built-in, but you would never know it unless you stumbled across it. To turn it on, set the language, and create a shortcut to toggle it on and off:

 set nospell spelllang=en_us
 nnoremap <silent> <F6> :set invspell<cr>
 inoremap <silent> <F6> <C-O>:set invspell<cr>

Check the spelling documentation on how to use it.

Markdown fenced language syntax highlighting

If you are editing a document, then you are probably using Markdown. Neovim has built-in syntax highlighting for most languages. A great feature of Markdown is the ability to embed code into your document using ‘code fencing’. To enable syntax highlighting for the code within those fences:

let g:markdown_fenced_languages = ['bash=sh', 'javascript', 'js=javascript', 'json=javascript', 'typescript', 'ts=typescript', 'php', 'html', 'css']

I have selected the languages I want included, but you can alter them.

Neovim can run as a service for other editors

One of the big features that Neovim has that Vim lacks is the ability to communicate with other software via an API. This means that you can use Neovim as the engine for other editors. The most interesting use for this that I have found is the vscode-neovim extension for Visual Studio Code.

vscode-neovim runs Neovim in the background and pipes all your keyboard commands through it. You get all the functionality of Neovim added to Visual Studio Code! Integrating Code can be a blessing or a curse depending on how easily you are distracted. The main benefit of just using Neovim is to stay focused and accept any limitations, otherwise, you can get sucked down the configuration and new feature rabbit hole and never return.

If you really want an IDE experience then using vscode-neovim is a far better option than spending weeks trying to turn Neovim into Code using extensions like coc.vim. The next big release of Neovim, 0.5, will have full language server integration with features like auto-completion, I advise you to wait for that to be released if you want to stay zen.

Bonus: Visual Studio Code dark color scheme

The default colors for syntax highlighting in Neovim are not to everyone’s taste. I think Visual Studio Code dark theme’s colors are a lot better. Luckily it has been converted for Vim/Neovim in the form of vim-code-dark. While it is not built-in, it is just a single config file codedark.vim that you can copy into ~/.config/nvim/colors

Add the following to activate it:

colorscheme codedark

It includes syntax highlighting for many different languages and works great in conjunction with Markdown fenced language syntax highlighting.

vim-code-dark vs vscode dark+ visual comparison

While not identical, it is close enough (select view image to see 1080p):


Neovim is an amazing text editor, but out of the box, it has many strange settings. It is a mistake to resort to 3rd party extensions until you know you can’t fix the problem using built-in features. Using the settings in this guide we have:

  • Changed the escape key to make it much easier to use
  • Given our document windows titles so we can easily find them using our windows manager
  • Changed to use spaces rather than tabs
  • Learned about soft wrapping and altered the settings for the best possible experience
  • Enabled the system clipboard and spell checker
  • Enabled syntax highlighting for code blocks inside markdown files
  • Swapped the default color scheme for an emulated Visual Studio Code dark theme.

If you have any more settings you find essential please add them in the comments. To leave you, here is my entire init.vim with a few extra bonus settings:

" select all
nnoremap <C-a> ggVG<CR>

" use system clipboard by default
set clipboard=unnamedplus

" esc to turn off search highlighting
nnoremap <silent> <esc> :noh<cr>

" better indenting in visual mode with < >
vnoremap < <gv
vnoremap > >gv

" stop cursor keys in normal mode
noremap <Up> <Nop>
noremap <Down> <Nop>
noremap <Left> <Nop>
noremap <Right> <Nop>

if exists('g:vscode')

  " call VSCodeCommentary on the visual selection, and then re-select that visual selection with gv
  xmap <C-/> <Plug>VSCodeCommentarygv
  nmap <C-/> <Plug>VSCodeCommentaryLine


  " format, can't use ctrl+shift in the terminal, but this works
  nnoremap <C-i> gggqG<CR>

  " can also use :!deno fmt %
  au FileType markdown setlocal formatprg=prettier\ --parser\ markdown
  au FileType javascript setlocal formatprg=prettier\ --parser\ typescript
  au FileType javascript.jsx setlocal formatprg=prettier\ --parser\ typescript

  " disable accidentally pressing ctrl-z and suspending 
  nnoremap <c-z> <Nop>

  " disable recording
  nnoremap q <Nop>

  " let g:codedark_conservative = 5
  colorscheme codedark

  " no swapfiles
  set noswapfile
  " converts tabs to spaces
  set expandtab
  " insert 6 spaces for a tab
  set tabstop=5
  " number of space characters inserted for indentation
  set shiftwidth=5
  " wrap at word boundaries rather than right at the terminal edge
  set linebreak
  " change terminal title to name of file
  set title
  " set spell checking language
  set nospell spelllang=en_us

  " map js to escape
  inoremap jk <Esc>
  tnoremap jk <C-\><C-n>

  " toggle spell checking in normal and insert mode
  nnoremap <silent> <F9> :set invspell<cr>
  inoremap <silent> <F9> <C-O>:set invspell<cr>

  " ctrl-s (you must add `stty -ixon` to ~/.bashrc)
  nnoremap <silent><c-s> :<c-u>update<cr>
  inoremap <silent><c-s> <c-o>:update<cr>
  vnoremap <silent><c-s> <c-c>:update<cr>gv

  let g:markdown_fenced_languages = ['bash=sh', 'javascript', 'js=javascript', 'json=javascript', 'typescript', 'ts=typescript', 'php', 'html', 'css']


4 replies on “Neovim Explained: How to Build a Plugin-Free Nvim Config That Unlocks Builtin Features”

I have a zsh function called “Fancy-ctrl-z” that helps me to get me back to my file after a ctrl-z

fancy-ctrl-z () {
if [[ $#BUFFER -eq 0 ]]; then
zle accept-line
zle push-input
zle clear-screen
zle -N fancy-ctrl-z
bindkey ‘^Z’ fancy-ctrl-z

I can press Ctrl-z to jump to a terminal, run any command and get back easily

Leave a Reply

Your email address will not be published. Required fields are marked *