# zsh-syntax-highlighting: fish-style colors in zsh, and the sourcing order that decides whether they work

> A zsh plugin that highlights the command line as you type it, built on ZLE widget wrapping or a pre-redraw hook depending on your zsh version. The README explains almost nothing about installation and everything about load order.

**zsh-users/zsh-syntax-highlighting** — Fish shell like syntax highlighting for Zsh.

- Repository: https://github.com/zsh-users/zsh-syntax-highlighting
- Website: github.com/zsh-users/zsh-syntax-highlighting
- Stars: 23,050 · Forks: 1,374
- Language: Shell
- License: BSD-3-Clause
- Published: 2026-10-08 · Updated: 2026-10-08 · Language: en
- Canonical page: https://hysenlabs.com/projects/zsh-users-zsh-syntax-highlighting

## Highlighting the buffer, not the output

The package description is one line: fish shell like syntax highlighting for Zsh. The mechanism sentence that follows is the important one. It enables highlighting of commands whilst they are typed at a zsh prompt into an interactive terminal, which helps in reviewing commands before running them, particularly in catching syntax errors. This is prompt-time highlighting of a line editor buffer, not post-hoc coloring of command output, which is the distinction that explains both how it is built and why it sometimes gets in the way.

The stated requirement is zsh 4.3.11 or newer. That floor is old, and the repository layout suggests the reason: the plugin is a pair of shell scripts at the top level, `zsh-syntax-highlighting.zsh` and `zsh-syntax-highlighting.plugin.zsh`, with no compiled component, no build step, and nothing to link against. Everything else in the tree is documentation, images, tests, or the `highlighters/` directory that does the actual color decisions.

The license is BSD-3-Clause, the default branch is `master`, the repository is not archived, and the last push was on 2026-09-17. It publishes no GitHub releases, which is not an oversight so much as a consequence of the file layout: there is a `changelog.md` and a `release.md`, but the announcement story is handled outside GitHub. Open issues sit at 213, a number worth reading with the topic list in mind rather than as a health signal.

## Sourcing order is the whole installation problem

The first FAQ entry answers the question every user hits: why must `zsh-syntax-highlighting.zsh` be sourced at the end of the `.zshrc` file. The answer is that the plugin hooks into the Zsh Line Editor and computes highlighting for the command-line buffer as it stands at the moment its hook runs. If it registers too early, it cannot see the widgets or hooks installed after it.

In zsh 5.2 and older, the plugin wraps ZLE widgets. It must be sourced after all custom widgets exist, meaning after every `zle -N` call and after `compinit` has run, so that there are widgets left to wrap. The consequence is stated precisely: widgets created after z-sy-h is sourced will still work, but will not update the syntax highlighting. You get a plugin that appears installed and silently fails on the widget you actually use.

In zsh newer than 5.8, excluding 5.8 itself, the approach changes. The plugin uses the `add-zle-hook-widget` facility to install a `zle-line-pre-redraw` hook. Hooks run in order of registration, so z-sy-h must register after anything else that adds hooks which modify the command-line buffer. The ordering rule is the same in both worlds even though the mechanism is different, which makes the one-line summary worth memorizing: source it last.

## What the Makefile installs, and how the test suite runs

The README hands installation to `INSTALL.md` and gives you no commands, which pushes most readers to a package manager. The Makefile at the top of the tree is the other route, and it is where the project's own conventions show. Four variables drive it:

```make
NAME=zsh-syntax-highlighting
INSTALL?=install -c
PREFIX?=/usr/local
SHARE_DIR?=$(DESTDIR)$(PREFIX)/share/$(NAME)
```

`INSTALL` and `PREFIX` are both overridable, and `SHARE_DIR` is derived from `DESTDIR` and `PREFIX`, which means packaging works without patching the Makefile. The install target builds the docs first, then copies `.version` and `zsh-syntax-highlighting.zsh` into the share directory:

```make
install: all
	$(INSTALL) -d $(SHARE_DIR)
	$(INSTALL) -d $(DOC_DIR)
	cp .version zsh-syntax-highlighting.zsh $(SHARE_DIR)
```

Note what is not installed: the plugin file and the version stamp, not the tests and not the images. The rest of the target walks `highlighters/` and its subdirectories, installing every `.zsh` it finds, which is the practical confirmation that highlighters are shipped plugins rather than inline code.

The test target is the part that tells you how seriously this is maintained. It walks each entry in `highlighters/`, and for any directory with a `test-data` folder it runs the highlighting harness in a scrubbed environment:

```make
test:
	@$(ZSH) -fc 'echo ZSH_PATCHLEVEL=$$ZSH_PATCHLEVEL'
	@result=0; \
	for test in highlighters/*; do \
```

Using `env -i` means no inherited locale, no inherited `ZSH` options and no inherited terminal state, and the `ZSH?=zsh` variable at the top of the Makefile is how you point the suite at a specific binary. There is also a `quiet-test` target that is just `$(MAKE) test QUIET=y`, and a `perf` target that walks the same directories. The repository also carries a `.github/` directory and a build status badge, so this is wired into CI rather than run by hand.

## Pluggable highlighters as the customization surface

