How the Astryx SideNav Component Handles Collapsible Navigation

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, 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 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) 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 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) 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

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

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

<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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →