I’ve been using Doom Emacs for months and I still couldn’t explain why C-x and C-c and SPC all exist as separate things. Every time I hit a keybinding that didn’t work, I’d stare at the screen, try three variations, google it, paste the first StackOverflow answer into my config, and move on.
The information exists. It’s scattered across the Emacs manual (which reads like a legal document from 1985), the Doom Emacs discourse, Evil mode docs, and a dozen “getting started with Emacs” posts that each cover 20% of the picture.
This is the post I wish existed when I started. The full conceptual model — why there are multiple keybinding layers, which one wins when they conflict, and the patterns you’ll actually use daily.
Why so many layers?
Emacs is 40+ years old. It has accumulated keybinding systems the way an old house accumulates electrical panels. Each one made sense at the time. Together, they form something that looks like chaos but actually has a logic to it.
The short version: Emacs has multiple keybinding layers because it has multiple scopes of behavior. Some keys should work everywhere (save, quit, open file). Some should only work in specific contexts (run Python, fold Org headings). Some are user-defined. Some are from packages. They all need to coexist without stepping on each other.
That’s the architecture. Now let’s look at the actual layers.
The three keymap levels
Every keybinding in Emacs lives in a keymap — a data structure that maps key sequences to commands. There are three levels, and they have a strict precedence:
| Level | Keymap | Precedence | Scope |
|---|---|---|---|
| 1 | Minor mode maps | Highest | Active minor modes (multiple can coexist) |
| 2 | Major mode map | Middle | The current major mode (one per buffer) |
| 3 | Global map | Lowest | Always active, everywhere |
When you press a key, Emacs checks minor mode maps first, then the major mode map, then the global map. First match wins.
This is the single most important thing to understand. If a keybinding “doesn’t work,” it’s almost always because something higher in the precedence chain is intercepting it. A minor mode is eating your keypress before the major mode ever sees it.
You can inspect this yourself: SPC h v (or C-h v in vanilla Emacs) and look up any keymap variable. SPC h k will tell you what a specific key sequence does and which keymap it came from.
The five leader key prefixes
If keymaps are the mechanism, leader keys are the interface. They’re the prefix keys that organize commands into discoverable groups. Emacs has accumulated five of them:
C-x — The original
This is vanilla Emacs’s main prefix. It’s been around since the beginning. Global operations that work regardless of what mode you’re in.
C-x C-f— find (open) fileC-x C-s— save fileC-x b— switch bufferC-x 2— split window horizontallyC-x 1— close all other windowsC-x k— kill buffer
Whether you’re in Doom, Spacemacs, or bare Emacs — these work. They’re the bedrock.
C-c — Mode-specific + user custom
C-c is reserved for two things: mode-specific commands and user-defined keybindings. The Emacs convention is that C-c followed by a letter (C-c a, C-c b, etc.) belongs to the user. C-c followed by a control character or other prefix belongs to the mode.
In practice:
C-c C-n/C-c C-pin Org mode — navigate headingsC-c C-cin Org mode — toggle checkbox, execute block, confirm action (context-dependent)C-c C-ein Org mode — export dispatcher
The commands change depending on your major mode. That’s the whole point — C-c is the prefix that says “what mode am I in right now, and what can I do here?”
If you’re defining your own keybindings, C-c [letter] is the conventionally safe namespace. Packages aren’t supposed to bind there (though some do anyway, because conventions are suggestions).
SPC — Doom’s central dispatch
This is the Doom Emacs innovation. SPC (Space) as a leader key, only active in Evil normal/visual mode (not insert mode). It’s a centralized command hub that organizes almost everything into a mnemonic tree:
| Prefix | Category | Examples |
|---|---|---|
SPC f |
Files | SPC f f find file, SPC f s save |
SPC b |
Buffers | SPC b b switch, SPC b k kill |
SPC w |
Windows | SPC w v split vertical, SPC w w switch |
SPC p |
Projects | SPC p p switch project, SPC p f find file in project |
SPC g |
Git | SPC g g Magit status, SPC g b blame |
SPC s |
Search | SPC s p search project, SPC s s search buffer |
SPC o |
Open | SPC o t terminal, SPC o p treemacs |
SPC h |
Help | SPC h k describe key, SPC h v describe variable |
SPC m |
Major mode | Context-dependent — see below |
SPC m is particularly clever. It’s the equivalent of C-c but routed through the leader key system. In Org mode, SPC m shows Org commands. In Python mode, it shows Python commands. Same idea as C-c, different ergonomics.
Important: SPC only works in Evil command mode (normal/visual). If you’re in insert mode, you need to hit ESC (or jk or whatever your escape binding is) first. This trips everyone up for the first week.
M-x — The universal escape hatch
M-x (Meta + x, usually Alt+x or Option+x on Mac) opens the command palette. Type any command name, fuzzy-match it, execute it. It’s not really a “leader key” in the traditional sense — it’s a search interface over every command Emacs knows about.
When you can’t remember the keybinding, M-x is your friend. In Doom, SPC : does the same thing.
This is also how you discover commands. Type a keyword, see what comes up. Half my keybinding knowledge came from M-x completions.
M-g — Jump commands
A lesser-known vanilla Emacs prefix for navigation/goto commands:
M-g g— go to line numberM-g n/M-g p— next/previous error
Most Doom users never touch this because SPC covers the same territory with better discoverability. But if you’re in insert mode and need to jump to a line, M-g g still works.
How modes actually work
I mentioned “major mode” and “minor mode” above without explaining them. These are core Emacs concepts that control everything about how a buffer behaves.
Major mode: Every buffer has exactly one. It determines the fundamental editing behavior — syntax highlighting, indentation rules, available commands. Open a .py file and you’re in python-mode. Open a .org file and you’re in org-mode. Open a plain text file and you’re in text-mode. You can see the current major mode in the modeline at the bottom of the screen.
Minor mode: A buffer can have many of these simultaneously. They add functionality on top of the major mode. flycheck-mode gives you real-time syntax checking. visual-line-mode wraps long lines. evil-mode itself is a minor mode — it layers Vim keybindings on top of everything.
This is why minor mode keybindings have the highest precedence. Evil mode needs its j/k/h/l to override whatever the major mode might have bound to those keys. The precedence system makes this work without conflicts.
Switch major mode manually: M-x python-mode, M-x org-mode, etc. Toggle a minor mode: M-x flycheck-mode (it’s a toggle — call it again to turn it off). Emacs usually picks the right major mode from the file extension, but sometimes you need to override.
Evil mode naming conventions
If you’re using Doom, you’re using Evil mode (Vim keybindings in Emacs). The codebase has naming conventions that help you navigate:
| Pattern | Meaning | Example |
|---|---|---|
evil-xxx |
Core Evil mode function/variable | evil-delete, evil-normal-state |
+evil/xxx |
Doom module extension to Evil | +evil/window-move-right |
+evil:xxx |
Less common Doom convention (user/config) | Various custom commands |
+fold/xxx |
Doom’s code folding extensions | +fold/toggle, +fold/open-all |
The + prefix is Doom’s convention for “this is a Doom module addition, not core Evil.” The / separates namespace from function name. When you see +evil/ in a keybinding description, you know it’s Doom-specific, not standard Evil.
Keybindings you’ll actually use daily
Theory is great. Here’s what I actually type.
Navigation and files
| Keys | What it does | Context |
|---|---|---|
SPC f f |
Open file | Anywhere |
SPC f s |
Save current file | Anywhere |
SPC b b |
Switch buffer | Anywhere |
SPC b k |
Kill buffer | Anywhere |
SPC p p |
Switch project | Anywhere |
SPC p f |
Find file in project | Anywhere |
SPC s p |
Search in project (ripgrep) | Anywhere |
SPC o p / SPC o P |
Treemacs / Treemacs at current file | Anywhere |
Windows
| Keys | What it does |
|---|---|
SPC w v |
Split vertical |
SPC w s |
Split horizontal |
SPC w w |
Cycle to next window |
SPC w q |
Close window |
SPC w = |
Balance window sizes |
Evil editing (Vim-style)
This is where Doom really shines — you get the full Vim editing language inside Emacs.
| Keys | What it does | Notes |
|---|---|---|
C-w |
Delete word backward | In insert mode (Doom binds this to evil-delete-backward-word) |
viw then p |
Replace word with clipboard | Select inner word, paste over it |
viw then gp |
Replace word without overwriting clipboard | Same as above, but your yank register stays intact |
yiw |
Yank (copy) word under cursor | Then viwp elsewhere to replace |
"0p |
Paste from yank register | When default register got overwritten by a delete |
That last one — "0p — is a lifesaver. Here’s the scenario: you yank a word with yiw. Then you diw to delete another word. Now your default register has the deleted word, not the one you yanked. But the yank register ("0) still has your original yank. "0p pastes it.
Git (Magit)
| Keys | What it does |
|---|---|
SPC g g |
Magit status (the main interface) |
SPC g b |
Git blame |
SPC g l |
Git log |
Help (your debugging toolkit)
| Keys | What it does | When to use |
|---|---|---|
SPC h k |
Describe key — what does this key do? | “What did I just press?” |
SPC h v |
Describe variable — inspect keymaps, configs | “What’s the value of X?” |
SPC h f |
Describe function | “What does this command actually do?” |
SPC h b |
Describe bindings — show ALL keybindings | “What’s available in this mode?” |
SPC h d m |
Doom module docs | “How does this Doom module work?” |
How to set your own keybindings
Three main approaches, from Doom-specific to vanilla:
map! (Doom’s macro) — the recommended way in Doom:
;; in ~/.doom.d/config.el
(map! :leader
:desc "Open scratch" "x" #'doom/open-scratch-buffer)
(map! :after org
:map org-mode-map
:n "C-c t" #'org-todo)
define-key — standard Emacs, works everywhere:
(define-key global-map (kbd "C-c k") #'kill-whole-line)
global-set-key — shorthand for global-map bindings:
(global-set-key (kbd "C-c k") #'kill-whole-line)
In Doom, prefer map! — it understands Evil states (:n for normal, :i for insert, :v for visual) and integrates with which-key for discoverability.
Debugging keybinding problems
When a key doesn’t do what you expect:
SPC h kthen press the key — tells you exactly what command it runs and which keymap defined itSPC h v [keymap-name]— inspect the raw keymap variable (e.g.,org-mode-map)M-x describe-mode— shows all active modes and their keybindings in the current buffer- Check precedence — minor mode > major mode > global. If something unexpected is happening, a minor mode is probably intercepting
The most common issue: you defined a binding in the global map, but a minor mode is overriding it. The fix is to bind it in the minor mode map instead, or use map! with the appropriate Evil state to ensure it takes priority.
The mental model
Here’s the picture I wish someone had drawn for me:
┌─────────────────────────────────────────┐
│ Minor mode maps │ ← Checked first (Evil, flycheck, etc.)
│ ┌───────────────────────────────────┐ │
│ │ Major mode map │ │ ← Checked second (python-mode, org-mode)
│ │ ┌─────────────────────────────┐ │ │
│ │ │ Global map │ │ │ ← Checked last (C-x, universal bindings)
│ │ │ │ │ │
│ │ └─────────────────────────────┘ │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
Leader keys are interfaces into these maps:
C-x → mostly global map
C-c → mostly major mode map + user custom
SPC → Doom's unified interface (Evil minor mode map)
M-x → command search across everything
SPC works because Evil mode is a minor mode, and minor modes have the highest precedence. Doom routes everything through it, giving you one consistent interface. But the underlying system is still keymaps all the way down.
Resources
- Doom Emacs keybinding docs — the official guide to
map!and Doom’s binding system - Emacs Wiki: Key Sequence — deep dive on how Emacs parses key sequences
- Evil mode documentation — how Vim states and operators work inside Emacs
- Emacs manual: Keymaps — the authoritative (if dense) reference
The keybinding system is one of those things where the complexity is the feature. Emacs has survived 40 years because new paradigms (Vim! Doom! which-key!) can layer on top without breaking what came before. Once you see the layers, it stops feeling random and starts feeling like archaeology — every stratum tells you something about when it was built and what problem it solved.