# indent-bars draws guide bars over your existing indentation, and its real configuration is a list of trade-offs

> jdtsmith/indent-bars is a fast, configurable Emacs Lisp package that renders indentation guide bars, with optional tree-sitter support for scope focus, depth based colouring, bars on blank lines and a character based fallback for the terminal. It is on ELPA, GPLv3 licensed, and the FAQ is really a catalogue of the cases where bars appear when you did not want them.

**jdtsmith/indent-bars** — Fast, configurable indentation guide-bars for Emacs

- Repository: https://github.com/jdtsmith/indent-bars
- Stars: 592 · Forks: 31
- Language: Emacs Lisp
- License: GPL-3.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/jdtsmith-indent-bars

## A display layer over the indentation you already have

indent-bars is a package for fast, flexible indentation guide bars in Emacs, with optional tree-sitter enhancement. The bars are the vertical lines you see beside indented code, the kind that show you at a glance which block a line belongs to.

The word fast is in the package description and it is the one that matters, because indentation guides are the classic example of a display feature that is cheap to describe and expensive to implement. Drawing a bar per indentation level on every visible line means touching the display code on every redisplay, and a naive implementation turns into the reason a user's Emacs feels sluggish. The package describes itself as optimised for speed, and the design detail that gets you that is that it works from the indentation you already have rather than parsing the code structure again.

That is also the honest boundary of the package. It is a rendering layer over Emacs indentation, so its accuracy is bounded by the accuracy of the indentation, and by the language mode's idea of what a list is. Several of the most common complaints in the FAQ are direct consequences of that, and the documentation is refreshingly candid about them.

The feature list is broader than the basic idea. Bars can appear on blank lines. Bars can be coloured by depth, with a cyclical palette you can customise. Appearance is configurable down to colour, blending, width, position within the character cell, a vertical fill or blank pattern, and even a zigzag style, with examples in the examples.md file. There is fast current-depth highlighting, with configurable colour and appearance changes. Bar depth can be held constant inside multi-line strings and lists. Bars can be skipped in multiply nested list structures. And it works in the terminal, using a vertical bar character.

The project is Emacs Lisp, the licence is GPLv3, and the last push was on 2026-09-04.

## Installing from ELPA and switching it on per mode hook

Installation is through the standard Emacs package system. The package is on ELPA, so it is installed with Emacs' package facilities, and then it is switched on by calling indent-bars-mode in the mode hooks you want it in.

The documented minimal configuration uses use-package:

```elisp
(use-package indent-bars
  :hook ((python-mode yaml-mode) . indent-bars-mode)) ; or whichever modes you prefer
```

That is the whole setup for the default behaviour. The mode hook is the right place for it because indent-bars is a buffer-local visual feature, and you almost certainly want it in the modes where you read code and not in the modes where you read prose.

That is also the first decision to make, and the FAQ suggests it explicitly. The package can be intrusive in a mode you read casually, and the same page answers the opposite complaint by pointing at the blend value, which controls how far the bar colour blends towards the background. Lower the blend towards zero to make the bars less intrusive, raise it towards one to make them more visible. The two complaints have the same answer in reverse, which is a sign the option is doing its job.

The tree-sitter section of the documentation adds a caveat that is worth knowing before you go further. It asks treesitter users for help: if you have good settings for wrap and scope for your favourite languages, add them to the project wiki, and if a large enough collection builds up they may be included as defaults. In other words, the tree-sitter configuration is community contributed, and the package is explicit that it needs your help.

## The list problem, and why braces and parentheses behave differently

The most substantive part of the FAQ is about bars appearing in places you did not expect, and the reason is that indent-bars has to decide what counts as a nested structure, and it uses the language mode's notion of a list to do that.

In a brace language such as C or JavaScript, the mode syntax often interprets braces as list context. If you have indent-bars-no-descend-lists set to t, you will get fewer bars than you expected inside function definitions, calls and multi-line parenthesised expressions. The documented answer is to disable that feature, or to use the other configuration in the bar setup section instead.

