# How to Implement Tree Nodes and Collapsible Headers in ImGui

> Learn to implement ImGui tree nodes and collapsible headers using TreeNode and CollapsingHeader APIs. Discover how to create persistent hierarchical UI elements with ease.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: how-to-guide
- Published: 2026-07-15

---

**Dear ImGui provides the `TreeNode()`, `TreeNodeEx()`, and `CollapsingHeader()` APIs that leverage the internal `TreeNodeBehavior()` function to render hierarchical, collapsible UI elements with automatic state persistence.**

The **ocornut/imgui** library offers a robust set of tools for creating hierarchical interfaces. Whether you are building property panels, scene graphs, or settings menus, understanding how to implement **tree nodes and collapsible headers in ImGui** is essential for organizing complex UI layouts. These widgets share a unified architecture that handles input, navigation, and persistence through a minimal set of high-level functions.

## Core Architecture Behind Tree Nodes

### TreeNodeBehavior() – The Internal Engine

At the heart of every tree widget lies **`TreeNodeBehavior()`**, defined in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) (line 3770). This internal function manages input detection, state tracking for open/close transitions, arrow rendering, and navigation integration. It serves as the single implementation point that powers all public tree APIs.

### Public API Wrappers

The user-facing functions **`TreeNode()`** and **`TreeNodeEx()`** (declared in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h), lines 743-750) are thin wrappers that generate unique `ImGuiID` values, process flag bitmasks, and invoke `TreeNodeBehavior()`. When a node returns true indicating it is open, these functions automatically push the ID onto the internal stack, requiring a matching **`TreePop()`** to restore hierarchy.

### CollapsingHeader() as a Convenience Wrapper

**`CollapsingHeader()`** (lines 757-758 in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)) provides a simplified interface for header-style nodes. Unlike `TreeNode()`, it forces the framed style and disables automatic `TreePush()`, meaning it returns the open state without requiring `TreePop()`.

## Configuring Appearance with ImGuiTreeNodeFlags

The **`ImGuiTreeNodeFlags_`** enumeration (lines 1304-1336 in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)) controls visual styling and interaction behavior. Key flags include:

- **`ImGuiTreeNodeFlags_Framed`**: Renders the node with a full background frame
- **`ImGuiTreeNodeFlags_Leaf`**: Removes the expand arrow for terminal nodes
- **`ImGuiTreeNodeFlags_DrawLinesFull`**: Draws connecting lines between hierarchy levels
- **`ImGuiTreeNodeFlags_OpenOnDoubleClick`**: Changes the default single-click to double-click for opening

## Implementing Hierarchical Trees in Practice

When building nested structures, remember that `TreeNode*` calls returning `true` indicate an open state and begin a new scope. You must close this scope with `TreePop()`.

```cpp
// Basic tree structure with nested nodes
if (ImGui::TreeNode("Root"))  // Opens new ID scope
{
    ImGui::Text("Root content");
    
    // Leaf node - no arrow but still requires TreePop()
    if (ImGui::TreeNodeEx("Leaf Item", ImGuiTreeNodeFlags_Leaf))
    {
        ImGui::Text("Leaf content");
        ImGui::TreePop();
    }
    
    // Nested child
    if (ImGui::TreeNode("Child Node"))
    {
        ImGui::Text("Child content");
        ImGui::TreePop();
    }
    
    ImGui::TreePop();  // Close "Root"
}

```

For framed header-style sections that do not affect indentation:

```cpp
// CollapsingHeader requires no TreePop()
if (ImGui::CollapsingHeader("Settings", ImGuiTreeNodeFlags_Framed))
{
    ImGui::Checkbox("Enable feature", &feature_enabled);
    ImGui::SliderFloat("Speed", &speed, 0.0f, 1.0f);
}

```

To render connecting lines between hierarchy levels:

```cpp
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DrawLinesFull |
                           ImGuiTreeNodeFlags_Framed;
if (ImGui::TreeNodeEx("With lines", flags))
{
    ImGui::Text("Child with visual connection");
    ImGui::TreePop();
}

```

## State Management and Navigation

Open/close states persist automatically via internal storage keyed to the widget's `ImGuiID`. The **`ImGuiTreeNodeStackData`** structure (lines 1424-1431 in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)) tracks this state within `ImGuiContext::TreeNodeStack`.

To programmatically control visibility, use **`SetNextItemOpen()`** before the tree node call. The **`GetTreeNodeToLabelSpacing()`** utility (line 756 in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)) provides the standard horizontal offset used for aligning custom content with tree labels.

## Summary

- **`TreeNodeBehavior()`** in [`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h) serves as the single implementation for all tree widgets
- **`TreeNode()`** and **`TreeNodeEx()`** require **`TreePop()`** when they return true to close the scope
- **`CollapsingHeader()`** automatically handles cleanup and returns the open state without requiring `TreePop()`
- Use **`ImGuiTreeNodeFlags`** to customize appearance with frames, bullets, or connecting lines
- State persistence is automatic via internal ID storage, or can be forced with **`SetNextItemOpen()`**

## Frequently Asked Questions

### What is the difference between TreeNode and CollapsingHeader?

`TreeNode()` pushes the current ID onto the stack when open and requires `TreePop()` to close the scope, affecting indentation and hierarchy. `CollapsingHeader()` is a convenience wrapper that forces a framed style and manages the scope internally, returning only the boolean open state without requiring `TreePop()`.

### How do I persist the open/close state of tree nodes?

Dear ImGui automatically persists tree node states using the widget's `ImGuiID` as a key in internal storage. To force a specific state programmatically, call `SetNextItemOpen(true)` or `SetNextItemOpen(false)` immediately before the `TreeNode()` call.

### Why must I call TreePop() after TreeNode()?

When `TreeNode()` returns true, it pushes a new identifier onto the ID stack to isolate child elements. Calling `TreePop()` restores the previous ID scope and indentation level. Failure to call `TreePop()` results in an ID stack mismatch and assertion errors.

### How do I draw connecting lines between tree nodes?

Enable the `ImGuiTreeNodeFlags_DrawLinesFull` or `ImGuiTreeNodeFlags_DrawLinesToNodes` flags when calling `TreeNodeEx()`. These flags trigger the internal line-drawing logic within `TreeNodeBehavior()` to render visual connections between hierarchy levels.