# How Plane Page Hierarchies Support Nested Documents and AI Capabilities

> Discover how Plane's page hierarchies enable nested documents and AI capabilities. Learn about its Django model, REST endpoints, and AI integration for context-aware menus. Explore Plane today!

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: deep-dive
- Published: 2026-06-22

---

**Plane implements page hierarchies through a self-referencing Django model with cascade deletion, exposes nested document operations via REST endpoints filtering by parent UUID, and integrates AI capabilities through an optional `aiHandler` prop in the `PageRenderer` component that renders context-aware menus.**

Plane (makeplane/plane) is an open-source project management platform that treats pages as first-class hierarchical documents. The architecture supports arbitrary-depth nesting through database-level parent-child relationships while enabling AI-powered editing features through a flexible handler interface in the frontend editor.

## Database Architecture for Page Hierarchies

The hierarchical structure in Plane begins at the data layer with a self-referencing foreign key that establishes tree-like relationships between documents.

### The Self-Referencing Page Model

In [`apps/api/plane/db/models/page.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/db/models/page.py), the `Page` class defines the hierarchy through a `parent` field pointing to itself:

```python
class Page(BaseModel):
    …
    parent = models.ForeignKey(
        "self",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="child_page",
    )
    …

```

- **Self-reference**: The `parent` field creates a recursive relationship where `null` values represent root-level documents.
- **Cascade deletion**: Setting `on_delete=models.CASCADE` ensures that deleting a parent page automatically removes all descendants.
- **Reverse query**: The `related_name="child_page"` allows efficient fetching of immediate children via `page.child_page.all()`.

The model also includes fields like `archived_at`, `is_locked`, and `view_props` that persist alongside the hierarchy metadata.

## API Endpoints for Managing Nested Documents

The backend exposes hierarchical operations through the `PageViewSet` in [`apps/api/plane/app/views/page/base.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/views/page/base.py), which handles CRUD operations while respecting parent-child relationships.

### Creating and Moving Pages

The `PageSerializer` in [`apps/api/plane/app/serializers/page.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/serializers/page.py) exposes the `parent` field for write operations:

```python
class PageSerializer(BaseSerializer):
    parent = serializers.PrimaryKeyRelatedField(
        queryset=Page.objects.all(),
        required=False,
        allow_null=True,
    )
    …

```

To create a nested document, POST to `/api/pages/` with a parent reference:

```bash
curl -X POST https://plane.example.com/api/pages/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"name": "Sub-section", "parent": "e3b0c442-98fc-1c14-9af4-7f5c1b2d6d0a"}'

```

To move a page within the hierarchy, PATCH the same endpoint with a new `parent` UUID.

### Fetching Subtrees

The `list` method in `PageViewSet` supports filtering by parent to retrieve specific tree branches:

```python
def list(self, request, *args, **kwargs):
    parent_id = request.query_params.get("parent")
    queryset = self.get_queryset()
    if parent_id:
        queryset = queryset.filter(parent_id=parent_id)
    …

```

Requesting `GET /api/pages/?parent=<uuid>` returns only direct children of the specified page, allowing client-side recursion to build complete tree structures. All endpoints enforce authorization through the permission classes defined in [`apps/api/plane/app/permissions/page.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/permissions/page.py).

## Frontend Rendering of Hierarchical Pages

The frontend renders individual pages through the `PageRenderer` component, which treats hierarchy as a data-layer concern while focusing on document editing capabilities.

### The PageRenderer Component

Located in [`packages/editor/src/core/components/editors/document/page-renderer.tsx`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/components/editors/document/page-renderer.tsx), this component receives an optional `aiHandler` prop alongside the editor instance:

```tsx
export function PageRenderer(props: Props) {
  const { editor, titleEditor, aiHandler, … } = props;
  …
  return (
    <div className={cn("frame-renderer w-full flex-grow", { "wide-layout": displayConfig.wideLayout })}>
      {titleEditor && <EditorContainer …>{/* title editor */}</EditorContainer>}

      <EditorContainer …>
        <EditorContentWrapper editor={editor} … />
        {editor.isEditable && !isTouchDevice && (
          <>
            {bubbleMenuEnabled && <EditorBubbleMenu … />}
            <BlockMenu … />
          </>
        )}
      </EditorContainer>
    </div>
  );
}

```

The component does not directly render nested children; instead, sidebar tree components handle hierarchy visualization by fetching data via the parent-filtered API endpoints described above.

