Rail Nav
navdeep-singh/rail-navFreeComponent
Motion rail navigation with the original line-to-link reveal, controlled expansion, flexible links, and optional scroll tracking.
Install it
npx shadcn@latest add https://ui.navdeepsingh.dev/r/rail-nav.jsonSource
1'use client'23import * as React from 'react'4import { useControllableState } from '@radix-ui/react-use-controllable-state'5import { motion, useReducedMotion } from 'motion/react'67import { cn } from '@/lib/utils'89export type RailNavItem = {10 label: React.ReactNode11 href: string12 /** Optional selector to observe when it differs from href. */13 target?: string14 className?: string15 onClick?: React.MouseEventHandler<HTMLAnchorElement>16}1718/** @deprecated Use RailNavItem instead. */19export type RailNavItems = RailNavItem2021export type RailNavProps = Omit<React.ComponentProps<'aside'>, 'defaultValue'> & {22 items: readonly RailNavItem[]23 value?: string24 defaultValue?: string25 onValueChange?: (value: string) => void26 expanded?: boolean27 defaultExpanded?: boolean28 onExpandedChange?: (expanded: boolean) => void29 trackActive?: boolean30 observerOptions?: IntersectionObserverInit31 preventNavigation?: boolean32 label?: string33}3435export function RailNav({36 items,37 value: valueProp,38 defaultValue,39 onValueChange,40 expanded: expandedProp,41 defaultExpanded = false,42 onExpandedChange,43 trackActive = true,44 observerOptions,45 preventNavigation = false,46 label = 'On this page',47 className,48 onMouseEnter,49 onMouseLeave,50 onFocusCapture,51 onBlurCapture,52 ...props53}: RailNavProps) {54 const shouldReduceMotion = useReducedMotion()55 const [activeValue = '', setActiveValue] = useControllableState({56 prop: valueProp,57 defaultProp: defaultValue ?? items[0]?.href ?? '',58 onChange: onValueChange,59 })6061 const [expanded = false, setExpanded] = useControllableState({62 prop: expandedProp,63 defaultProp: defaultExpanded,64 onChange: onExpandedChange,65 })6667 const [hoveredValue, setHoveredValue] = React.useState<string | null>(null)6869 React.useEffect(() => {70 if (!trackActive || typeof IntersectionObserver === 'undefined') {71 return72 }7374 const observedItems = items.flatMap((item) => {75 const selector = item.target ?? (item.href.startsWith('#') ? item.href : null)7677 if (!selector) {78 return []79 }8081 try {82 const element = document.querySelector(selector)8384 return element85 ? [86 {87 element,88 value: item.href,89 },90 ]91 : []92 } catch {93 return []94 }95 })9697 if (!observedItems.length) {98 return99 }100101// … truncated
What it pulls in
npm packages
Files it writes
More from navdeep-singh
All 64 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Groupanimated-group | navdeep-singh | Components | Free | 1 dep | |
| Animated Numbersanimated-numbers | navdeep-singh | Components | Free | 1 dep | |
| Animated Tabsanimated-tabs | navdeep-singh | Components | Free | 2 deps | |
| Approval Panel Illustrationapproval-panel | navdeep-singh | Components | Free | 2 deps · 2 files | |
| Brand Logobrand-logo | navdeep-singh | Components | Free | 1 dep | |
| Contribution Graphcontribution-graph | navdeep-singh | Components | Free | no deps | |
| Designed for Focus Illustrationdesigned-for-focus-illustration | navdeep-singh | Components | Free | 1 dep | |
| Expandable Cardexpandable-card | navdeep-singh | Components | Free | 4 deps |
