Skip to content

feat: RAC SideNav - #10404

Open
snowystinger wants to merge 3 commits into
mainfrom
rac-sidenav
Open

feat: RAC SideNav#10404
snowystinger wants to merge 3 commits into
mainfrom
rac-sidenav

Conversation

@snowystinger

@snowystinger snowystinger commented Jul 31, 2026

Copy link
Copy Markdown
Member

Closes

Adds the RAC implementation of SideNav. Rewrites S2 to use that. Adds starter examples and docs page for new RAC.

Switches Docs SideNav over to our S2 SideNav.

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

🧢 Your Project:

@rspbot

rspbot commented Jul 31, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jul 31, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jul 31, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jul 31, 2026

Copy link
Copy Markdown
## API Changes

react-aria-components

/react-aria-components:SideNav

+SideNav <T> {
+  aria-describedby?: string
+  aria-details?: string
+  aria-label?: string
+  aria-labelledby?: string
+  autoFocus?: boolean | FocusStrategy
+  children?: ReactNode | (T) => ReactNode
+  className?: ClassNameOrFunction<SideNavRenderProps> = 'react-aria-SideNav'
+  defaultExpandedKeys?: Iterable<Key>
+  dependencies?: ReadonlyArray<any>
+  disabledKeys?: Iterable<Key>
+  expandedKeys?: Iterable<Key>
+  id?: string
+  items?: Iterable<T>
+  onExpandedChange?: (Set<Key>) => any
+  render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, SideNavRenderProps>
+  renderEmptyState?: (TreeEmptyStateRenderProps) => ReactNode
+  selectedRoute?: string | null
+  slot?: string | null
+  style?: StyleOrFunction<SideNavRenderProps>
+}

/react-aria-components:SideNavItem

+SideNavItem <T extends {}> {
+  aria-label?: string
+  children: ReactNode
+  className?: ClassNameOrFunction<SideNavItemRenderProps> = 'react-aria-SideNavItem'
+  download?: boolean | string
+  hasChildItems?: boolean
+  href?: Href
+  hrefLang?: string
+  id?: Key
+  isDisabled?: boolean
+  onClick?: (MouseEvent<FocusableElement>) => void
+  onHoverChange?: (boolean) => void
+  onHoverEnd?: (HoverEvent) => void
+  onHoverStart?: (HoverEvent) => void
+  onPress?: (PressEvent) => void
+  onPressChange?: (boolean) => void
+  onPressEnd?: (PressEvent) => void
+  onPressStart?: (PressEvent) => void
+  onPressUp?: (PressEvent) => void
+  ping?: string
+  referrerPolicy?: HTMLAttributeReferrerPolicy
+  rel?: string
+  render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, SideNavItemRenderProps>
+  routerOptions?: RouterOptions
+  style?: StyleOrFunction<SideNavItemRenderProps>
+  target?: HTMLAttributeAnchorTarget
+  textValue: string
+}

/react-aria-components:SideNavItemContent

+SideNavItemContent {
+  children: ChildrenOrFunction<SideNavItemContentRenderProps>
+}

/react-aria-components:SideNavSection

+SideNavSection <T extends {}> {
+  aria-label?: string
+  children?: ReactNode | (T) => ReactElement
+  className?: string
+  dependencies?: ReadonlyArray<any>
+  id?: Key
+  items?: Iterable<T>
+  render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, undefined>
+  style?: CSSProperties
+}

/react-aria-components:SideNavHeader

+SideNavHeader {
+  children?: ReactNode
+  className?: string
+  id?: string
+  render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, undefined>
+  style?: CSSProperties
+}

/react-aria-components:SideNavContext

+SideNavContext {
+  UNTYPED
+}

/react-aria-components:SideNavProps

+SideNavProps <T> {
+  aria-describedby?: string
+  aria-details?: string
+  aria-label?: string
+  aria-labelledby?: string
+  autoFocus?: boolean | FocusStrategy
+  children?: ReactNode | (T) => ReactNode
+  className?: ClassNameOrFunction<SideNavRenderProps> = 'react-aria-SideNav'
+  defaultExpandedKeys?: Iterable<Key>
+  dependencies?: ReadonlyArray<any>
+  disabledKeys?: Iterable<Key>
+  expandedKeys?: Iterable<Key>
+  id?: string
+  items?: Iterable<T>
+  onExpandedChange?: (Set<Key>) => any
+  render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, SideNavRenderProps>
+  renderEmptyState?: (TreeEmptyStateRenderProps) => ReactNode
+  selectedRoute?: string | null
+  slot?: string | null
+  style?: StyleOrFunction<SideNavRenderProps>
+}

/react-aria-components:SideNavRenderProps

+SideNavRenderProps {
+  isEmpty: boolean
+  isFocusVisible: boolean
+  isFocused: boolean
+  state: TreeState<unknown>
+}

/react-aria-components:SideNavItemProps