## AI Capabilities Integration in Plane

Plane integrates artificial intelligence through a modular handler system that connects document content with AI services while maintaining awareness of page hierarchies.

### The TAIHandler Interface

The contract for AI functionality is defined in [`packages/editor/src/core/types/ai.ts`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/types/ai.ts):

```ts
export type TAIHandler = {
  /** Optional render function for the AI dropdown */
  menu?: (props: TAIMenuProps) => React.ReactNode;
};

```

### Rendering AI Menus

The `AIFeaturesMenu` component in [`packages/editor/src/core/components/menus/ai-menu.tsx`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/components/menus/ai-menu.tsx) renders when users interact with the AI handle element:

```tsx
export function AIFeaturesMenu({ menu }: Props) {
  const [isPopupVisible, setIsPopupVisible] = useState(false);
  const menuRef = useRef<HTMLDivElement>(null);
  …
  return (
    <div className={cn("pointer-events-none fixed inset-0 …", { "pointer-events-auto opacity-100": isPopupVisible })}>
      <div ref={menuRef} className="z-10">
        {menu?.({ isOpen: isPopupVisible, onClose: hidePopup })}
      </div>
    </div>
  );
}

```

When supplied to `PageRenderer`, the `aiHandler` enables the AI menu to access the current page ID, allowing backend services to traverse the parent chain for context-aware suggestions:

```tsx
const aiHandler: TAIHandler = {
  menu: ({ isOpen, onClose }) => (
    <MyAIDropdown
      open={isOpen}
      onClose={onClose}
      pageId={currentPage.id}
    />
  ),
};

<PageRenderer
  editor={editor}
  titleEditor={titleEditor}
  aiHandler={aiHandler}
  …
/>

```

## Practical Implementation Example

Combining these layers enables a complete workflow for hierarchical documents with AI assistance:

1. **Create root**: `POST /api/pages/ {"name":"Project Specs"}` returns ID `R1`
2. **Add child**: `POST /api/pages/ {"name":"API Details","parent":"R1"}` returns ID `C1`
3. **Fetch branch**: `GET /api/pages/?parent=R1` returns the child array
4. **Render with AI**: Supply `aiHandler` to `PageRenderer` so that when users click the AI handle, the dropdown receives `pageId: C1` and can fetch parent context for intelligent suggestions

## Summary

- **Database layer**: [`apps/api/plane/db/models/page.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/db/models/page.py) implements hierarchy via a self-referencing `parent` ForeignKey with `child_page` reverse relation and cascade deletion.
- **API layer**: [`apps/api/plane/app/views/page/base.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/views/page/base.py) supports creating, moving, and filtering pages by parent UUID, while [`apps/api/plane/app/serializers/page.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/app/serializers/page.py) handles validation.
- **Frontend layer**: [`packages/editor/src/core/components/editors/document/page-renderer.tsx`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/components/editors/document/page-renderer.tsx) renders document content and accepts an optional `aiHandler` prop.
- **AI integration**: [`packages/editor/src/core/components/menus/ai-menu.tsx`](https://github.com/makeplane/plane/blob/main/packages/editor/src/core/components/menus/ai-menu.tsx) renders AI interfaces that can access page identifiers and traverse hierarchies for contextual assistance.

## Frequently Asked Questions

### How does Plane handle recursive deletion of nested pages?

When a parent page is deleted, Django's `on_delete=models.CASCADE` configured in [`apps/api/plane/db/models/page.py`](https://github.com/makeplane/plane/blob/main/apps/api/plane/db/models/page.py) automatically removes all descendant pages. This ensures data consistency without requiring manual cleanup of nested documents.

### Can AI features access parent page context for generating suggestions?

Yes. The `aiHandler` prop receives the current page identifier, allowing implementations to fetch the page's parent chain via the API. AI services can traverse the hierarchy using the `parent` field to build rich context for content generation or summarization.

### How do you move a page to a different parent in the hierarchy?

Send a PATCH request to `/api/pages/<id>/` with a new `parent` UUID in the payload. The `PageSerializer` validates the reference and updates the relationship, effectively moving the page and its descendants within the tree structure.

### Is there a limit to nesting depth in Plane?

The data model itself imposes no artificial depth limit; pages can nest arbitrarily deep through the self-referencing `parent` field. Practical limits depend on API performance when recursively fetching deep trees and frontend rendering capabilities for heavily nested sidebar structures.