# purcell/emacs.d: A Batteries-Included Emacs Config and How to Install It

> Steve Purcell's Emacs configuration tree is a long-running, opinionated starting point for Emacs users, especially web developers. It installs by cloning into ~/.emacs.d and pulls the rest of its packages on first start.

**purcell/emacs.d** — An Emacs configuration bundle with batteries included

- Repository: https://github.com/purcell/emacs.d
- Stars: 7,075 · Forks: 2,040
- Language: Emacs Lisp
- License: BSD-2-Clause
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/purcell-emacs-d

## What purcell/emacs.d actually is, and who it is for

This is not a package you install from a registry. It is one person's Emacs configuration tree, described in the README as "continually used and tweaked since 2000" and offered as "a good starting point for other Emacs users, especially web developers". The repository is Emacs Lisp, licensed BSD-2-Clause, and its top level holds early-init.el, init.el, lisp/, site-lisp/ and test-startup.sh.

The README lists the languages the config extends, roughly in the order the author uses them: Haskell, Purescript, Elm and OCaml first, then Ruby and Rails, SQL, CSS and its preprocessors, JavaScript and TypeScript, HTML and templating languages, Common Lisp with Sly, Python, Rust, Clojure with Cider and nRepl, PHP and Erlang. If your work sits outside that list, the value proposition is weaker: you inherit a lot of configuration aimed at someone else's stack.

The README is explicit that the config is "somewhat geared towards OS X", while stating it is known to work on Linux and Windows. That phrasing matters. The primary target is macOS, and the other platforms are supported by report rather than by the author's daily use.

## How the config is structured and what happens at startup

The layout follows a conventional split. early-init.el runs before Emacs builds its initial frame, init.el is the entry point, and lisp/ holds the configuration modules. site-lisp/ is present at the top level for locally held code. The README does not enumerate the modules in lisp/, so the split between core defaults and language-specific setup has to be read from the files themselves.

Startup is where the design shows. Third-party packages are not vendored into the repository. According to the README, on the first start Emacs downloads and installs them automatically. That means the repository is small and the installed configuration is not: the effective package set is resolved at install time against whatever the package archives serve. Two people cloning the same commit on different days can end up with different package versions.

The README also notes that the config uses the desktop and session packages, so Emacs usually restores working buffers after a restart. That is a deliberate trade: restarting to pick up changes is cheap, but a bad package update can be reintroduced on every restart until you fix it.

Completion is a good example of the config's opinions. In-buffer completion uses corfu, minibuffer completion uses vertico, syntax checking uses flymake re-using backends from flycheck, and LSP support is provided through eglot. These are choices, not defaults, and they shape how the editor feels from the first keystroke.

## Installing purcell/emacs.d into ~/.emacs.d

The README gives one installation step: clone the repository so that its init.el ends up at ~/.emacs.d/init.el. If you already have a configuration there, move it aside first, because the clone will not merge with it.

```bash
git clone https://github.com/purcell/emacs.d.git ~/.emacs.d
```

Start Emacs. On that first start, further third-party packages are downloaded and installed automatically. Expect this to take a while and to produce network traffic; the README does not give a package count or a time estimate.

If errors appear during that first run, the README's remedy is to restart Emacs, and possibly to run M-x package-refresh-contents before doing so. That refreshes the package archive index, which is the usual cause of a failed install on a fresh machine.

The README states the config should run on Emacs 28.1 or greater and is designed to degrade smoothly, with the CI build as evidence, but warns that many enhancements may be unavailable on an older Emacs. Use the latest stable release you have available.

## Updating the config and the packages it depends on

Updating has two halves, and the README treats the second as mandatory rather than optional. The config itself comes from git.

```bash
git pull
```

Third-party packages are updated from inside Emacs. The README describes the sequence as M-x package-list-packages, then U followed by x. In the package list buffer, U marks available upgrades and x executes them. The README's justification for this is direct: the config assumes you update packages regularly, "because that's what I do".

After pulling changes or updating packages, restart Emacs so the changes take effect. The desktop and session packages usually bring your buffers back, so the restart is less disruptive than it sounds.

There is a real cost here that the README does not hide. You are tracking a moving set of third-party packages against a configuration that is tested by its author's CI, not against your machine. A package that changes behaviour between your update and the author's can break your setup before it breaks his.

## Customising without forking, and where that stops working

