Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
ergou edited this page Nov 4, 2025 · 34 revisions

Installing

Install globally with npm

npm install -g @vue/language-server

Install with mason

:MasonInstall vue-language-server

Configuration

Important

If you are on the recent nvim-lspcofig, you do NOT have to config on_init. See: https://github.com/neovim/nvim-lspconfig/pull/3943

Important

vue_ls was renamed from volar in nvim-lspconfig and mason Mason PR: https://github.com/mason-org/mason-lspconfig.nvim/pull/561 nvim-lspconfig PR: https://github.com/neovim/nvim-lspconfig/pull/3843

Important

For lazyvim user, since mason is locked at version 1 in lazyvim, please read through this issue for more information: https://github.com/LazyVim/LazyVim/issues/6236

-- If you are using mason.nvim, you can get the ts_plugin_path like this
-- For Mason v1,
-- local mason_registry = require('mason-registry')
-- local vue_language_server_path = mason_registry.get_package('vue-language-server'):get_install_path() .. '/node_modules/@vue/language-server'
-- For Mason v2,
-- local vue_language_server_path = vim.fn.expand '$MASON/packages' .. '/vue-language-server' .. '/node_modules/@vue/language-server'
-- or even
-- local vue_language_server_path = vim.fn.stdpath('data') .. "/mason/packages/vue-language-server/node_modules/@vue/language-server"

-- IMPORTANT: nvchad users cannot use `$MASON` directly as the option is set to `skip`, see: https://github.com/NvChad/NvChad/blob/29ebe31ea6a4edf351968c76a93285e6e108ea08/lua/nvchad/configs/mason.lua#L4

local vue_language_server_path = '/path/to/@vue/language-server'
local tsserver_filetypes = { 'typescript', 'javascript', 'javascriptreact', 'typescriptreact', 'vue' }
local vue_plugin = {
  name = '@vue/typescript-plugin',
  location = vue_language_server_path,
  languages = { 'vue' },
  configNamespace = 'typescript',
}
local vtsls_config = {
  settings = {
    vtsls = {
      tsserver = {
        globalPlugins = {
          vue_plugin,
        },
      },
    },
  },
  filetypes = tsserver_filetypes,
}

local ts_ls_config = {
  init_options = {
    plugins = {
      vue_plugin,
    },
  },
  filetypes = tsserver_filetypes,
}

-- If you are on most recent `nvim-lspconfig`
local vue_ls_config = {}
-- If you are not on most recent `nvim-lspconfig` or you want to override
local vue_ls_config = {
  on_init = function(client)
    client.handlers['tsserver/request'] = function(_, result, context)
      local ts_clients = vim.lsp.get_clients({ bufnr = context.bufnr, name = 'ts_ls' })
      local vtsls_clients = vim.lsp.get_clients({ bufnr = context.bufnr, name = 'vtsls' })
      local clients = {}

      vim.list_extend(clients, ts_clients)
      vim.list_extend(clients, vtsls_clients)

      if #clients == 0 then
        vim.notify('Could not find `vtsls` or `ts_ls` lsp client, `vue_ls` would not work without it.', vim.log.levels.ERROR)
        return
      end
      local ts_client = clients[1]

      local param = unpack(result)
      local id, command, payload = unpack(param)
      ts_client:exec_cmd({
        title = 'vue_request_forward', -- You can give title anything as it's used to represent a command in the UI, `:h Client:exec_cmd`
        command = 'typescript.tsserverRequest',
        arguments = {
          command,
          payload,
        },
      }, { bufnr = context.bufnr }, function(_, r)
          local response = r and r.body
          -- TODO: handle error or response nil here, e.g. logging
          -- NOTE: Do NOT return if there's an error or no response, just return nil back to the vue_ls to prevent memory leak
          local response_data = { { id, response } }

          ---@diagnostic disable-next-line: param-type-mismatch
          client:notify('tsserver/response', response_data)
        end)
    end
  end,
}
-- nvim 0.11 or above
vim.lsp.config('vtsls', vtsls_config)
vim.lsp.config('vue_ls', vue_ls_config)
vim.lsp.config('ts_ls', ts_ls_config)
vim.lsp.enable({'vtsls', 'vue_ls'}) -- If using `ts_ls` replace `vtsls` to `ts_ls`

-- nvim below 0.11
local lspconfig = require('lspconfig')
-- If using vtsls
lspconfig.vtsls.setup vtsls_config
-- If using ts_ls
lspconfig.ts_ls.setup ts_ls_config
lspconfig.vue_ls.setup vue_ls_config

I am not and do not want to use nvim-lspconfig

To make the lsp work without nvim-lspconig the only extra options you have to maintain are cmd, filetypes and root_markers.(Assume you are on nvim 0.11)

Custom Component Highlight

Since 3.0.2, semantic tokens are handled on the vue_ls side rather than tsserver, and the token name has changed, to adopt this change you have to:

  1. set client.server_capabilities.semanticTokensProvider.full = true (To be fixed, see #5542)semanticTokensProvider.full set to true from v3.0.5, you can also set this to false for vtsls or ts_ls if the filetype is vue(Note: you have to set back to true if none vue).
-- vtsls or ts_ls
on_attach = function(client)
  if vim.bo.filetype == 'vue' then
    existing_capabilities.semanticTokensProvider.full = false
  else
    existing_capabilities.semanticTokensProvider.full = true
  end
end
  1. Set highlight group @lsp.type.component, to preserve the old behavior you can link it to @type

nvim-cmp integration

Check out this discussion

Vue 2 Support

Please make sure you are using ~3.0(0 as minor version) for vue 2 support.

For more information please see: https://github.com/vuejs/language-tools/discussions/5455

Clone this wiki locally

Morty Proxy This is a proxified and sanitized view of the page, visit original site.