Skip to content
DocsSupportPricing
Roadmap (has updates coming soon)XFigma3.6K
Sign inGet All-Access
Overview
  • Introduction
  • Get Started
  • License Setup
  • Styling
  • Registry
  • MCP Server
  • Embed
  • Agent Skills
  • llms.txt
  • RTL
  • Changelog
  • YouTube
MCP Server
  • Claude
  • CodexCodexCodex
  • Cursor
  • Grok
  • Conductor
  • v0
  • Lovable
  • Replit
  • Bolt
  • OpenCode
  • VS Code
  • GitHub Copilot
  • Kilo Code
  • Zed
  • Antigravity
  • WSWindsurf
  • CLCline
  • Gemini CLI
  • AMAmp
  • JBJetBrains Junie
Components
  • Alert
  • Autocomplete
  • Badge
  • CascaderCascader can now search the whole tree from any level
  • Code Block
  • Data GridData Grid now scrolls in RTL without an extra provider
  • Date Selector
  • Event Calendar
  • File Upload
  • Filters
  • Frame
  • Gantt
  • Icon Stack
  • Icon Tile
  • Kanban
  • Number Field
  • Phone Input
  • Rating
  • Scrollspy
  • Signature PadNew Signature Pad docs
  • Sortable
  • Stepper
  • Time PickerNew Time Picker docs
  • Timeline
  • Tree

Application

  • App Shell
  • Auth
  • Card
  • ChartAdded 17 Chart blocks for cron jobs, deploys, firewall traffic and SLOs
  • Dashboard
  • Dialog
  • Empty State
  • Event Calendar
  • Flow
  • Form
  • Gantt
  • Kanban Board
  • List
  • Navbar
  • Onboarding
  • Profile
  • Rich Text EditorNew Rich Text Editor category with 5 Tiptap editor blocks
  • Schedule
  • Settings
  • SheetAdded 13 Sheet blocks for cron jobs, deploys, firewall rules, SLOs and data
  • Stats
  • Timeline
  • WhiteboardNew Whiteboard category with 2 Excalidraw board blocks
  • Wizard

Solutions

  • Dev OpsNew Dev Ops category with 6 platform console blocks
  • AI Ops
  • CRMAdded 2 CRM blocks: a forecast call workspace and a deal desk
  • Agents
  • Analytics
  • Billing
  • Bookings
  • Files
  • Inventory
  • Users

AI & Agents

  • AI ChatAdded a corner chat popup block
  • Agent Activity

Templates

  • E-commerce
  • SaaS
  • Dashboard
  • Landing
  • All templates

eCommerce

  • Category Card
  • Checkout
  • Comparison
  • Coupon
  • Filter Sidebar
  • Product Card
  • Product Detail
  • Product Grid
  • Receipt
  • Review
  • Shopping Cart
  • Wishlist
  • Shop Hero

Data Grid

  • Base
  • Columns
  • Drag & Drop
  • EditingAdded a spreadsheet reorder plan Data Grid block
  • Expansion
  • Filtering
  • Grouping
  • Virtualization

Marketing

  • Blog
  • Compare
  • Contact
  • CTA
  • FAQ
  • Hero
  • How It Works
  • PricingAdded 7 Pricing blocks: plan cards, comparison tables and upgrade dialogs

Resources

  • Components
  • Blocks
  • Icons
  • MCP for Agents
  • Docs
  • Support
  • Pricing
  • Tailwind Plus Alternative
  • Roadmap(has updates coming soon)
  • AffiliateSoon

Legal

  • Privacy Policy
  • Terms & Conditions
  • License
  • Refunds
  • Cookies

© 2026 ReUI. All rights reserved.

3.6K

Shadcn Scrollspy

PreviousNext

Custom Shadcn Scrollspy for React and Tailwind CSS. Dynamically highlights navigation to indicate current visible section in viewport during page scroll

Base UIRadix UI

Installation

pnpm dlx shadcn@latest add @reui/scrollspy

Usage

import { Scrollspy } from "@/components/reui/scrollspy"
<Scrollspy targetRef={







Shadcn Scrollspy Free Components

Browse 2 production-ready Shadcn Scrollspy components for dashboards, forms, and product UI. These examples use Base UI primitives from @base-ui/react and stay fully compatible with Shadcn Create so radius, color, and typography match your configured theme.

Browse all 2 Shadcn Scrollspy components for copy-ready layouts, dashboards, and forms built with Tailwind CSS in the ReUI library.

RatingSignature Pad

On This Page

InstallationUsageExamplesHorizontalAPI ReferenceScrollspyData Attributes
containerRef
}
>
<a href="#section-1" data-scrollspy-anchor="section-1">Section 1</a>
<a href="#section-2" data-scrollspy-anchor="section-2">Section 2</a>
</Scrollspy>
<div ref={containerRef}>
<div id="section-1">Content 1</div>
<div id="section-2">Content 2</div>
</div>

