Emacs is a wonderful piece of software. It’s easily my favourite program of all time. It can also be a little overwhelming to configure for the first time.

One of the many things that make Emacs great, though, is its dedicated and helpful user base. I hope to add a small contribution to that community with this post, going over what I would have told my past self as I was just starting out using Emacs and trying to write my first Emacs configuration file.

I’ll assume you know the basics of editing with Emacs and won’t cover general concepts like major modes or how to save files in Emacs. I’ll primarily be focusing on how you can get started personalising it.

Before we get into it, I’d like to cover three things: (1) Emacs is a Lisp interpreter at heart, (2) there are great places to get help, and (3) you should try out pieces of other people’s configs.

Preamble

A Primer in Emacs and Elisp

Emacs is often thought of as a text editor. While it is built around text editing and has many great packages to do so, I’d argue it’s more useful to think of it as an (Emacs) Lisp interpreter.

Your configuration file is a program of Emacs Lisp (or “elisp” for short) code that modifies the behaviour of Emacs itself. Most of the behaviours of vanilla Emacs are written in Elisp, and the rest in C. Therefore, almost everything that Emacs does, you can change! You can, of course, add and create new functionality as well.

The power to build your ideal editor is right at your fingertips! As long as you’re willing to learn a little bit of a Lisp dialect.

Thankfully, Elisp is a pretty readable language and as long as you watch your parentheses, you’ll be fine.

Get Help

The Emacs community is very friendly and are often keep to help out. I hang out a lot on the r/emacs subreddit and have gotten tons of tips and troubleshooting help from there. r/planetemacs is also great — they focus mostly on packages and blog posts/article links.

Emacs also has a pretty good documentation and help system. You can take the Emacs tutorial using C-h t. Ask Emacs for help using:

  • C-h v to see the documentation for a variable
  • C-h f to see the documentation for a function
  • C-h k to see what a keybinding does
  • C-h m to show help for the current major and minor modes

Or read on the Emacs Wiki.

Config Inspo

This brings me to my first and (I think) most helpful tip: Get inspired by other people’s configurations.

I’ll talk a little later about the basics of using Elisp for your config file, and that might be useful to help you read and understand the configs, but I think it’s good to have a starting point and to start experimenting early.

If you’re only just starting out, I invite you to check out my config and send me any questions you might have by email. (If you’re experienced, I invite you to send me any corrections or improvements!)

Here are some of the configurations I have stolen a lot of my configuration from and can heartily recommend checking out.

Check out the configs piece by piece by copying something into your own config and testing it out for a while. This is probably what has been the most helpful to me.

You could also get a lot of useful tips and tricks from checking out some curated, “sane defaults” builds of Emacs. These are designed to be minimal and extensible, just configuring some settings to make the initial switch to Emacs a little smoother and give you a solid foundation for writing your own config. Most of them don’t include too many extra packages, either. Some of these include:

Basics: Setting Variables

Your configuration file is an Emacs Lisp file called init.el, located in your Emacs home directory. Usually, this is at ~/.emacs.d/init.el. This is where you’ll add your code, and Emacs will load this file at startup.

Emacs can be configured either by modifying default behaviour or by installing and configuring community packages. You’ll probably want to do both.

There are quite a few blog posts and configurations that cover how to set “sane defaults” — i.e., modify the default behaviour of Emacs in a way most people would agree with. Check out the “early init” files or startup sections of the configs above. You might also want to check out sensible-defaults, which contains a lot of useful and, yes, sensible functions you can use in your config.

In general, you set the value of a variable using setq. Specify the name of the variable or the mode, then set it to an accepted value. Often variables will use t / nil and modes will use 1 / 0.

(setq enabled-option  t
	  disabled-option nil

	  enabled-mode    1
	  disabled-mode   0)

Installing Packages

After you’re done tweaking Emacs’ default behaviours, you’ll probably need some packages.

You can install a package from a package source or load an installed package in your load path using the require keyword.

(require 'package-name)

This is the easiest way to get started. Add the require-line to your init.el file and use setq to configure the package.

In fact, at the top of your init.el file, you should (require 'package). package.el is the default package manager for Emacs.

Many Emacs packages are available at MELPA, a package archive with generally high-quality packages. You might also want to add some other package sources. Here’s an example from my config, where I add quite a few sources and tell Emacs which places to check first.

(setq package-archives
	  '(("GNU ELPA"     . "https://elpa.gnu.org/packages/")
		("MELPA"        . "https://melpa.org/packages/")
		("ORG"          . "https://orgmode.org/elpa/")
		("MELPA Stable" . "https://stable.melpa.org/packages/")
		("nongnu"       . "https://elpa.nongnu.org/nongnu/"))
	  package-archive-priorities
	  '(("GNU ELPA"     . 20)
		("MELPA"        . 15)
		("ORG"          . 10)
		("MELPA Stable" . 5)
		("nongnu"       . 0)))

