# How the Astryx SideNav Component Handles Collapsible Navigation

> Discover how the Astryx SideNav component manages collapsible navigation using props, state, and a collapse context for controlled or uncontrolled modes. Learn more!

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: internals
- Published: 2026-08-06

---

**The Astryx SideNav component handles collapsible navigation through a combination of props, internal React state, and a dedicated collapse context that supports both controlled and uncontrolled modes.**

The `facebook/astryx` repository provides a comprehensive navigation system for React applications, with the **SideNav** component offering sophisticated collapsible behavior. This implementation allows developers to toggle between an expanded navigation list and a compact icon-only toolbar while maintaining flexibility for various application architectures.

## Understanding the Collapsible Prop Configuration

The **SideNav** component accepts a `collapsible` prop that determines whether the sidebar can be collapsed. This prop accepts either a **boolean** or an **object** with advanced configuration options including default state, controlled mode flags, and custom button settings.

In [`packages/core/src/SideNav/SideNav.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/SideNav/SideNav.tsx), the component parses this prop to initialize the collapsible architecture. When `collapsible` is truthy, the component creates a **collapse context** that propagates state through the component tree. The prop structure supports fine-grained control, allowing developers to specify `defaultIsCollapsed` for uncontrolled scenarios or `isCollapsed` with `onCollapsedChange` for controlled implementations.

## State Management Architecture

The SideNav component implements a dual-mode state management system that adapts to your application's needs.

### Uncontrolled Mode

When the `collapsible` object does not contain an `isCollapsed` property, the component manages state internally using the `useState` hook. The local state `uncontrolledCollapsed` initializes from `defaultIsCollapsed` and updates independently through the `toggle` function. This mode suits applications where the sidebar state does not need to synchronize with external UI elements.

### Controlled Mode

When `collapsible.isCollapsed` is supplied, the component treats the state as **controlled**. Changes propagate via `onCollapsedChange`, allowing parent components to manage the collapsed state explicitly. This pattern proves essential when coordinating the SideNav with other layout elements or persisting navigation preferences across sessions.

The `toggle` function in [`SideNav.tsx`](https://github.com/facebook/astryx/blob/main/SideNav.tsx) updates the appropriate state based on the current mode. When the sidebar is also resizable, this function additionally coordinates with the resizable hook to adjust width values during collapse or expand operations.

## The Collapse Context API

The **SideNavCollapseContext** ([`packages/core/src/SideNav/SideNavCollapseContext.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/SideNav/SideNavCollapseContext.ts)) provides the communication layer for collapsible behavior. This React context carries three critical values:

- **`isCollapsed`**: Current boolean state of the sidebar
- **`toggle`**: Function to switch between collapsed and expanded states
- **`isCollapsible`**: Boolean indicating whether collapse functionality is active

The SideNav component provides this context to its subtree when `isCollapsible` evaluates to true (lines 74-80 in the source). Child components like **SideNavCollapseButton** consume this context through the `useSideNavCollapse` hook, enabling deeply nested elements to access and modify the collapse state without prop drilling.

## Imperative Handle for External Control