The README offers two levels of customisation. The light one is Emacs' own machinery: M-x customize, M-x customize-themes and similar. For code, the config looks for a file at ~/.emacs.d/lisp/init-local.el, which should end with a provide form.

```el
... your code here ...

(provide 'init-local)
```

If your code needs to run earlier in startup, the README names a second file, ~/.emacs.d/lisp/init-preload-local.el. That is the whole extension surface documented in the README.

Beyond that, the README's advice is to fork the repository and edit the config directly, remembering to merge changes from upstream regularly so the fork stays compatible with current package and Emacs versions. That is honest, and it is also the boundary: the README states plainly that the author cannot provide support for customised versions of this configuration. If your setup diverges, you own the divergence, including the merge work.

## Limitations, and when this is the wrong config

The most concrete constraint is version support. Emacs 28.1 is the floor. On anything older the config may still load, but the README says many enhancements may be unavailable, which is a poor trade if you cannot upgrade.

The second constraint is external programs. The README warns that making the most of the language-specific support will likely require further programs, particularly the ones flymake or flycheck use for on-the-fly syntax checking. Cloning the repository does not install those. A fresh machine will have the Emacs side configured and the checker side missing, and the README does not list which binaries each language needs.

The third is platform focus. macOS is the stated target, with Linux and Windows known to work. If you are on Windows and something misbehaves, you are outside the author's daily path.

The fourth is the support model. Issues go to the GitHub project, but only after you have confirmed you are on the latest code and the latest packages. Customised forks are out of scope by the author's own statement. This is a configuration to adopt as a whole or fork, not one to negotiate with.

## Alternatives and the difference in approach

The obvious alternative is to write your own init.el from scratch. That gives you a configuration you can explain line by line, with no upstream to merge and no package set chosen by someone else. The cost is that defaults, completion, syntax checking and language modes are all work you have to do, and the config's two decades of accumulated tweaks are exactly what you are choosing to redo.

A middle path is to keep this repository as a reference rather than as your live configuration. Read how it wires corfu, vertico, flymake and eglot together, then borrow the parts you want. You lose the automatic package installation and the CI-checked startup, but you also lose the obligation to track the author's package updates.

The README itself suggests forking when customisation goes deep. That is a genuine third option, and it changes the maintenance model: you get the config's structure and take on the merge work. The distinction between these paths is not features, it is who absorbs the next package breakage.

## Conclusion

Adopt purcell/emacs.d if you want a maintained, opinionated Emacs baseline and are willing to run the latest stable Emacs and update third-party packages regularly. Do not adopt it if you need supported customisations, since the README states the author cannot provide support for customised versions, or if you are pinned to an Emacs older than 28.1. Before committing, verify that Emacs 28.1 or greater is what you actually run, that your external flymake and flycheck checkers are installed, and that ~/.emacs.d is free of an existing configuration you still need.

## FAQ

### What is emacs.d?

emacs.d is the conventional directory Emacs reads its configuration from, and purcell/emacs.d is one such configuration tree: the author's personal setup, published as a starting point for other users, especially web developers.

### How does purcell/emacs.d compare with a plain .emacs file?

This project is a full configuration directory containing init.el, early-init.el, lisp/ and site-lisp/, installed by cloning it to ~/.emacs.d, rather than a single .emacs file. The README describes it as an opinionated config with packages downloaded automatically on first start.

### Where should purcell/emacs.d be installed?

The README says to clone the repository to ~/.emacs.d, ensuring that the init.el contained in the repo ends up at ~/.emacs.d/init.el.

### Which Emacs versions does purcell/emacs.d support?

The README states the config should run on Emacs 28.1 or greater and is designed to degrade smoothly, but warns that many enhancements may be unavailable on an older Emacs, and recommends using the latest stable release.

### Can I customise purcell/emacs.d without forking it?

Yes, up to a point. The README documents ~/.emacs.d/lisp/init-local.el for your own code and ~/.emacs.d/lisp/init-preload-local.el for code that must run earlier, and states that the author cannot provide support for customised versions of the configuration.

## Sources

- [Issues](https://github.com/purcell/emacs.d/issues)
- [License: BSD-2-Clause](https://github.com/purcell/emacs.d/blob/main/LICENSE)
- [purcell/emacs.d on GitHub](https://github.com/purcell/emacs.d)
- [README](https://github.com/purcell/emacs.d/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/purcell-emacs-d