In a parenthesis language such as Emacs Lisp or Scheme, the opposite happens. Almost every line of those languages is inside a nested continuing list, so the bars disappear on some lines. The fix there is to disable indent-bars-no-descend-lists, or set it to skip, so that intermediate bars are skipped rather than every list contributing a bar.

The third case is the one that catches people by surprise. If bars are missing on lines with tabs, the likely cause is that indent-tabs-mode is set to nil in a buffer containing a tab indented file, so the visual indentation no longer matches what the mode computes.

The option names themselves encode the behaviour. no-descend-lists means bars do not descend into lists, and the value skip adds the behaviour of skipping intermediate bars. The setting is doing two related jobs, and that is why it has a value that is neither true nor false.

The other placement questions are simpler. To get a bar in the very first column, set indent-bars-starting-column to 0.

## tree-sitter scope focus, configured by hand per language

The tree-sitter integration is the most powerful part of the package and the most demanding, because it lets the bars show structure that indentation alone cannot express.

Scope focus means the bars can be restricted to a set of node types that are meaningful in your language, so a function definition, a class and a control flow statement each produce a bar while incidental grouping does not. The documented configuration looks like this:

```elisp
(use-package indent-bars
  :custom
  (indent-bars-no-descend-lists 'skip) ; prevent extra bars in nested lists + skip intermediate bars
  (indent-bars-treesit-support t)
  (indent-bars-treesit-ignore-blank-lines-types '("module"))
  ;; Add other languages as needed; check the wiki
  (indent-bars-treesit-scope '((python function_definition class_definition for_statement
	  if_statement with_statement while_statement)))
  ;; Note: wrap likely not be needed if no-descend-list is enough
  ;;(indent-bars-treesit-wrap '((python argument_list parameters ; for python, as an example
  ;;				      list list_comprehension
  ;;				      dictionary dictionary_comprehension
  ;;				      parenthesized_expression subscript)))
  :hook ((python-base-mode yaml-mode) . indent-bars-mode))
```

The scope list is per language, and the FAQ gives clear advice on how to build it. A common mistake is adding too many node types, so start small with the things you know you want, such as function, method and class. And if you turned tree-sitter support on and nothing happened, the answer is that you have to configure indent-bars-treesit-scope, and possibly the wrap variable, for the languages you care about.

There is also a separate way to change the style of the out-of-scope bars, through an alternate set of ts- prefixed customisation variables, where by default only the blend differs between the ts and the normal style. And if you want out-of-scope text to keep the default style while in-scope text becomes special, you set indent-bars-ts-styling-scope to in-scope.

That is a lot of configuration surface, and it is the price of the feature. The package treats the tree-sitter setup as a community wiki rather than shipping defaults, which is honest but means your first session with it is a configuration session.

## The terminal path and the character fallback

Not every Emacs display can draw what indent-bars wants, and the package has an honest answer for that.

The first FAQ entry is the one every new user hits. If you do not see anything, or the bars are garbled, the reason is that most but not all Emacsen support stipples, and the documentation points to a compatibility section and to a section on testing stipples. A stipple is a pattern of pixels within a character cell, and it is what allows the package to draw fills, partial vertical strokes and the zigzag patterns. On a display that cannot do it, the result is noise rather than bars.

The fallback is a single character. There is an indent-bars-prefer-character option that switches to old fashioned vertical bar characters, and the FAQ recommends it for anyone who wants a minimal look, along with the example in the examples file that is described as a minimal configuration. The pattern the package supports is that a character based bar is a way of turning off the stipple while keeping the feature.

The terminal case is handled separately and more simply. The FAQ entry for terminal users says that indent-bars will just work, though you do not get any of the fancy bar patterns. So the degradation ladder is: full stipple rendering with patterns, stipple rendering without the extras, and plain character bars in the terminal.

