Better Emacs Config: use-package
Posted on
use-package is an Emacs package which allows packages to be loaded declaratively. It's been around for ages and I've seen it used in other people's configurations, but I've only recently paid some real attention to it. I wish I'd learned how to use it sooner - it's really improved my Emacs config.
Let's look at an example:
(use-package magit
:bind (("C-x g" . magit-status)
("C-x C-g" . magit-status)))
When this is run, the use of the Magit package is declared and two key-bindings for its main function are defined. What's really nice here is that autoloads are installed for those key-bindings - the package isn't actually loaded until I actually type either of those shortcuts for the first time. This means Emacs can start faster and resources aren't used for features I don't use often (that said, Magit is something I do use all the time!).
Note use-package's clear, compact syntax. There's less ceremony for common startup tasks such as setting up key-bindings and I love how use-package encourages all setup related to a given package to be neatly grouped together.
Configuring Packages
It's common to need to run some code before or after a package is
loaded in order to set it up. With use-package this is done using
:init
(before loading) and :config
(after loading). Here's the
above example with some "helpful" messages inserted printed before and
after the magit package is loaded:
(use-package magit
:init
(message "Loading Magit!")
:config
(message "Loaded Magit!")
:bind (("C-x g" . magit-status)
("C-x C-g" . magit-status)))
Again, because loading of the package is deferred until one of the key-bindings is used, the messages won't appear until I actually hit one of those keys.
Deferred Loading
Key-bindings are just one way that a deferred package might be loaded
by use-package. Other mechanisms include assigning a file extension to
a mode defined in the package (via :mode
) or by adding a function
from the package into a hook (via :hook
). use-package provides
a variety of syntactic sugar to make this painless and concise.
Immediate Loading
Of course there are some packages which you always want to be loaded
immediately. use-package can handle this too through the :demand
keyword. Here's an example:
(use-package evil
:demand t
:custom
(evil-esc-delay 0.001 "avoid ESC/meta mixups")
(evil-shift-width 4)
(evil-search-module 'evil-search)
:bind (:map evil-normal-state-map
("S" . replace-symbol-at-point))
:config
;; Enable evil-mode in all buffers.
(evil-mode 1))
Here we have the Evil package
being loaded immediately (due to the :demand t
) with some configuration
set before it's loaded (some customisations need to be set before
loading), a key-binding added, and evil-mode being enabled globally.
Note the use of the :custom
keyword here. This is a clean way of
setting customisations that you could also set with Emacs' customize
functionality. It can be nice to keep customizations with the
use-package declaration, although I'm not that consistent about this
myself.
Key-bindings in Keymaps
In the previous example, the key-binding is slightly different to earlier examples because the binding is being set inside a specific keymap instead of globally. use-package provides clean syntax for this. Here's an example with multiple key-bindings being set up across multiple keymaps:
(use-package evil-args
:bind (:map evil-inner-text-objects-map
("a" . evil-inner-arg)
:map evil-outer-text-objects-map
("a" . evil-outer-arg)
:map evil-normal-state-map
("L" . evil-forward-arg)
("K" . evil-jump-out-args)
:map evil-normal-state-map
("H" . evil-backward-arg)
("L" . evil-forward-arg)
:map evil-motion-state-map
("H" . evil-backward-arg)
("L" . evil-forward-arg)))
Not bad! Much better than:
(define-key evil-inner-text-objects-map (kbd "a") 'evil-inner-arg)
(define-key evil-outer-text-objects-map (kbd "a") 'evil-outer-arg)
(define-key evil-normal-state-map (kbd "L") 'evil-forward-arg)
; you get the idea ...
Package Manager Integration
By default, use-package only loads packages that have already installed somehow, but it can integrate with a package manager too.
If you're already using the built-in Emacs package manager
(package.el)
then simply adding :ensure t
to a use-package
block will cause
use-package to download and install the package if it's not already
there. Extending our first example slightly:
(use-package magit
:ensure t
:bind (("C-x g" . magit-status)
("C-x C-g" . magit-status)))
This avoids the need to separately call package.el's install-package
function or use the list-packages
interface to install a package.
use-package can also work with other package managers. The powerful straight.el package manager has tight integration with use-package (it's what I use now).
What Next?
If you want to learn more about use-package, the official README is approachable and comprehensive. There's plenty more to it than what I've covered here, although you don't need to know much to start see its benefits.
Other articles that you might also find helpful:
- Configuring Emacs from Scratch — use-package by Suvrat Apte.
- Getting Started With use-package by Gregory J Stein.
Edit 2020-05-15: Use :custom
instead of setq
in :init
. Thanks Canatella.