The README's last section, How to tweak, says syntax highlighting is done by pluggable highlighter scripts and points at the documentation on highlighters for details and configuration settings. That is the whole customization story on the front page, and the `highlighters/` directory plus `docs/` in the tree are where it lives. The Makefile's `all` target concatenates `docs/highlighters.md` and every individual highlighter document into a single `docs/all.md`, which tells you the highlighters are individually documented rather than lumped into one reference.

For most users this split is invisible, because the defaults are good enough that nobody opens the directory. For the users who do open it, the plugin layout is the point: a highlighter is a separate `.zsh` file that gets loaded in its own subdirectory, tested by its own `test-data` fixtures, and reinstalled by a loop in the Makefile. Adding or removing one does not require touching the main script.

That structure is also why the open issue count is what it is. With one shared plugin file, a broken highlighter can be fixed without a release cycle for every other highlighter, which is the tradeoff the architecture buys. The cost is that the default color scheme is a set of independent decisions rather than one opinion, so a mismatched result usually traces to a single highlighter rather than to the plugin.

## Incremental history search is where the zsh floor bites

The second FAQ entry is the honest limitation. Highlighting during incremental history search, bound to Ctrl+R in zsh's emacs keymap, requires zsh 5.4 or newer. Under older versions, zsh's own default underlining of the matched portion of the buffer remains available, but the plugin's additional highlighting is not, because those versions do not expose enough information to compute it correctly. The README links issues 288 and 415 for the detail.

This is a good example of a version floor being documented as a real behavior difference rather than as a warning label. A user on an older distribution zsh is not missing a feature by accident; they are below the point where the line editor can support it. Note that the floor for this feature (5.4) is higher than the floor for the plugin as a whole (4.3.11), which is the sort of detail that usually goes undocumented.

There is a related naming trap worth flagging. zsh has its own character highlighting facility, linked from the README as the source of the default underlining behavior, and it is a different mechanism from this plugin's. When you search for this project you will find both that facility and a separate project called fast-syntax-highlighting, which is a separate implementation rather than a mode of this one. The README here makes no comparison between them and does not claim any speed advantage, so treat any such comparison you read elsewhere as somebody else's claim.

## Watching for updates without a notification channel

The third FAQ entry is about release announcements, and the answer starts with a negative: there is currently no push announcements channel. Two substitutes are named. The first is GitHub's RSS feed of releases at the releases atom URL. The second is an anitya entry at release-monitoring.org for project 7552, which is the kind of release monitor that can also send email or appear on a status page.

For a plugin with no versioned releases on GitHub, that is a reasonable arrangement, and it explains a detail in the Makefile. The install target writes a `.revision-hash` file into the share directory, preferring `git rev-parse HEAD` when it is running inside a work tree and falling back to the checked-in `.revision-hash` file otherwise. A tree with a `.version` file and a `.revision-hash` file is a project that expects to be installed by more than one packaging method and needs to answer the question of which version landed on disk.

That matters for the terminal case in practice. The searches people run for this project skew toward platform package managers, which means many installs come from a distribution repository rather than from GitHub. A packaged install can therefore lag the plugin, and the absence of a push channel means you have to check the atom feed or the anitya entry yourself. Nothing in the README automates that check.

## Conclusion

The plugin does one small thing and does it in the way the fish shell does, which is a fair standard to hold it to. What it gets right is honesty about itself: the README explains the mechanism, names the two zsh versions where the mechanism changes, and links the two issues behind the incremental search limitation instead of hiding it. What it does not give you is an install command, because the README defers that to INSTALL.md and most people arrive from a package manager or an oh-my-zsh plugin list rather than from this page. For a working setup, source `zsh-syntax-highlighting.zsh` after `compinit` and after every `zle -N` call in your `.zshrc`, then check the colors on a deliberately broken quote rather than assuming they appeared.

## FAQ

### How can I use syntax highlighting in Zsh?

Load `zsh-syntax-highlighting.zsh` at the end of your `.zshrc`, after `compinit` and after every `zle -N` call, because the plugin hooks the Zsh line editor and can only wrap widgets that already exist. The plugin then colors the command-line buffer as you type it at the prompt, which makes catching syntax errors before you run the command possible.

### What does syntax highlighting do?

In this plugin it colors the command-line buffer while you type it in an interactive terminal, so you can review a command before running it. The README frames that as helping with catching syntax errors in particular, rather than as decoration of command output.

### What is the difference between fast syntax highlighting and Zsh syntax highlighting?

fast-syntax-highlighting is a separate project, not a mode of this one, and this README makes no performance comparison between the two. What this project does document is its own mechanism: it wraps ZLE widgets on zsh 5.2 and older, and installs a `zle-line-pre-redraw` hook via `add-zle-hook-widget` on zsh newer than 5.8.

## Sources

- [Issues](https://github.com/zsh-users/zsh-syntax-highlighting/issues)
- [License: BSD-3-Clause](https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/LICENSE)
- [README](https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/README.md)
- [zsh-users/zsh-syntax-highlighting on GitHub](https://github.com/zsh-users/zsh-syntax-highlighting)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zsh-users-zsh-syntax-highlighting