+SideNavItemProps <T = {}> {
+  aria-label?: string
+  children: ReactNode
+  className?: ClassNameOrFunction<SideNavItemRenderProps> = 'react-aria-SideNavItem'
+  download?: boolean | string
+  hasChildItems?: boolean
+  href?: Href
+  hrefLang?: string
+  id?: Key
+  isDisabled?: boolean
+  onClick?: (MouseEvent<FocusableElement>) => void
+  onHoverChange?: (boolean) => void
+  onHoverEnd?: (HoverEvent) => void
+  onHoverStart?: (HoverEvent) => void
+  onPress?: (PressEvent) => void
+  onPressChange?: (boolean) => void
+  onPressEnd?: (PressEvent) => void
+  onPressStart?: (PressEvent) => void
+  onPressUp?: (PressEvent) => void
+  ping?: string
+  referrerPolicy?: HTMLAttributeReferrerPolicy
+  rel?: string
+  render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, SideNavItemRenderProps>
+  routerOptions?: RouterOptions
+  style?: StyleOrFunction<SideNavItemRenderProps>
+  target?: HTMLAttributeAnchorTarget
+  textValue: string
+}

/react-aria-components:SideNavItemRenderProps

+SideNavItemRenderProps {
+  allowsDragging?: boolean
+  hasChildItems: boolean
+  id: Key
+  isCurrent: boolean
+  isCurrentAncestor: boolean
+  isDisabled: boolean
+  isDragging?: boolean
+  isDropTarget?: boolean
+  isExpanded: boolean
+  isFocusVisible: boolean
+  isFocusVisibleWithin: boolean
+  isFocused: boolean
+  isHovered: boolean
+  isPressed: boolean
+  isSelected: boolean
+  level: number
+  selectionBehavior: SelectionBehavior
+  selectionMode: SelectionMode
+  state: TreeState<unknown>
+}

/react-aria-components:SideNavItemContentProps

+SideNavItemContentProps {
+  children: ChildrenOrFunction<SideNavItemContentRenderProps>
+}

/react-aria-components:SideNavItemContentRenderProps

+SideNavItemContentRenderProps {
+  allowsDragging?: boolean
+  hasChildItems: boolean
+  id: Key
+  isCurrent: boolean
+  isCurrentAncestor: boolean
+  isDisabled: boolean
+  isDragging?: boolean
+  isDropTarget?: boolean
+  isExpanded: boolean
+  isFocusVisible: boolean
+  isFocusVisibleWithin: boolean
+  isFocused: boolean
+  isHovered: boolean
+  isPressed: boolean
+  isSelected: boolean
+  level: number
+  selectionBehavior: SelectionBehavior
+  selectionMode: SelectionMode
+  state: TreeState<unknown>
+}

/react-aria-components:SideNavSectionProps

+SideNavSectionProps <T> {
+  aria-label?: string
+  children?: ReactNode | (T) => ReactElement
+  className?: string
+  dependencies?: ReadonlyArray<any>
+  id?: Key
+  items?: Iterable<T>
+  render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, undefined>
+  style?: CSSProperties
+}

/react-aria-components:SideNavHeaderProps

+SideNavHeaderProps {
+  children?: ReactNode
+  className?: string
+  id?: string
+  render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, undefined>
+  style?: CSSProperties
+}

@react-spectrum/s2

/@react-spectrum/s2:SideNav

 SideNav <T> {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean | FocusStrategy
   children?: ReactNode | (T) => ReactNode
   defaultExpandedKeys?: Iterable<Key>
   dependencies?: ReadonlyArray<any>
   disabledKeys?: Iterable<Key>
   expandedKeys?: Iterable<Key>
   id?: string
   items?: Iterable<T>
   onExpandedChange?: (Set<Key>) => any
+  renderEmptyState?: (TreeEmptyStateRenderProps) => ReactNode
   selectedRoute?: string | null
   slot?: string | null
   styles?: StylesPropWithHeight
 }

/@react-spectrum/s2:SideNavProps

 SideNavProps <T> {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean | FocusStrategy
   children?: ReactNode | (T) => ReactNode
   defaultExpandedKeys?: Iterable<Key>
   dependencies?: ReadonlyArray<any>
   disabledKeys?: Iterable<Key>
   expandedKeys?: Iterable<Key>
   id?: string
   items?: Iterable<T>
   onExpandedChange?: (Set<Key>) => any
+  renderEmptyState?: (TreeEmptyStateRenderProps) => ReactNode
   selectedRoute?: string | null
   slot?: string | null
   styles?: StylesPropWithHeight
 }

@rspbot

rspbot commented Jul 31, 2026

Copy link
Copy Markdown

Agent Skills Changes

Added (2)
Modified (8)
Install

React Spectrum S2:

npx skills add https://d1pzu54gtk2aed.cloudfront.net/pr/2ec920868992fffe6668670005f6eae9e12d36e0/

React Aria:

npx skills add https://d5iwopk28bdhl.cloudfront.net/pr/2ec920868992fffe6668670005f6eae9e12d36e0/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants