Open-source project
emacs-lsp/lsp-ui avatar
emacs-lsp/lsp-ui

lsp-ui: the panels that turn lsp-mode into an editor experience

UI integrations for lsp-mode

1,118 stars143 forksEmacs LispGPL-3.0

At a glance

What is it?
lsp-ui is the Emacs Lisp package that holds the higher level UI modules of lsp-mode, from the symbol information sideline and the peek window to the documentation child frame and the imenu index. It is GPL-3.0, it installs from MELPA, and it is normally activated for you by lsp-mode itself.
Who is it for?
lsp-ui fits an Emacs user who already runs lsp-mode and wants the diagnostics, hover, code actions and cross reference views as visible panels rather than a flycheck overlay, and it costs one package to get them. It does not fit a setup where lsp-auto-configure is nil, because lsp-ui is then not activated and nothing appears, and it does not fit someone who prefers a single flycheck buffer.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 126 days ago.
What is it written in?
Mainly Emacs Lisp, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The user interface of lsp-mode lives in its own package

lsp-ui contains all the higher level UI modules of lsp-mode, like flycheck support and code lenses. That split is deliberate, and it explains what the package is: not a language server client, not a completion engine, but the visible surfaces of the editor.

The modules are self-contained enough to be understood one at a time, and each one is a small feature with its own set of variables.

lsp-ui-sideline shows information about the symbols on the current line, and also shows flycheck diagnostics and LSP code actions. lsp-ui-peek adds a peek feature for cross references. lsp-ui-doc shows object documentation at point in a child frame, and can also render it in a WebKit widget. lsp-ui-imenu shows imenu entries, the index of the symbols in the buffer.

The result is a coherent editor rather than a pile of popups. Diagnostics, hover text, code actions, documentation and the symbol index are all visible at once, in places you chose, and each of them is tunable.

The package is published on MELPA and MELPA Stable, has a Discord server and runs a CI workflow with a test badge in the README. The code is a set of Emacs Lisp files, lsp-ui.el for the package itself and then lsp-ui-sideline.el, lsp-ui-peek.el, lsp-ui-doc.el, lsp-ui-imenu.el, lsp-ui-flycheck.el and lsp-ui-util.el, with the documentation as a mkdocs site and an Eask file for the package build.

Activation is automatic unless you have disabled lsp-mode's own setup

The installation is one line, and the README's phrasing is worth reading closely.

By default, lsp-mode automatically activates lsp-ui unless lsp-auto-configure is set to nil. So for most people the package is not something you wire up, it is something that appears when lsp-mode starts. You only have to put (use-package lsp-ui) in your config and the package will work out of the box.

elisp
(use-package lsp-ui)

The alternative route is the built-in package manager, with the README showing the interactive command:

bash
M-x package-install [RET] lsp-ui [RET]

The lsp-auto-configure detail is the one that catches people. A configuration that sets lsp-auto-configure to nil, which is a common choice for people who want full control over lsp-mode's setup, will not activate lsp-ui either. The result is that the package is installed, nothing appears, and it is easy to conclude that the package is broken. It is not, it is simply not switched on.

The same logic applies to the other direction. If you already have lsp-mode configured with lsp-auto-configure left at its default, installing lsp-ui changes your editor's appearance immediately, because the sideline and the code lenses start showing up in buffers you were used to seeing plain.

The sideline is where diagnostics stop being invisible

lsp-ui-sideline shows information of the symbols on the current line, and it also shows flycheck diagnostics and LSP code actions. The idea is that the information about the line you are looking at is drawn next to it, in the margin, rather than in a separate buffer you have to visit.

The customisation variables are the whole story here, and they are unusually well chosen.

lsp-ui-sideline-show-diagnostics, lsp-ui-sideline-show-hover and lsp-ui-sideline-show-code-actions each switch one category on or off, so you can keep code actions visible while dropping hover text if it is in your way.

lsp-ui-sideline-update-mode is the interesting one. When set to 'line' the information will be updated when the user changes the current line, otherwise the information will be updated when the user changes the current point. That is a performance and stability trade as much as a behaviour one, since updating on every point movement means recomputing and redrawing as the cursor moves within a line.

