In Nix, a package's closure encapsulates all of the packages required to build or run it (as well as the packages required to build those packages, and so on). A package can have zero packages in its closure, because no other packages are required to build it, or it can have many. Whenever you build a package, Nix always realises its entire closure in a sandboxed environment.
Nix closures come in two different types:
To give an example, let's say that you're using Nix to build Firefox because you want to install and run it on your machine. The build-time closure for Firefox would include GCC because you can't build Firefox without it. The runtime closure, however, would not include GCC—Firefox has already been built!—but it would include GTK, which Firefox needs for its user interface.
When you see mentions of Nix closures in Nix documentation and other places, runtime closures are usually what's meant.
If you include things that a package doesn't need in its closure, you end up with a waste of resources—time, disk space, and network bandwidth—known as closure bloat. Nix always realises whatever you tell it to and doesn't have any built-in mechanisms to prevent closure bloat. The solution to closure bloat is vigilance—you should always vet the derivations you create to ensure that every package you include in a derivation is truly required to build or run the desired package.