(package-initialize)

After setting up your package sources, you can add (require 'package-name) to your init.el and Emacs will automatically search, download, and install that package from the source. Remember to evaluate your init.el file for this to happen, either by using the command eval-buffer on init.el or by restarting Emacs.

Some packages are not available on MELPA & Co., in which case you might want to download it and save it on your computer manually. Or maybe you want to use a package you’ve written yourself.

In that case, you can also point Emacs to look for .el files on your computer. It’s practical to put all these files in the same place and add all of them to your “load path” so Emacs knows where to look for them. Here’s a setup for loading files from the ~/.emacs.d/local-lisp directory and sub-directories.

(defvar local-lisp "~/.emacs.d/local-lisp/")
(add-to-list 'load-path  local-lisp)
(let ((default-directory local-lisp))
  (normal-top-level-add-subdirs-to-load-path))
A note on package management

Using require and setting all your variables with setq statements can lead to a long and hard-to-read config when you add many packages.

That’s why some people prefer using other systems or wrappers around package.el, for example use-package, quelpa, or straight. You’ll see this a lot in the installation instructions for packages. Personally, I use use-package because it’s so prevalent and because it’s quite convenient.

But don’t get too caught up in this, you don’t need to use any of these, and it’s not very hard to refactor your code incrementally if you decide you want to try later.

Here’s a (simple) example of how to remove some visual elements from vanilla Emacs:

(setq tool-bar-mode     0    ;; Remove toolbar
      scroll-bar-mode   0    ;; Remove scollbars
      menu-bar-mode     0    ;; Remove menu bar
      blink-cursor-mode 0)   ;; Solid cursor, not blinking

And here’s an example of setting up the Olivetti package:

(require 'olivetti)     ;; Make sure Olivetti is installed
(setq olivetti-style t) ;; Set the 'olivetti-style' variable to true

Some Package Suggestions

I still think you should use bits and pieces of other people’s configurations to figure out what look and feel you prefer for your Emacs experience, but below I have gathered some packages I like and some alternatives to them.

Completion Systems

The really big one is your completion system. By default, some things in Emacs can feel a little clunky, such as entering commands, searching documents, and finding files. Thankfully, there are packages that give you a nice prompt with auto-completions, search tools, suggestions, prettification, etc. For example, they can auto-fill directory and file names when opening a file with C-x C-f.

These packages are usually combined with other supplementary packages, but there are a few common ones that people use as the core of their systems. They include:

As mentioned, there are many, many packages people pair with these systems. Ido comes built-in with Emacs, so you might want to start there. I personally use Vertico and have been very happy with that. I tried Helm, but found it a little overwhelming and felt I wasn’t using it to its full potential. People do love it though.

Play around with them a little, and if you like one, I’d say stick to it. Jumping around with these is kind of confusing, so I would only consider switching if I had a problem with the one I was using, or if another one had some very attractive feature.

Terminal Emulators

You can emulate a terminal inside Emacs. I.e., you never have to leave Emacs!

There’s a built-in terminal emulator, but it’s not particularly nice. Instead, I would suggest checking out either vterm or Eat.

Text Editing

Some packages just make text editing easier.

Take for example auto-completion packages such as the built-in dabbrev-mode, or corfu and company-mode.

Depending on your use, you might find multiple-cursors useful.

De Facto Standards

Magit is the best Git interface I’ve ever used.

Spell- and syntax-checking is usually done with Flyspell and Flycheck, respectively. These are pretty great.

Helpful gives you better help buffers.

When you start a keybinding combination, which-key shows a popup with suggestions for possible continuations of the sequence.

More Packages

For suggestions on more packages to check out, awesome-emacs is a curated and oft-updated package list. I also love browsing the top posts on the Emacs subreddits mentioned above.

Aesthetics: Fonts & Themes

The quickest way to make your Emacs experience feel more personalised!

There are many different ways to set fonts in Emacs. You can also set different fonts for the Emacs UI, for programming (monospaced or “fixed pitch” fonts) and for prose editing (regular or “variable pitch” fonts).

Here’s how I set my fonts. I use Roboto Mono for the UI and for programming, and I use Source Sans Pro for my other documents. I check that the relevant font is installed, and then set their size using the :height property.

(when (member "Roboto Mono" (font-family-list))
  (set-face-attribute 'default nil :font "Roboto Mono" :height 108)
  (set-face-attribute 'fixed-pitch nil :family "Roboto Mono"))

(when (member "Source Sans Pro" (font-family-list))
  (set-face-attribute 'variable-pitch nil :family "Source Sans Pro" :height 1.18))

Note that I don’t set the height of the programming (fixed pitch) font. It simply uses the same height as the default font. I do set the size of the prose (variable pitch) font, though, and I do that by giving a relative size – it’s relative to the size of the default font.

When it comes to themes, I’d recommend starting with a pack so you can explore many different options quickly, without needing to install a bunch of theme packages by hand. A great place to get started, is Doom Emacs’ theme pack called doom-themes. I particularly like the doom-nord theme. You can install and enable it like this:

(require' doom-themes)    ;; Make sure doom-themes are installed
(load-theme 'doom-nord t) ;; Load the doom-nord theme

I also really like the Modus themes, the Ef themes and the Nano themes. You can find many more on emacsthemes.com.

Programming

Eglot (Emacs polyGLOT) is an LSP server client for Emacs that comes pre-installed from Emacs 29. You can install it manually in Emacs >26.3.

You can also build Emacs with tree-sitter support. It’s available for Emacs >25.1.

Besides this, there are tons of major modes for various languages, usually called <language-name>-mode. These will often give you basic syntax highlighting and commands.

For example, here’s a simple way to get basic Python 3.11 support:

(require 'python-mode)
(setq python-shell-interpreter "python3.11")

Here’s my current setup for Haskell (except I’ve rewritten it to use require instead of use-package) using the Stack tool to build my Haskell projects:

(require 'haskell-mode)
(add-hook 'haskell-mode 'haskell-doc-mode)
(setq haskell-hoogle-command                    "hoogle"
		haskell-compile-stack-build-command     "stack build"
		haskell-compile-stack-build-alt-command "stack build --pedantic")
(define-key haskell-mode-map (kbd "C-c h")   'haskell-hoogle)
(define-key haskell-mode-map (kbd "C-c C-c") 'haskell-compile)

Hooks

In the Haskell example above, we briefly saw how to add a hook. A hook lets you run a function each time a specific thing happens. Most of the time, this is used to execute some function or activate/deactivate a mode when a certain mode is activated.

For example, in my Haskell config, I want to activate haskell-doc-mode each time I activate haskell-mode, so that I always have documentation on hand while I’m programming in Haskell.

Maybe you want to activate line numbers each time you start programming. To do that, you can add a hook to prog-mode like so:

;; Activate 'display-line-numbers-mode' when programming
(add-hook 'prog-mode-hook 'display-line-numbers-mode)

Note the apostrophe before the names of the functions (here, the names of the modes)!

You can do so many more things with hooks and get really creative! I mostly use them to set up my modes the way I like them. E.g., I always center my text buffers (Org mode, LaTeX, etc.) with Olivetti and I hide the line numbers.

Binding Keys

We can get pretty deep on the subject of keybindings, so I’ll try to keep it brief.

Most major modes with have a dedicated “mode map” of all the keybindings you can use while in that mode. Remember that you can always check out the currently available keybindings with C-h m, which describes the current major and minor modes.

This separation is very useful, as it allows you to bind the same keys to similar commands in different settings. E.g., you can bind C-c C-c to the relevant compilation command in all your programming languages if you like, even if the actual compilation command is different for each. You can also define your own custom keymaps and dictate when they should be used (another thing you can use hooks for!)

In general, you can bind a key globally, locally, or in a specific keymap like so:

(global-set-key KEY COMMAND)
(local-set-key  KEY COMMAND)
(define-key     KEYMAP KEY COMMAND)

Note that the KEY must be given in a way Emacs understands. You can use the kbd macro and then provide your keystrokes as a string, e.g., (kbd "C-c C-c"). Navigation keys and the function keys must be surrounded by <>, e.g., <tab> and <F1>.

Again from the Haskell config, I set keybindings for haskell-compile and haskell-hoogle commands in the haskell-mode-map.

(define-key haskell-mode-map (kbd "C-c h")   'haskell-hoogle)
(define-key haskell-mode-map (kbd "C-c C-c") 'haskell-compile)

You can define keybindings for any command in Emacs. People even build personalised mnemonic systems. If you want to dive deep into keybindings, I would recommend checking out the package Hydra.

Conclusion

Tinkering with my Emacs config is probably my favourite past-time, but even I will admit it was a bit of a headache in the very, very beginning. I hope this post has put a small dent in that frustration for you, or maybe given you some pointers to where you might want to take your WIP config.

I welcome corrections, feedback, and questions by email. Happy hacking!