lsp-ui-sideline-delay is the number of seconds to wait before showing the sideline, and lsp-ui-sideline-diagnostic-max-lines defaults to showing only the first line of diagnostic messages, with the README's advice being to increase it for more verbose messages and to decrease it if flickering occurs.

That last variable is the one to think about before you start tuning anything else. A sideline that redraws on every keystroke is the classic complaint about this kind of margin UI, and the documented remedy is to show fewer lines of the message and to delay the display.

Peek gives cross references a window and a jump list of their own

lsp-ui-peek adds a peek feature, the README links to Visual Studio Code's peek documentation for the idea.

The Emacs way of doing it is to remap the xref commands. The README shows that xref-find-definitions and xref-find-references, bound to M-. and M-? by default, can be remapped inside lsp-ui-mode-map:

elisp
(define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions)
(define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references)

The remapping is what makes it feel native rather than bolted on. The xref commands keep their meaning, and the buffer-local keymap decides whether they open a window or navigate.

The part that goes beyond a simple window is the jump list. There is a window-local jump list dedicated to cross references, so you can step back and forward through the definitions you have looked at without disturbing the global jump list of the session:

elisp
(lsp-ui-peek-jump-backward)
(lsp-ui-peek-jump-forward)

Other cross references are supported too, including finding a workspace symbol by pattern, and a custom cross reference command for servers that expose their own:

