# nvim-metals, a Lua plugin that makes the Scala language server usable in Neovim

> nvim-metals is a Lua plugin from the Scala team that wires Metals, the Scala language server, into Neovim's built-in LSP support, with commands, settings and integrations for nvim-dap and Telescope. It suits Scala developers who want Metals features without the nvim-lspconfig setup, and it requires a recent Neovim and Coursier on the path.

**scalameta/nvim-metals** — A Metals plugin for Neovim

- Repository: https://github.com/scalameta/nvim-metals
- Website: https://scalameta.org/metals/
- Stars: 566 · Forks: 91
- Language: Lua
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/scalameta-nvim-metals

## A Neovim client for Metals, not a language server

nvim-metals is a Lua plugin built to provide a better experience while using Metals, the Scala language server, with Neovim's built-in LSP support. The homepage is scalameta.org/metals, and the repository lives under the scalameta organisation, which is the Scala tooling group behind Metals itself.

That relationship explains the design. The plugin is not a language server and does not implement any Scala analysis. It is the Neovim side of a protocol conversation, and the value it adds is everything around the language features: the commands Metals exposes, the configuration surface, and the integrations with other plugins.

The README points to a discussion thread listing all available features, which is a sensible way to see what the plugin adds before installing it.

The repository layout is a small Lua project. There is lua/ for the plugin code, doc/ for the help file, spec/ for the tests, a Makefile with the formatting, linting and test targets, a rockspec file for nvim-metals-scm-1, a neovim.toml and a selene.toml, and a stylua.toml for formatting. The .busted file points at the Busted test framework, and the Makefile runs the tests through luarocks, which is worth remembering if you want to run the suite yourself rather than only using the plugin.

## Prerequisites: recent Neovim, Coursier, and your own key mappings

The prerequisites section is short and each item bites in a different way.

You need the latest nvim, version 0.11.x or newer, and if you are on an older release the README tells you to target the tag that matches your version. You need Coursier installed locally, because the plugin uses Coursier to download and update Metals, and the footnote on that explains the Windows wrinkle: you should run coursier or cs.exe once from the command line so it installs itself, then add the bin directory to your path, and verify with cs --help from a new shell.

The third requirement is the one people miss. You need to have all the LSP mappings for the core functionality you want set up. By default the methods for things like goto definition and find references are there, but not automatically mapped, and the README points to a minimal example configuration for the mappings you need. It also notes that Metals uses those mappings for things like Jobs, popups and paths, so the plugin depends on your keymap setup in a way that is easy to overlook.

The fourth item is procedural. The README asks you to subscribe to a discussion thread that notifies you of any breaking changes, which for a plugin that drives a language server is a sensible thing to have in your reading list.

## Do not configure Metals twice, and Lazy.nvim is the intended path

The getting started section carries a note that is worth reading before anything else. The plugin works without needing to install nvim-lspconfig. If you have nvim-lspconfig installed for other languages, that is not a problem, but you must make sure you do not have Metals configured through nvim-lspconfig while using this plugin.

The footnote at the bottom of the README is blunter. It says not to try to use Metals both with nvim-lspconfig and with nvim-metals, as stuff will not work right. That is a hard constraint rather than a recommendation, and it makes sense: two clients attaching to the same server with different configurations will fight over the session.

The absolute minimal example is written for Lazy.nvim, and it shows the shape of the setup:

```lua
{
  "scalameta/nvim-metals",
  ft = { "scala", "sbt", "java" },
  opts = function()
    local metals_config = require("metals").bare_config()
    metals_config.on_attach = function(client, bufnr)
      -- your on_attach function
    end

    return metals_config
  end,
```

The plugin registers for three filetypes, scala, sbt and java, and the configuration is obtained from require("metals").bare_config(), with your own on_attach function layered on top. The config hook then creates an augroup and a FileType autocmd that calls require("metals").initialize_or_attach(metals_config) when a matching file is opened.

The README is honest that the minimal example assumes you have your own attach function that sets up all your LSP mappings, and that it makes use of no configuration at all, so you will likely want something more involved.

## Commands, settings and two integrations

The documentation is split into three help topics, and the plugin is expected to be explored through them rather than through a settings file.

Commands are covered under :help metals-commands, and settings under :help metals-settings, with the full help available as :help nvim-metals. The README does not enumerate the commands, which is a deliberate choice for a plugin with this much surface, but it does point to a discussion thread with the full list of features.