Examples

Horizontal

"use client"

import { useRef } from "react"
import { Scrollspy } from "@/components/reui/scrollspy"

import { Button } from "@/components/ui/button"
import { ScrollArea } from "@/components/ui/scroll-area"

export function Pattern() {
  const parentRef = useRef<HTMLDivElement>(null)
  const nav = [
    {
      id: "section-6",
      label: "Section 1",
    },
    {
      id: "section-7",
      label: "Section 2",
    },
    {
      id: "section-8",
      label: "Section 3",
    },
    {
      id: "section-9",
      label: "Section 4",
    },
    {
      id: "section-10",
      label: "Section 5",
    },
  ]

  return (
    <div className="w-full space-y-5">
      <div className="flex w-full gap-2">
        <Scrollspy offset={50} targetRef={parentRef} className="flex gap-2.5">
          {nav.map((item) => (
            <Button
              key={item.id}
              variant="outline"
              data-scrollspy-anchor={item.id}
              className={
                "data-[active=true]:bg-primary data-[active=true]:text-primary-foreground"
              }
            >
              {item.label}
            </Button>
          ))}
        </Scrollspy>
      </div>
      <div className="w-full" ref={parentRef}>
        <ScrollArea className="h-[400px] grow">
          <div className="space-y-8">
            {nav.map((item) => (
              <div key={item.id} id={item.id} className="space-y-2.5">
                <h3 className="text-foreground text-base">{item.label}</h3>
                <div className="bg-muted rounded-2xl h-[350px]"></div>
              </div>
            ))}
          </div>
        </ScrollArea>
      </div>
    </div>
  )
}

API Reference

Scrollspy

The main component that wraps the navigation links and manages the scroll spying logic.

PropTypeDefaultDescription
targetRefRefObject<HTMLElement | Document>windowThe scrollable container to monitor.
onUpdate(id: string) => void-Callback fired when the active section changes.
offsetnumber0Global pixel offset from the top when calculating active sections.
smoothbooleantrueWhether to use smooth scrolling when clicking on anchors.
historybooleantrueWhether to update the URL hash when the active section changes.
dataAttributestring"scrollspy"The prefix for data attributes (e.g., data-scrollspy-anchor).
classNamestring-Additional CSS classes for the wrapper.

Data Attributes

Navigation links within Scrollspy should use these attributes to connect to sections:

AttributeDescription
data-scrollspy-anchorRequired. The ID of the target section (without the #).
data-scrollspy-offsetOptional. Overrides the global offset for this specific link.

The component adds data-active="true" to the link element when its corresponding section is active.

"use client"

import { useRef } from "react"
import { Scrollspy } from "@/components/reui/scrollspy"

import { Button } from "@/components/ui/button"
import { ScrollArea } from "@/components/ui/scroll-area"

export function Pattern() {
  const parentRef = useRef<HTMLDivElement | null>(null)

  const nav = [
    {
      id: "section-1",
      label: "Section 1",
    },
    {
      id: "section-2",
      label: "Section 2",
    },
    {
      id: "section-3",
      label: "Section 3",
    },
    {
      id: "section-4",
      label: "Section 4",
    },
    {
      id: "section-5",
      label: "Section 5",
    },
  ]

  return (
    <div className="flex w-full grow gap-5">
      <div className="flex w-[150px] flex-col gap-2">
        <Scrollspy
          offset={50}
          targetRef={parentRef}
          className="flex flex-col gap-2.5"
        >
          {nav.map((item) => (
            <Button
              key={item.id}
              variant="outline"
              data-scrollspy-anchor={item.id}
              className={
                "data-[active=true]:bg-primary data-[active=true]:text-primary-foreground"
              }
            >
              {item.label}
            </Button>
          ))}
        </Scrollspy>
      </div>
      <div className="grow" ref={parentRef}>
        <ScrollArea className="-me-5 h-[500px] grow pe-5">
          <div className="space-y-8">
            {nav.map((item) => (
              <div key={item.id} id={item.id} className="space-y-2.5">
                <h3 className="text-foreground text-base">{item.label}</h3>
                <div className="bg-muted rounded-2xl h-[350px]"></div>
              </div>
            ))}
          </div>
        </ScrollArea>
      </div>
    </div>
  )
}