Remember when I said Emacs can do everything? I wasn’t exaggerating. There’s a package for everything. Want to control Spotify from Emacs? There’s a package. Want to play Dungeons & Dragons? Package. Want to turn Emacs into a full-featured IDE that makes VS Code jealous? Oh boy, are there packages.
The Emacs package ecosystem is like a candy store where everything is free and the candy makes you more productive. Let’s go shopping.
Emacs has several package repositories:
Add them to your config:
;; Initialize package system
(require 'package)
;; Add package archives
(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
;; Initialize packages
(package-initialize)
;; Fetch package list if needed
(unless package-archive-contents
(package-refresh-contents))
🚸 IDE Refugee Note: This is like npm, pip, or cargo, but for your editor. And unlike VS Code extensions, these packages can fundamentally change how Emacs works, not just add features.
Let’s install which-key, a package that shows available keybindings:
M-x package-install RET which-key RET
Now configure it:
(require 'which-key)
(which-key-mode 1)
(setq which-key-idle-delay 0.3) ; Show hints after 0.3 seconds
Now when you type a prefix key like C-x and wait, you’ll see all available completions. It’s like training wheels for keybindings!
Managing packages manually gets tedious. Enter use-package, the declarative package configuration tool:
;; Install use-package if not already installed
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t) ; Auto-install packages
Now you can declare packages beautifully:
(use-package magit
:ensure t
:bind ("C-x g" . magit-status)
:config
(setq magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1))
(use-package company
:ensure t
:hook (prog-mode . company-mode)
:config
(setq company-idle-delay 0.1
company-minimum-prefix-length 2))
🤔 Why Though? Use-package makes your config self-documenting, auto-installs missing packages, and lazy-loads them for faster startup. It’s the difference between a config file and a config masterpiece.
;; Vertico: Better minibuffer completion
(use-package vertico
:ensure t
:init
(vertico-mode))
;; Orderless: Fuzzy matching
(use-package orderless
:ensure t
:custom
(completion-styles '(orderless basic))
(completion-category-overrides '((file (styles basic partial-completion)))))
;; Consult: Search and navigation commands
(use-package consult
:ensure t
:bind (("C-s" . consult-line)
("C-x b" . consult-buffer)
("M-y" . consult-yank-pop)))
;; Marginalia: Annotations in minibuffer
(use-package marginalia
:ensure t
:init
(marginalia-mode))
;; Projectile: Project navigation
(use-package projectile
:ensure t
:init
(projectile-mode +1)
:bind-keymap
("C-c p" . projectile-command-map))
;; Treemacs: File tree sidebar
(use-package treemacs
:ensure t
:bind
(:map global-map
("M-0" . treemacs-select-window)
("C-x t t" . treemacs)))
;; Magit: The best git interface ever created
(use-package magit
:ensure t
:bind ("C-x g" . magit-status))
;; Git-gutter: Show changes in fringe
(use-package git-gutter
:ensure t
:hook (prog-mode . git-gutter-mode)
:config
(setq git-gutter:update-interval 0.02))
;; Multiple cursors
(use-package multiple-cursors
:ensure t
:bind (("C-S-c C-S-c" . mc/edit-lines)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)))
;; Expand region: Semantic selection
(use-package expand-region
:ensure t
:bind ("C-=" . er/expand-region))
;; Rainbow delimiters: Colored parentheses
(use-package rainbow-delimiters
:ensure t
:hook (prog-mode . rainbow-delimiters-mode))
(use-package python-mode
:ensure t
:mode "\\.py\\'"
:config
(setq python-indent-offset 4))
(use-package elpy
:ensure t
:init
(elpy-enable))
(use-package blacken ; Auto-format with black
:ensure t
:hook (python-mode . blacken-mode))
(use-package js2-mode
:ensure t
:mode "\\.js\\'"
:config
(setq js2-basic-offset 2))
(use-package typescript-mode
:ensure t
:mode "\\.ts\\'")
(use-package prettier-js
:ensure t
:hook ((js2-mode . prettier-js-mode)
(typescript-mode . prettier-js-mode)))
(use-package web-mode
:ensure t
:mode (("\\.html?\\'" . web-mode)
("\\.css\\'" . web-mode)
("\\.jsx\\'" . web-mode))
:config
(setq web-mode-markup-indent-offset 2
web-mode-css-indent-offset 2
web-mode-code-indent-offset 2))
(use-package emmet-mode
:ensure t
:hook (web-mode css-mode))
(use-package lsp-mode
:ensure t
:commands (lsp lsp-deferred)
:hook ((python-mode . lsp-deferred)
(js2-mode . lsp-deferred)
(typescript-mode . lsp-deferred))
:config
(setq lsp-idle-delay 0.6
lsp-prefer-capf t))
(use-package lsp-ui
:ensure t
:hook (lsp-mode . lsp-ui-mode)
:config
(setq lsp-ui-doc-enable t
lsp-ui-doc-delay 0.5))
(use-package company
:ensure t
:hook (after-init . global-company-mode)
:config
(setq company-idle-delay 0.1
company-minimum-prefix-length 2
company-show-numbers t))
(use-package company-box ; Better UI
:ensure t
:hook (company-mode . company-box-mode))
(use-package flycheck
:ensure t
:init (global-flycheck-mode)
:config
(setq flycheck-display-errors-delay 0.3))
;; Doom themes: Beautiful modern themes
(use-package doom-themes
:ensure t
:config
(load-theme 'doom-dracula t)
(doom-themes-visual-bell-config)
(doom-themes-org-config))
;; Doom modeline: Pretty status bar
(use-package doom-modeline
:ensure t
:init (doom-modeline-mode 1)
:config
(setq doom-modeline-height 25
doom-modeline-icon t))
;; All-the-icons: File icons
(use-package all-the-icons
:ensure t
:if (display-graphic-p))
;; Run M-x all-the-icons-install-fonts on first install
;; Undo-tree: Visualize undo history
(use-package undo-tree
:ensure t
:init (global-undo-tree-mode)
:config
(setq undo-tree-visualizer-timestamps t
undo-tree-visualizer-diff t))
;; Avy: Jump to any character
(use-package avy
:ensure t
:bind ("C-;" . avy-goto-char-timer))
;; Helpful: Better help buffers
(use-package helpful
:ensure t
:bind (("C-h f" . helpful-callable)
("C-h v" . helpful-variable)
("C-h k" . helpful-key)))
;; Which-key: Discover keybindings
(use-package which-key
:ensure t
:init (which-key-mode)
:config
(setq which-key-idle-delay 0.3))
;; Auto-update packages
(use-package auto-package-update
:ensure t
:config
(setq auto-package-update-delete-old-versions t
auto-package-update-hide-results t)
(auto-package-update-maybe))
;; Or manually:
;; M-x package-list-packages
;; Then press 'U' to mark updates, 'x' to install
Yes, you can create packages too!
;;; my-amazing-package.el --- Does amazing things -*- lexical-binding: t -*-
;; Author: Your Name
;; Version: 1.0
;; Package-Requires: ((emacs "27.1"))
;;; Commentary:
;; This package does amazing things
;;; Code:
(defun my-amazing-function ()
"Do something amazing."
(interactive)
(message "Amazing!"))
(provide 'my-amazing-package)
;;; my-amazing-package.el ends here
When packages misbehave:
;; Debug package loading
(setq use-package-verbose t)
;; Profile package loading
(use-package benchmark-init
:ensure t
:config
(add-hook 'after-init-hook 'benchmark-init/deactivate))
;; Then M-x benchmark-init/show-durations-tree
;; Isolate package issues
;; Start Emacs with: emacs -Q
;; Then load only the problematic package
Some packages that showcase Emacs’s versatility:
🎯 Pro Tip: Don’t install everything at once! Add packages as you need them. Your config should grow organically with your needs.
(use-package olivetti ; Distraction-free writing
:ensure t
:hook (text-mode . olivetti-mode)
:config
(setq olivetti-body-width 80))
(use-package writeroom-mode ; Full-screen writing
:ensure t
:bind ("C-c w" . writeroom-mode))
(use-package writegood-mode ; Writing style hints
:ensure t
:hook (text-mode . writegood-mode))
(use-package format-all ; Auto-format any language
:ensure t
:hook (prog-mode . format-all-mode))
(use-package editorconfig ; Respect .editorconfig
:ensure t
:config
(editorconfig-mode 1))
(use-package docker
:ensure t
:bind ("C-c d" . docker))
Chapter 6 introduces Org-mode, which isn’t just a package—it’s a way of life. It’s the reason many people switch to Emacs and never leave. You’re about to understand why.
But remember: packages are tools, not goals. The perfect Emacs setup is the one that helps you work better, not the one with the most packages. Though having a lot of packages is pretty fun too.
“I came for the text editor, I stayed for the packages, I became one with the ecosystem.” —The Emacs Package Enlightenment Process