Integrations are the third topic, under :help metals-integrations, and the README names the two that exist. The first is nvim-dap, which is the obvious pairing since debugging is where a language server's view of the program matters most. The second is Telescope, the fuzzy finder, which is how you would drive Metals' own search and symbol features from a picker.

What this tells you about the plugin's shape is that it is an adapter, not an opinionated setup. The configuration you write is largely the configuration of Metals, exposed through Lua, and the plugin's job is to start the server with it, attach to it, and make the extra commands available. That is a different proposition from a plugin that gives you a working configuration out of the box, and it is the right trade for a language server whose behaviour depends heavily on the build tool of the project you are in.

## Running the test suite, and what the repository reveals

The Makefile shows how the project is developed, and it is worth reading for what it implies about the plugin's scope.

Formatting is stylua over lua/ and spec/, with a format-check target that runs the same thing in check mode, and linting is selene over the same directories. The test targets are luarocks based: test-setup runs luarocks test spec/setup --local, test runs luarocks test --local, and test-handlers runs the handler suite specifically.

The local-test-setup target clones three example projects from GitHub, a multiple build file example, a mill-minimal project and a minimal Scala CLI test, and the clean target removes those three directories afterwards. That tells you something concrete about what the plugin has to cope with: Scala projects are not all built the same way, and a client for Metals has to work with sbt, mill and Scala CLI build definitions rather than one fixed layout.

The rest of the tree is standard Lua plugin infrastructure. There is a nvim-metals-scm-1.rockspec for luarocks installation, a neovim.toml and a selene.toml for the linter configuration, and a stylua.toml for the formatter settings.

On the maintenance side, the last push to the main branch was on 2026-07-02, which is more than three months before now, and the repository publishes no GitHub releases, so there is no tagged version to pin. The licence is Apache-2.0, with a LICENSE file at the root.

## Conclusion

Adopt nvim-metals if you write Scala in Neovim and want Metals features such as the command palette, imports and code actions without wiring the server up through nvim-lspconfig. Do not adopt it if you already configure Metals through nvim-lspconfig, because the README says using both at once does not work, and do not expect it to replace your LSP key mappings, since the plugin requires you to set those up yourself. Before you start, read the breaking changes discussion, confirm Coursier is on your path so the server can be downloaded and updated, and note the maintenance picture, with the last push on 2026-07-02 and no GitHub releases published.

## FAQ

### What does nvim-metals do?

It is a Lua plugin that provides a better experience while using Metals, the Scala language server, with Neovim's built-in LSP support. It registers for the scala, sbt and java filetypes, exposes the server's commands and settings, and integrates with nvim-dap and Telescope.

### Do I need nvim-lspconfig to use nvim-metals?

No. The plugin works without nvim-lspconfig being installed, and having it for other languages is fine, but you must not configure Metals through nvim-lspconfig while using this plugin, because the README says using both at once does not work right.

### What do I need installed before using nvim-metals?

Neovim 0.11.x or newer, Coursier installed locally so the plugin can download and update Metals, and your own LSP key mappings for the functionality you want. On Windows you should run coursier or cs.exe once from the command line so it installs itself, then add the Coursier bin directory to your path and check with cs --help.

### How do I set up nvim-metals with Lazy.nvim?

Use the plugin spec with ft = { "scala", "sbt", "java" }, take the configuration from require("metals").bare_config() and set your own on_attach, then in the config hook create an augroup and a FileType autocmd that calls require("metals").initialize_or_attach(metals_config). A fuller minimal example configuration is linked from the README.

### Which plugins does nvim-metals integrate with?

Two are listed under the metals-integrations help topic: nvim-dap for debugging and Telescope for the fuzzy finder picker. The plugin also expects the LSP mappings to be configured yourself, since Metals uses them for things like Jobs, popups and paths.

## Sources

- [Issues](https://github.com/scalameta/nvim-metals/issues)
- [License: Apache-2.0](https://github.com/scalameta/nvim-metals/blob/main/LICENSE)
- [Project website](https://scalameta.org/metals/)
- [README](https://github.com/scalameta/nvim-metals/blob/main/README.md)
- [scalameta/nvim-metals on GitHub](https://github.com/scalameta/nvim-metals)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/scalameta-nvim-metals
