avatarAmy Li

Summary

This article provides a step-by-step guide to configuring Language Server Protocol (LSP) in Neovim for coding in Next.js (React), TypeScript, and TailwindCSS, offering features like intelligent code completion, beautiful diagnostics, auto-formatting, and more.

Abstract

The article focuses on configuring LSP in Neovim for a Next.js project using TypeScript and TailwindCSS. It covers creating a new Next.js project, setting up LSP configuration with nvim-lspconfig, managing language servers using Mason.nvim, and handling root directory issues. The article also discusses setting up language servers for Lua, TypeScript, and TailwindCSS, enabling code formatting with Null-ls and Prettier, improving inline diagnostics, and enhancing LSP UI using the Lspsaga plugin.

Opinions

  • The author believes that it's easier to understand the different Neovim add-ons when combined with a realistic project.
  • The author suggests that it's not a bad idea to have a nice working environment with Lua, as it's used throughout the Neovim configuration process.
  • The author recommends using the Mason.nvim package manager for Neovim, as it easily installs and manages LSP servers, DAP servers, linters, and formatters.
  • The author emphasizes the importance of understanding how language servers are automatically launched and attached to the current buffer.
  • The author suggests that the highly performant UIs provided by the Lspsaga plugin can significantly improve the LSP experience in Neovim.

A Step-by-Step Guide to Configuring LSP in Neovim for Coding in Next.js (React), TypeScript, and TailWindCSS

Intelligent Code Completion, Beautiful Diagnostics, Auto Formatting, etc.

LSP-powered vim editor: working with the .tsx file in a Next.js project. GIF by Amy Li

It’s easier to understand the different Neovim add-ons when combined with a realistic project. So, in this article, we will slowly configure LSP in Neovim to meet our requirements for coding a Next.js project with TypeScript and TailwindCSS.

Table of Content

Create a New Next.js Project

Initiate a Next.js project:

Initiate a Nextjs (Nextjs +TypeScript + TailwindCSS) project. GIF by Amy Li

You can also follow this article to quickly create an application using Next.js, TypeScript, and TailwindCSS.

Run the following commands from the terminal:

$ yarn create next-app --typescript my-app
$ cd my-app
# Install tailwindcss
$ yarn add -D tailwindcss postcss autoprefixer
$ npx tailwindcss init -p

Now open a file to test whether typescript or typescriptreact is recognized in the Neovim.

nvim pages/index.tsx
Working with typescriptreact (.tsx) WITHOUT tsserver setup and autocompletion. GIF by Amy Li

Besides .tsx, sometimes we also need to create .ts.

Working with typescript (.ts) WITHOUT tsserver setup and autocompletion. GIF by Amy Li

In order to work beautifully with file types of .ts or .tsx.in our Next.js project using TypeScript, we need to set up thetsserver through LSP config as well as enable the Intelligent Code Completion. Let’s move on to the next section.

LSP Configuration With nvim-lspconfig

Nvim supports the Language Server Protocol (LSP), which means it acts as a client to LSP servers and includes a Lua framework `vim. lsp` for building enhanced LSP tools.

LSP facilitates features like go-to-definition, find-references, hover, completion, rename, format, refactor, etc., using semantic whole-project analysis.

So, what is nvim-lspconfig?

de>nvim-lspconfig is Configs for the Nvim LSP client.

Install nvim-lspconfig Plugin

Use nvim-lspconfig inside the plugins.lua file.

Create the lspconfig.lua file with the initial setup including importing nvim-lspconfig and enable formatting when buffer is saved.

Import nvim-lspconfig as well as attach the formatting feature inside the lspconfig.lua file.

Set Up the Language Server for Lua

As we’re using Lua language throughout our Neovim configuration process, so it’s not a bad idea to first have a nice working environment with Lua.

Before the Setup of the Lua Language Server

Here is what looks like without setting up the Lua language server:

No autocompletion and no AutoFormat when saving the Lua file with :w.

Writing in Lua language inside vim-terminal before setting up the language server for Lua: no auto-completion, and no AutoFormat. GIF by Amy Li

Install the Language Server

brew install lua-language-server

Enable the Language Server

Enable the Lua language server inside the lspconfig.lua file.

Then running:luafile % to apply the changes

After the Setup of the Lua Language Server

Now autocompletion is configured, as well as AutoFormat when saving the file with:w.

Writing in Lua language inside vim-terminal after setting up the language server for Lua: autocompletion and AutoFormat. GIF by Amy Li

Set Up the Language Server for TypeScript

Install the Language Server — ‘tsserver’