For scenarios requiring control from outside the SideNav tree, the component exposes an **imperative handle** via the `handleRef` prop. The `useImperativeHandle` hook in [`SideNav.tsx`](https://github.com/facebook/astryx/blob/main/SideNav.tsx) attaches a `SideNavImperativeCollapseHandle` object that returns the current collapse state through `getCollapseState()`.

This mechanism enables buttons rendered outside the SideNav component—such as those in a top navigation bar—to toggle the sidebar programmatically. When `SideNavCollapseButton` receives a `handleRef` prop, it bypasses the context API and instead invokes methods on this external reference.

## SideNavCollapseButton Implementation

The **SideNavCollapseButton** component ([`packages/core/src/SideNav/SideNavCollapseButton.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/SideNav/SideNavCollapseButton.tsx)) serves as the primary interaction point for toggling collapse state. This component reads the collapse state from `SideNavCollapseContext` when rendered inside the SideNav tree, or falls back to the imperative handle when provided externally.

The button automatically hides when the sidebar is not collapsible or when the application operates in mobile mode (detected via `useAppShellMobile`). Clicking the button invokes the `toggle` function, which flips the `isCollapsed` flag and triggers re-rendering throughout the component tree.

Developers can customize the button content by passing children, or rely on the default icon-only presentation that adapts to the current collapse state.

## Visual Rendering and Layout Changes

When collapsed, the SideNav applies specific styling transformations to create a compact toolbar interface. The root element receives `styles.rootCollapsed`, while the scrollable area switches to `styles.scrollableCollapsed`, implementing a centered, icon-only layout.

In default mode, the component renders a full vertical sidebar with sticky containers for the header, top content, scrollable area, and footer. The built-in collapse button appears only when both `isCollapsible` and `hasCollapseButton` evaluate to true. When a resize handle is active, the sidebar width defers to the resizable hook's calculations, though the collapse logic operates independently of width management.

## Implementation Examples

### Basic Collapsible SideNav with Built-in Button

```tsx
import {SideNav, SideNavSection, SideNavItem, SideNavHeading} from '@astryxdesign/core';

<SideNav
  collapsible
  header={<SideNavHeading heading="My App" headingHref="/" />}
  topContent={<Button label="Create" variant="primary" />}
>
  <SideNavSection heading="Main">
    <SideNavItem label="Dashboard" isSelected href="/dashboard" />
    <SideNavItem label="Projects" href="/projects" />
  </SideNavSection>
</SideNav>

```

### Controlled Collapse with External Button

```tsx
import {useRef, useState} from 'react';
import {SideNav, SideNavCollapseButton} from '@astryxdesign/core';

function Layout() {
  const collapseRef = useRef<SideNavImperativeCollapseHandle>(null);
  const [collapsed, setCollapsed] = useState(false);

  return (
    <>
      <TopNav
        endContent={<SideNavCollapseButton handleRef={collapseRef} />}
      />
      <SideNav
        collapsible={{isCollapsed: collapsed, onCollapsedChange: setCollapsed}}
        handleRef={collapseRef}
      >
        {/* navigation items */}
      </SideNav>
    </>
  );
}

```

### Custom Collapse Button Content

```tsx
<SideNav collapsible>
  <SideNavCollapseButton label="Toggle menu">
    <MyCustomIcon />
  </SideNavCollapseButton>
  {/* navigation items */}
</SideNav>

```

## Summary

- The **Astryx SideNav** supports collapsible navigation through the `collapsible` prop, which accepts boolean values or configuration objects for advanced control.
- **State management** operates in two modes: **uncontrolled** (internal `useState`) and **controlled** (external state via `isCollapsed` and `onCollapsedChange`).
- The **SideNavCollapseContext** provides `isCollapsed`, `toggle`, and `isCollapsible` values to child components through React context.
- An **imperative handle** (`handleRef`) enables external components to control collapse state without being nested inside the SideNav tree.
- **SideNavCollapseButton** automatically adapts to context or imperative handles and hides on mobile devices.
- Visual changes apply through `styles.rootCollapsed` and `styles.scrollableCollapsed` when transitioning to the collapsed state.

## Frequently Asked Questions

### How do I make the SideNav collapse by default?

Pass a configuration object to the `collapsible` prop with `defaultIsCollapsed: true`. This initializes the uncontrolled state in the collapsed position. For example: `collapsible={{ defaultIsCollapsed: true }}`.

### Can I control the SideNav collapse state from a top navigation bar?

Yes. Use the `handleRef` prop to obtain an imperative handle, then pass this ref to `SideNavCollapseButton` components rendered outside the SideNav tree. The button will use the imperative `getCollapseState` method rather than the context API.

### What happens to the SideNav on mobile devices?

The `SideNavCollapseButton` automatically hides when the application detects mobile mode through `useAppShellMobile`. In mobile contexts, the SideNav typically renders as a drawer rather than a collapsible sidebar, making the collapse button irrelevant.

### Is the collapse state preserved when the page refreshes?

The component does not automatically persist state to localStorage or sessionStorage. To preserve the collapse state across refreshes, use the **controlled mode** with `isCollapsed` and `onCollapsedChange`, then synchronize the state with your preferred storage mechanism in the parent component.