That three step degradation is the practical answer to how portable this is across the many Emacs front ends in use, from a modern graphical build to a terminal over ssh. The package also flags a separate compatibility concern, that indent-bars may conflict with another package, and points to a section of workarounds for that, which is worth reading before you install it into an existing configuration.

## A delay knob for the highlight, and a project with no tags

The current-depth highlight is the feature that most people notice first, and it comes with a knob that exists because of how fast it is.

The complaint in the FAQ is that the current bar highlight is fast but flashes too rapidly during scrolling. The answer is to update to v0.2.2 or later and set indent-bars-depth-update-delay to a comfortable number like 0.1s, with 0.075s as the default, and to set it to 0 if you like the crazy fast updates. So the highlight is throttled by a configurable delay, with a default that is already tuned down from frame rate.

That is a small piece of engineering with a large usability consequence, and it points at the package's overall approach: the default is fast, and every complaint about speed is answered with a variable rather than with a change of default. Blending, depth colouring, the update delay, the starting column, the preferred character, the tree-sitter options. The FAQ is essentially an index of the knobs.

The project structure is correspondingly small. The repository root holds indent-bars.el, the main package, indent-bars-ts.el, the tree-sitter integration kept as a separate file, examples.md with the configuration gallery, NEWS.org as the release news file, and the LICENSE. Two Lisp files and a wiki is a very small surface to read, which is the other reason this package is pleasant to evaluate: you can read the source.

The licence is GPLv3, the repository is not archived, and the last push was on 2026-09-04. There are no GitHub releases, so the version history lives in NEWS.org rather than in tags, and the FAQ points at v0.2.2 as the version where the delay option landed, which means the release notes file, not the repository page, is the place to check what changed and when.

## Conclusion

Use indent-bars if you read deeply nested code in Emacs and want visible structure that follows your indentation rather than a separate guide column. Do not expect it to be zero effort, because the list handling and the tree-sitter scope configuration are where the time goes, and the FAQ is a list of those failure modes. Before enabling it across your modes, turn it on in one major language, read the entries about no-descend-lists and the brace versus paren difference, and check the compatibility notes for the packages you already use, since a package that also draws guides will conflict with it.

## FAQ

### What is the indent-bars Emacs package?

It is a fast, configurable package for indentation guide bars in Emacs, with optional tree-sitter support including scope focus. Bars can appear on blank lines, can be coloured by depth, can be held constant inside multi-line strings and lists, and can be skipped in nested list structures.

### How do I install and enable indent-bars?

It is on ELPA, so install it with Emacs' package facilities, then call indent-bars-mode in the mode hooks where you want it. The documented minimal configuration is a use-package form with a :hook listing the modes, for example python-mode and yaml-mode.

### Why do fewer bars than expected appear in my C or JavaScript code?

Your mode syntax likely interprets braces as list context, and you have indent-bars-no-descend-lists set to t. The documentation says to disable that feature or to use the alternative configuration in the bar setup section.

### Why do the bars disappear on some lines in Emacs Lisp or Scheme?

Almost all lines in parenthesis based languages are inside nested continuing lists. The documented fix is to disable indent-bars-no-descend-lists, or to set it to skip so intermediate bars are skipped.

### Why is nothing drawn in my Emacs?

Not all Emacsen support stipples, and the package needs them to draw the bars. The documentation points to a compatibility section and to a section on testing stipples, and an alternative is the character based mode through indent-bars-prefer-character.

### How do I configure the tree-sitter support in indent-bars?

Set indent-bars-treesit-support and then configure indent-bars-treesit-scope, and possibly the wrap variable, for the languages you care about. The documentation advises starting with a small list of node types, and the project collects per-language settings in its wiki.

## Sources

- [Issues](https://github.com/jdtsmith/indent-bars/issues)
- [jdtsmith/indent-bars on GitHub](https://github.com/jdtsmith/indent-bars)
- [License: GPL-3.0](https://github.com/jdtsmith/indent-bars/blob/main/LICENSE)
- [README](https://github.com/jdtsmith/indent-bars/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/jdtsmith-indent-bars