See server_configurations.md from the nvim-lspconfig doc(: help lspconfig-all from Nvim) for the full list of configs for each language server. In our case, we need to set up the language server for TypeScript — typescript-language-server, which depends on the typescript. Both packages can be installed via npm:

npm install -g typescript typescript-language-server

Enable the Language Server — tsserver

Set filetypes = { “typescript”, “typescriptreact”, “typescript.tsx” }
Enable the tsserver inside the lspconfig.lua file. Screenshoot by Amy Li

See the nvim-lspconfig README for details.

Enable the Autocompletion

Manual, triggered completion is provided by Nvim’s builtin omnifunc. For autocompletion, a general purpose autocompletion plugin is required.

So, let’s introduce a completion plugin for neovim coded in Lua — hrsh7th/nvim-cmp.

LSP configuration for the plugin:

Set up nvim-cmp:

Set up nvim-cmp.

Demo the Auto-completion in Typescript (typescript file — ‘.ts’)

Working with typescript (.ts) with tsserver setup and auto-completion. GIF by Amy Li

Demo the Auto-completion in React TypeScript (typescriptreact file — ‘.tsx’)

Working with typescriptreact (.tsx) with tsserver setup and auto-completion. GIF by Amy Li

Demo the Auto-completion in React

Auto-completion feature in React. Screenshot by Amy Li.

Manage LSP Language Server in a Better Way Using Mason LspConfig

In the above, we have applied several ways to install language servers:

// TtypeScript Language Server
npm install -g typescript typescript-language-server
// Lua Language Server
brew install lua-language-server

It’s not easy to manage LSP servers in such way. So let’s introduce another mason.nvim, which is a portable package manager for Neovim that runs everywhere Neovim runs. It can easily install and manage LSP servers, DAP servers, linters, and formatters. Please refer the available lsp servers for this mason-lspconfig.nvim

Installation Using Packer

use {
    "williamboman/mason.nvim",
    "williamboman/mason-lspconfig.nvim",
    "neovim/nvim-lspconfig",
}

Important: you must set up the plugins in the following order:

  1. mason.nvim
  2. mason-lspconfig.nvim
  3. Set up servers via lspconfig

Mason Config

require("mason").setup()
require("mason-lspconfig").setup({
    ensure_installed = { "sumneko_lua", "tsserver", "tailwindcss" }
})

ensure_installed indicates a list of servers to be automatically installed if they’re not already installed. So, the above configuration will install Lua language server(sumneko_lua), TypeScript language server (tsserver), and the TailwindCSS language server (tailwindcss) if we haven’t installed yet.

Or manually install the provided servers via :LspInstall [<server>...] .

such as :LspInstall tsserver .

Notice tsserver takes care of JavaScript.

Available LSP servers for mason-lspconfig.nvim.

You can find detailed documention for mason-lspconfig.nvim on its GitHub page:

Check out the mason-lspconfig.nvim documentation. GIF by Amy Li.

Show the UI for Mason — Visualize All the Installed Servers

Run :Mason to display the installed servers, and all the available ones you can choose to install.

Command ‘:Mason’ display the installed servers. Screenshot by Amy Li.

As always, the:help command from your Neovim terminal comes in handy whenever you want to refresh your knowledge about the plugins we just learned.

Look through the doc by running ‘:h mason’, and ‘:h mason-lsp’. GIF by Amy Li.

Root Directory for the Lspconfig — Problem & Solution

It’s necessary to understand how the language servers are automatically launched (attached to the current buffer).

In order to automatically launch a language server, lspconfig searches up the directory tree from your current buffer to find a file matching the root_dir pattern defined in each server's configuration. For pyright, this is any directory containing ".git", "setup.py", "setup.cfg", "pyproject.toml", or "requirements.txt"). Language servers require each project to have a root in order to provide completion and search across symbols that may not be defined in your current file, and to avoid having to index your entire filesystem on each startup.

- Archive: Neovim-nvim-lspconfig on 2021-07-03

Here is our lspconfig for tsserver so far:

