Skip to content

Why import-tree?

As Nix configurations grow, the imports list becomes a maintenance burden:

# This doesn't scale.
{
imports = [
./modules/networking.nix
./modules/desktop/sway.nix
./modules/desktop/waybar.nix
./modules/services/docker.nix
./modules/services/ssh.nix
./modules/users/alice.nix
# ... dozens more
];
}

Every new file requires updating the import list. Forget one and your config silently ignores it. Reorganize your directory and you must update every path by hand.

{
imports = [ (import-tree ./modules) ];
}

Add a file to ./modules/ and it is automatically discovered. Reorganize freely. Use /_ prefixed directories for helpers that should not be imported.

The Dendritic pattern — where each file is a self-contained module — was the original inspiration for import-tree.

With Dendritic, your configuration becomes a file tree — each concern in its own file, each file a module. import-tree removes the glue code that would otherwise connect them.

import-tree is not just a file loader. Its builder API lets you:

  • Filter which files are selected — by predicate, regex, or both.
  • Transform discovered paths — wrap them in custom modules, read their contents, or anything else.
  • Compose multiple directory trees with shared filters.
  • Extend with domain-specific APIs — let library authors ship curated import-tree instances.

This makes import-tree useful for sharing pre-configured sets of modules across projects. Library authors can ship an import-tree instance with custom filters and API methods, and consumers pick what they need:

# A library could expose:
lib.modules-tree = import-tree.addAPI {
gaming = self: self.filter (lib.hasInfix "+gaming");
minimal = self: self.filterNot (lib.hasInfix "+heavy");
};
# Consumers use it like:
{ imports = [ lib.modules-tree.gaming.minimal ]; }
  • Zero dependencies — a single default.nix, no extra flake inputs
  • Works everywhere — flakes, non-flakes, any module system
  • Composable — builder pattern with filter/map/extend chains
  • Predictable — sensible defaults, clear ignore rules, no magic
Contribute Community Sponsor