Table of Contents
inferencesh/table-of-contentsFreeBlock
Auto-scrolling table of contents with intersection observer for active section.
Live preview
live · rendered by the registry
Rendered by inferencesh on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.inference.sh/r/table-of-contents.jsonSource
1"use client"23import { useEffect, useState, useCallback } from "react"4import { cn } from "@/lib/utils"5import { TableOfContentsIcon } from "lucide-react"67export interface TocItem {8 id: string9 title: string10 level?: number11}1213interface TableOfContentsProps {14 items: TocItem[]15 className?: string16}1718function useActiveSection(itemIds: string[]) {19 const [activeId, setActiveId] = useState<string>("")2021 useEffect(() => {22 if (itemIds.length === 0) return2324 const observer = new IntersectionObserver(25 (entries) => {26 entries.forEach((entry) => {27 if (entry.isIntersecting) {28 setActiveId(entry.target.id)29 }30 })31 },32 {33 rootMargin: "-80px 0px -80% 0px",34 threshold: 0,35 }36 )3738 itemIds.forEach((id) => {39 const element = document.getElementById(id)40 if (element) {41 observer.observe(element)42 }43 })4445 return () => observer.disconnect()46 }, [itemIds])4748 return activeId49}5051function TableOfContents({ items, className }: TableOfContentsProps) {52 const itemIds = items.map((item) => item.id)53 const activeId = useActiveSection(itemIds)5455 const scrollToSection = useCallback((id: string) => {56 const element = document.getElementById(id)57 if (element) {58 element.scrollIntoView({ behavior: "smooth" })59 }60 }, [])6162 if (items.length === 0) {63 return null64 }6566 return (67 <nav className={cn("relative", className)}>68 <div className="flex items-center gap-2 mb-3">69 <TableOfContentsIcon className="h-4 w-4 text-muted-foreground" />70 <p className="text-xs font-semibold text-muted-foreground tracking-wide">71 on this page72 </p>73 </div>7475 <ul className="space-y-1 border-l border-border">76 {items.map(({ id, title, level = 1 }) => {77 const isActive = activeId === id78 const isChild = level > 17980 return (81 <li key={id}>82 <button83 onClick={() => scrollToSection(id)}84 className={cn(85 "flex items-center w-full text-left text-[13px] py-1 -ml-px border-l transition-colors",86 level === 1 && "pl-3 font-medium",87 level === 2 && "pl-5 text-xs",88 isActive89 ? "border-foreground text-foreground"90 : "border-transparent text-muted-foreground hover:text-foreground hover:border-muted-foreground"91 )}92// … truncated
Files it writes
More from inferencesh
All 14 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Agent Chatagent | inferencesh | Blocks | Free | 1 dep | |
| Chat UI Primitiveschat | inferencesh | Blocks | Free | 1 dep · 15 files | |
| Code Blockcode-block | inferencesh | Blocks | Free | no deps · 7 files | |
| Markdown Renderer (legacy)markdown | inferencesh | Blocks | Free | 2 deps · 2 files | |
| pretext-mdpretext-md | inferencesh | Blocks | Free | 4 deps · 10 files | |
| Sidebar Lightsidebar-light | inferencesh | Blocks | Free | no deps | |
| Stepssteps | inferencesh | Blocks | Free | no deps · 2 files | |
| Tasktask | inferencesh | Blocks | Free | 1 dep · 9 files |