require'lspconfig'.tsserver.setup{
  filetypes = { "javascript",", "typescript", "typescriptreact", "typescript.tsx" }
}

If we try to work with a JavaScript file in a project directory where there is no package.json , tsconfig.json,jsconfig.json, and .git, an error message will pop up when running:LspInfo from the current buffer.

And you will not see Intelligent code feature:

Two ways of fixing the problem:

  1. Any one of the missing files to your project directory
  2. Allow Lsp for JavaScript to run in any directory by adding the code in bold to lspconfig:
require'lspconfig'.tsserver.setup{
  filetypes = { "javascript", "typescript", "typescriptreact", "typescript.tsx" },
  root_dir = function() return vim.loop.cwd() end  
}

Result — TypeScript Language Server Is Finally Attached to Current Buffer

Run ‘:LspInfo’ to show the attached clients to current buffer (a JavaScript file). We notice tsserver supports several file types we have defined via the lspconfig before — javascript, javascriptreact, javascript.jsx, typescript, typescriptreact, andtypescript.tsx.

Run ‘:LspInfo’ to show the attached clients to current buffer (a JavaScript file). Command ‘:Mason’ display the installed servers. Screenshot by Amy Li.

Demo the Problem and Solution

Tip: Run command:LspInfo to see the attached language server client to current file buffer.

Run ‘:LspInfo’ to discover the problem and apply the fix. GIF by Amy Li.

Set Up the Language Server for TailwindCSS

Install Tailwincss Language Server

As mentioned in the above section, you could install it by adding it to the ensure_installed list as below:

require("mason").setup()
require("mason-lspconfig").setup({
    ensure_installed = { "tailwindcss" }
})

Or run command :LspInstall taillwindcss .

Make sure you have it installed by running the command: Mason.

Tailwincss Language Server Config

After we installed the tailwindcss language server in Neovim, let’s configure it via lspconfig :

local nvim_lsp = require "lspconfig"
nvim_lsp.tailwindcss.setup {}

Demo the Auto-completion in TailWindCSS

Enable Code Formatting Using Null-ls and Prettier

The AutoFormat for Lua has been enabled via LSP config. However, to have the same auto-formatting feature for file types of javascript, javascriptreact, typescript, and typescriptreact, we need to use the other two neovim plugins — null-ls and prettier.

jose-elias-alvarez/null-ls.nvim: “Use Neovim as a language server to inject LSP diagnostics, code actions, formatting, hover, and completion.”

MunifTanjim/prettier.nvim: “Prettier plugin for Neovim’s built-in LSP client.”

Install the Plugins

use('neovim/nvim-lspconfig')
use('jose-elias-alvarez/null-ls.nvim')
use('MunifTanjim/prettier.nvim')

We also need to install prettierd to run prettier fast in our editor:

npm install -g @fsouza/prettierd
# or use homebrew to install
brew install fsouza/prettierd/prettierd

Example Configs

Null-ls config
Prettier config

Enable Beautiful and Easy-to-Read Inline Diagnostics

The Problem With the Current Inline Diagnostics

Inline diagnostics are enabled automatically after our previous setup with the language server through LSP config, e.g. syntax errors or warnings will be annotated in the buffer.

However, the inline diagnostics are easily out of the screen. We have to zoom out of the screen to see the whole message, which makes it hard to read.

Inline diagnostics are enabled in the .tsx file but are not user-friendly. GIF by Amy Li

Solution — Showing the Inline Diagnostics Automatically in the Hover Window

Add the below code to our lspconfig. lua file:

Code source: Show inline diagnostics automatically in the hover window

Demo the Inline Diagnostics That Automatically Shows Up in the Hover Window

Show Inline diagnostics automatically in the hover window when moving your cursor to the target line in the .tsx file. GIF by Amy Li

Further Improvement on Inline Diagnostics by Adding Diagnostic Symbols in the Sign Column

Here is what the current diagnostic symbols look like in the Sign Column:

No diagnostic symbols are displayed in the Sign Column. Screenshot by Amy Li

Let’s add four types of diagnostic symbols — Error, Warn, Hint, and Info.

LSP config for the diagnostics. Screenshot by Amy Li

The diagnostic symbols are displayed in the Sign Column on the left:

Displaying inline diagnostic symbols in the Sign Column. Screenshot by Amy Li

Improve the LSP UI Using Lspsaga Plugin

Lspsaga is a lightweight LSP plugin based on neovim’s built-in LSP with a highly performant UI. (source: glepnir/lspsaga.nvim)

Lspsaga provides a bunch of highly performant UIs for LSP-related features including hover doc, peek definition, rename in preview, and much more.

Install Lspsaga Plugin Using Packer

Lspsaga Config

You can find some detailed example config in its GitHub Doc.

Example config:

Demo the Highly Performant UI for LSP

Demo the highly performant UIs provided by the Lspsaga plugin for LSP. GIF by Amy Li

Wrap Up

We have thoroughly discussed these useful plugins for Neovim built-in LSP support: nvim-lspconfig, null-ls, lspsaga, and nvim-cmp. With proper configurations, we could achieve Intelligent Code Completion, user-friendly inline diagnostics, etc. easily in Neovim.

Thanks for reading. I hope you find this article helpful for your Next.js project using a Neovim-based text editor.

Please check out my GitHub Repo for the configuration of nvim in Lua.

Read Further

Neovim
Nextjs
Typescript
Web Development
Recommended from ReadMedium