elisp
(lsp-ui-peek-find-workspace-symbol "pattern 0")
;; If the server supports custom cross references
(lsp-ui-peek-find-custom 'base "$cquery/base")

The alternative to all of this is plain xref navigation, where the command takes you straight to the target. The difference is context: peek keeps you in the file you were reading, which matters when you are checking a definition to decide something and then want to keep editing where you were.

lsp-ui-doc renders documentation in a frame you can focus

lsp-ui-doc shows object documentation at point in a child frame, and the README shows three ways it can appear: the child frame, a WebKit widget, and a state where you focus into the lsp-ui-doc-frame.

The WebKit option is the one that stands out. Rendering the documentation in a WebKit widget means the markup the language server sends is displayed as the server intended it, which matters for documentation that contains formatting rather than plain text. It also means the documentation is not selectable as Emacs text, which is the trade.

The position variables are the main configuration surface. lsp-ui-doc-enable switches the feature on, lsp-ui-doc-position decides where to display the doc, with the values top, bottom or at-point, and lsp-ui-doc-side decides whether the doc goes left or right. lsp-ui-doc-delay is the number of seconds before showing the doc.

Then there are two triggers. lsp-ui-doc-show-with-cursor, when non-nil, moves the cursor over a symbol to show the doc, and lsp-ui-doc-show-with-mouse does the same for the mouse pointer. The mouse one is the setting that turns the documentation into an on-demand tooltip rather than something tied to where you are typing, and it is worth enabling for anyone who reads by pointing rather than by navigating.

The separate focus-into-lsp-ui-doc-frame behaviour is what makes the documentation usable for anything longer than a signature. Without it, the frame is a picture; with it, you can move the point inside the documentation and interact with it.

imenu is a symbol index in a window you can size and pin

lsp-ui-imenu shows imenu entries, which in Emacs terms is the list of the definitions and headings in the current buffer, and it places them in a window next to the code.

Its customisation is mostly about the window, which is the practical problem with any side panel. lsp-ui-imenu-kind-position places the kind of the entry, that is the label that says whether an entry is a function or a variable, and lsp-ui-imenu-buffer-position decides where the buffer window sits. lsp-ui-imenu-window-width sets the window width, and lsp-ui-imenu-window-fix-width, when non-nil, means the window will not be resizable, for example unaffected by balance-windows.

That last option is the useful one in practice. Emacs has several ways of adjusting window widths automatically, and an index panel that is resized every time a window is balanced is unusable. Pinning the width is a one line change with an immediate effect.

Two more options control the refresh behaviour. lsp-ui-imenu-auto-refresh refreshes when necessary, and lsp-ui-imenu-refresh-delay is the delay to refresh imenu. There is also lsp-ui-imenu--custom-mode-line-format for the mode line format, which is how the panel gets its own header.

The module is small, and it is the one most likely to be overlooked, because an index panel is something you use every day and only notice when it is missing.

GPL-3.0, an Eask build, and a CI target that does not run the tests

lsp-ui is licensed under GPL-3.0, with the LICENSE file at the root of the repository. For a package that is loaded into other people's Emacs configuration, that is the licence the Emacs ecosystem expects.

The build is driven by Eask, the Emacs package development tool, and the Makefile in the repository shows the whole lifecycle. The build target packages and installs the package and cleans the compiled elc files, compile runs the compilation step, test installs the dev dependencies and then runs ert-runner over the test files with the exclusions '!no-win' and '!org', and checkdoc runs the docstring checker. The lint target runs package-lint.

One line in that Makefile is worth quoting, because it is the kind of honest note that tells you about the project's priorities: the ci target is clean build compile checkdoc, and it carries a FIXME comment to add test and lint. So the continuous integration workflow that the README badges runs the package build, the compilation and the docstring check, but not the test suite and not the linter, even though both targets exist in the Makefile.

The release history explains the rest. The tags are 8.0.0 from 2021-09-04, 8.0.1 from 2022-07-14 and 9.0.0 from 2024-04-06, and the last push to the repository was on 2026-06-19. A stable package on MELPA Stable that has not cut a release since 2024 is a normal state for a mature Emacs package: the code keeps getting fixes through the master branch and the stable archive is refreshed when the maintainers choose to promote a release.

For a user, the practical consequence is that you should install from MELPA Stable if you want a known good release, and from MELPA if you want the newest fixes. The README carries badges for both.

Editorial conclusion

lsp-ui fits an Emacs user who already runs lsp-mode and wants the diagnostics, hover, code actions and cross reference views as visible panels rather than a flycheck overlay, and it costs one package to get them. It does not fit a setup where lsp-auto-configure is nil, because lsp-ui is then not activated and nothing appears, and it does not fit someone who prefers a single flycheck buffer. Before you configure it, decide whether your sideline should update on line or on point movement, because lsp-ui-sideline-update-mode changes both the behaviour and how much flicker you see when lsp-ui-sideline-diagnostic-max-lines is left at its default of one line.

Frequently asked questions

What is lsp-ui and how do I install it?

It contains the higher level UI modules of lsp-mode, such as flycheck support and code lenses. You can put (use-package lsp-ui) in your config, or use the built-in package manager with M-x package-install and then lsp-ui, and the package is also published on MELPA and MELPA Stable.

Why does nothing appear after I install lsp-ui?

lsp-mode automatically activates lsp-ui unless lsp-auto-configure is set to nil, so a configuration that disables lsp-mode's own setup also leaves lsp-ui inactive. The package is installed but not switched on in that case.

How do I make the lsp-ui sideline less noisy?

lsp-ui-sideline-diagnostic-max-lines defaults to showing only the first line of diagnostic messages, and the README advises increasing it for more verbose messages and decreasing it if flickering occurs. You can also set lsp-ui-sideline-delay to wait before showing, and lsp-ui-sideline-update-mode to 'line' so the information updates when the current line changes.

What does the lsp-ui peek window add over plain xref navigation?

lsp-ui-peek remaps xref-find-definitions and xref-find-references so cross references open in a window instead of navigating away, and it keeps a window-local jump list dedicated to cross references with lsp-ui-peek-jump-backward and lsp-ui-peek-jump-forward. It also supports workspace symbol lookup and custom cross references.

Can lsp-ui-doc display documentation as HTML?

Yes. The README shows the documentation in a child frame, in a WebKit widget, and a state where you focus into the lsp-ui-doc-frame. The variables lsp-ui-doc-position, lsp-ui-doc-side, lsp-ui-doc-delay, lsp-ui-doc-show-with-cursor and lsp-ui-doc-show-with-mouse control where and when it appears.

Official sources

  1. emacs-lsp/lsp-ui on GitHub
  2. License: GPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/emacs-lsp-lsp-ui.svg)](https://hysenlabs.com/projects/emacs-lsp-lsp-ui)

Was this useful?