Skip to content

Conversation

@KariraLakshya
Copy link
Contributor

@KariraLakshya KariraLakshya commented Jan 10, 2026

Description

This PR fixes an issue where the “On this page” Table of Contents (TOC) active section highlight stops working after navigating between documentation pages using client-side routing.

The TOC uses react-scrollspy, which does not automatically reset when the route changes. As a result, it continues to track headings from the previously visited page, causing the active section highlight to break until a full page reload.

What was Changed

  • Added useRouter to detect route changes
  • Keyed the Scrollspy component with router.asPath to force a remount on navigation
  • Ensured headings are re-scanned and scroll listeners are re-initialized after each route change

Why this Works

Changing the key forces React to recreate the Scrollspy component whenever the route changes. This resets its internal state and restores correct active section tracking without introducing custom scroll logic or breaking existing behavior.

Related issue(s)
Fixes #4845

Summary by CodeRabbit

  • Bug Fixes
    • Improved the table of contents and scrollspy so the active section reliably updates when navigating between pages, preventing incorrect highlighting after route changes.

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link

netlify bot commented Jan 10, 2026

Deploy Preview for asyncapi-website ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 2f383c8
🔍 Latest deploy log https://app.netlify.com/projects/asyncapi-website/deploys/69627870bdf06a00085259d5
😎 Deploy Preview https://deploy-preview-4919--asyncapi-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 10, 2026

📝 Walkthrough

Walkthrough

Set up Next.js router inside the TOC component and keyed the Scrollspy instance to router.pathname, forcing remount and reinitialization of scroll listeners on client-side route changes to prevent stale active-section state.

Changes

Cohort / File(s) Summary
Scrollspy State Reset
components/TOC.tsx
Imported useRouter from next/router, initialized const router = useRouter(), and added key={router.pathname} to the Scrollspy component so it remounts on route changes and reinitializes its scroll listeners.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

🐰 I hopped through docs where highlights slept,
Routes danced around and the markers wept.
A tiny key on the Scrollspy's door,
Remounted paths and the highlights soar,
Now every heading wakes — hooray, I leapt! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding client-side routing detection and Scrollspy remounting to restore TOC active section highlighting after navigation.
Linked Issues check ✅ Passed The PR implementation directly addresses all coding objectives from issue #4845: detecting route changes via useRouter, forcing Scrollspy remount by keying to router.pathname, and ensuring correct active section highlighting without full page reloads.
Out of Scope Changes check ✅ Passed All changes in the PR are directly scoped to the linked issue #4845. The modifications only add necessary imports and component keying to TOC.tsx to fix the reported Scrollspy highlighting bug.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16da278 and 2f383c8.

📒 Files selected for processing (1)
  • components/TOC.tsx
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:38:35.745Z
Learning: In Next.js, nested `<a>` tags cause hydration issues due to invalid HTML. To fix this, avoid nesting `<a>` tags by replacing inner `<a>` tags with non-interactive elements like `<span>`, and use click handlers to maintain interactivity if needed.
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.
📚 Learning: 2025-12-29T14:21:43.887Z
Learnt from: sammy200-ui
Repo: asyncapi/website PR: 4804
File: components/navigation/Filter.tsx:32-41
Timestamp: 2025-12-29T14:21:43.887Z
Learning: In the Filter component (components/navigation/Filter.tsx), the useEffect hooks are intentionally designed to react only to route/URL changes, not to prop changes (data, checks, onFilter). The omitted dependencies are by design.

Applied to files:

  • components/TOC.tsx
📚 Learning: 2024-10-11T07:38:35.745Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:38:35.745Z
Learning: In Next.js, nested `<a>` tags cause hydration issues due to invalid HTML. To fix this, avoid nesting `<a>` tags by replacing inner `<a>` tags with non-interactive elements like `<span>`, and use click handlers to maintain interactivity if needed.

Applied to files:

  • components/TOC.tsx
📚 Learning: 2024-10-11T10:46:58.882Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.

Applied to files:

  • components/TOC.tsx
📚 Learning: 2025-01-18T08:44:43.614Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3423
File: tests/index.test.js:2-7
Timestamp: 2025-01-18T08:44:43.614Z
Learning: In the AsyncAPI website project, JavaScript test files must include the .ts extension when importing TypeScript files (e.g., `require('../scripts/build-rss.ts')`). This is enforced by the project's configuration which uses `moduleResolution: "bundler"` in tsconfig.json and TypeScript-aware ESLint plugins. The .ts extensions are required even though the files are imported using CommonJS require statements.

Applied to files:

  • components/TOC.tsx
📚 Learning: 2025-12-29T14:21:28.216Z
Learnt from: sammy200-ui
Repo: asyncapi/website PR: 4804
File: components/navigation/Filter.tsx:32-41
Timestamp: 2025-12-29T14:21:28.216Z
Learning: In the asyncapi/website repository, when you intentionally omit dependencies in React hooks (e.g., useEffect, useMemo), add an eslint-disable comment with an explanatory note above the line to justify the design choice. For example: // eslint-disable-next-line react-hooks/exhaustive-deps: intentionally omitting dependencies to avoid unnecessary re-runs. This helps reviewers understand the rationale and keeps lint guidance informative.

Applied to files:

  • components/TOC.tsx
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - asyncapi-website
  • GitHub Check: Header rules - asyncapi-website
  • GitHub Check: Pages changed - asyncapi-website
  • GitHub Check: cypress-run
  • GitHub Check: Test NodeJS PR - windows-latest
🔇 Additional comments (2)
components/TOC.tsx (2)

91-98: This review's core assumption is incorrect—the codebase does not use dynamic routes like pages/docs/[...slug].tsx.

The site uses static generation (output: 'export') with markdown files pre-built into config/posts.json at build time. Each doc page has its own static route, so router.pathname does change across different docs pages. The premise that router.pathname remains constant across docs navigation is unfounded.

While using router.asPath.split('#')[0] is a valid pattern to prevent Scrollspy remounting on hash-only changes, it's not needed to solve the stated issue here.

Likely an incorrect or invalid review comment.


1-1: No action needed; the code correctly uses Pages Router.

The component properly imports useRouter from 'next/router' (correct for Pages Router). The docs routing in this codebase is not a dynamic catch-all pattern—Layout.tsx routes based on actual pathnames (e.g., /docs/guide, /docs/reference), and getDocBySlug() matches the full pathname directly. Therefore, router.pathname does change when navigating between different doc pages, and the Scrollspy key remounts correctly.

No 'use client' directive is needed or applicable in the Pages Router context.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@KariraLakshya KariraLakshya changed the title fix:restore TOC active section highlighting after client-side navigation fix: restore TOC active section highlighting after client-side navigation Jan 10, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/TOC.tsx (1)

90-97: Fix formatting: remove spaces around = in the key prop.

The key prop has a formatting issue that's blocking the pipeline. Remove the spaces around the = sign.

🔧 Proposed fix for formatting
       <div className={`${!open && 'hidden'} ${cssBreakingPoint === 'xl' ? 'xl:block' : 'lg:block'}`}>
         <Scrollspy
-        key = {router.asPath}
+          key={router.asPath}
           items={tocItems.map((item) => (item.slug ? item.slug : item.slugWithATag))}
           currentClassName='text-primary-500 font-bold'
           componentTag='div'

Solution approach is correct.

Using router.asPath as the key effectively solves the issue by forcing Scrollspy to remount when navigating between pages. This resets its internal state and scroll listeners, restoring the active section highlighting functionality.

🤖 Fix all issues with AI agents
In @components/TOC.tsx:
- Around line 1-6: Imports at the top of the file (React, useState, Scrollspy,
twMerge, useRouter, ArrowRight) are not sorted per the
simple-import-sort/imports rule; run the auto-fix or reorder them to satisfy
ESLint: either run the project's formatter (npm run format) or run ESLint with
--fix for this file (npx eslint --fix) to automatically sort the imports, or
manually reorder the import lines so grouped/aliased imports follow the
simple-import-sort ordering.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2fd8c44 and 589580e.

📒 Files selected for processing (1)
  • components/TOC.tsx
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:38:35.745Z
Learning: In Next.js, nested `<a>` tags cause hydration issues due to invalid HTML. To fix this, avoid nesting `<a>` tags by replacing inner `<a>` tags with non-interactive elements like `<span>`, and use click handlers to maintain interactivity if needed.
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.
📚 Learning: 2024-10-11T10:46:58.882Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.

Applied to files:

  • components/TOC.tsx
📚 Learning: 2025-12-29T14:21:28.216Z
Learnt from: sammy200-ui
Repo: asyncapi/website PR: 4804
File: components/navigation/Filter.tsx:32-41
Timestamp: 2025-12-29T14:21:28.216Z
Learning: In the asyncapi/website repository, when you intentionally omit dependencies in React hooks (e.g., useEffect, useMemo), add an eslint-disable comment with an explanatory note above the line to justify the design choice. For example: // eslint-disable-next-line react-hooks/exhaustive-deps: intentionally omitting dependencies to avoid unnecessary re-runs. This helps reviewers understand the rationale and keeps lint guidance informative.

Applied to files:

  • components/TOC.tsx
🪛 GitHub Actions: PR testing - if Node project
components/TOC.tsx

[error] 1-1: ESLint/Prettier: Run autofix to sort imports. Rule: simple-import-sort/imports


[error] 91-91: Prettier formatting issue. Replace key = with key=

⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - asyncapi-website
  • GitHub Check: Header rules - asyncapi-website
  • GitHub Check: Pages changed - asyncapi-website
  • GitHub Check: cypress-run
  • GitHub Check: Lighthouse CI
🔇 Additional comments (1)
components/TOC.tsx (1)

29-29: LGTM! Router instance correctly initialized.

The router is properly instantiated using the useRouter hook, which is the standard pattern for accessing route information in Next.js 15.

Comment on lines 1 to 6
import React, { useState } from 'react';
import Scrollspy from 'react-scrollspy';
import { twMerge } from 'tailwind-merge';

import { useRouter } from 'next/router';
import ArrowRight from './icons/ArrowRight';

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix import sorting to pass ESLint.

The pipeline reports that imports need to be sorted according to the simple-import-sort/imports rule. Run the autofix command to resolve this.

Run the following command to fix import sorting automatically:

npm run format

or

npx eslint --fix components/TOC.tsx
🧰 Tools
🪛 GitHub Actions: PR testing - if Node project

[error] 1-1: ESLint/Prettier: Run autofix to sort imports. Rule: simple-import-sort/imports

🤖 Prompt for AI Agents
In @components/TOC.tsx around lines 1 - 6, Imports at the top of the file
(React, useState, Scrollspy, twMerge, useRouter, ArrowRight) are not sorted per
the simple-import-sort/imports rule; run the auto-fix or reorder them to satisfy
ESLint: either run the project's formatter (npm run format) or run ESLint with
--fix for this file (npx eslint --fix) to automatically sort the imports, or
manually reorder the import lines so grouped/aliased imports follow the
simple-import-sort ordering.

@asyncapi-bot
Copy link
Contributor

asyncapi-bot commented Jan 10, 2026

⚡️ Lighthouse report for the changes in this PR:

Category Score
🔴 Performance 39
🟢 Accessibility 98
🟢 Best practices 92
🟢 SEO 100
🔴 PWA 33

Lighthouse ran on https://deploy-preview-4919--asyncapi-website.netlify.app/

@codecov
Copy link

codecov bot commented Jan 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (7c39172) to head (2f383c8).
⚠️ Report is 5 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #4919   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           22        22           
  Lines          796       796           
  Branches       146       146           
=========================================
  Hits           796       796           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
components/TOC.tsx (1)

92-92: Consider using router.pathname to avoid unnecessary Scrollspy remounts on hash changes.

The fix correctly forces Scrollspy to remount on route changes. However, router.asPath includes the hash fragment, which means clicking TOC links that only change the hash (e.g., from #section1 to #section2) will trigger remounts even though the underlying page hasn't changed.

Since the issue focuses on navigating between docs pages, use router.pathname instead, which excludes query parameters and hashes:

Suggested change
-        key={router.asPath}
+        key={router.pathname}

This limits remounts to actual page navigation, avoiding unnecessary disruption during same-page scrolling.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 589580e and 16da278.

📒 Files selected for processing (1)
  • components/TOC.tsx
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:38:35.745Z
Learning: In Next.js, nested `<a>` tags cause hydration issues due to invalid HTML. To fix this, avoid nesting `<a>` tags by replacing inner `<a>` tags with non-interactive elements like `<span>`, and use click handlers to maintain interactivity if needed.
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.
📚 Learning: 2025-12-29T14:21:43.887Z
Learnt from: sammy200-ui
Repo: asyncapi/website PR: 4804
File: components/navigation/Filter.tsx:32-41
Timestamp: 2025-12-29T14:21:43.887Z
Learning: In the Filter component (components/navigation/Filter.tsx), the useEffect hooks are intentionally designed to react only to route/URL changes, not to prop changes (data, checks, onFilter). The omitted dependencies are by design.

Applied to files:

  • components/TOC.tsx
📚 Learning: 2024-10-11T07:38:35.745Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/newsroom/FeaturedBlogPost.tsx:90-92
Timestamp: 2024-10-11T07:38:35.745Z
Learning: In Next.js, nested `<a>` tags cause hydration issues due to invalid HTML. To fix this, avoid nesting `<a>` tags by replacing inner `<a>` tags with non-interactive elements like `<span>`, and use click handlers to maintain interactivity if needed.

Applied to files:

  • components/TOC.tsx
📚 Learning: 2024-10-11T10:46:58.882Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.

Applied to files:

  • components/TOC.tsx
📚 Learning: 2025-01-18T08:44:43.614Z
Learnt from: akshatnema
Repo: asyncapi/website PR: 3423
File: tests/index.test.js:2-7
Timestamp: 2025-01-18T08:44:43.614Z
Learning: In the AsyncAPI website project, JavaScript test files must include the .ts extension when importing TypeScript files (e.g., `require('../scripts/build-rss.ts')`). This is enforced by the project's configuration which uses `moduleResolution: "bundler"` in tsconfig.json and TypeScript-aware ESLint plugins. The .ts extensions are required even though the files are imported using CommonJS require statements.

Applied to files:

  • components/TOC.tsx
📚 Learning: 2025-12-29T14:21:28.216Z
Learnt from: sammy200-ui
Repo: asyncapi/website PR: 4804
File: components/navigation/Filter.tsx:32-41
Timestamp: 2025-12-29T14:21:28.216Z
Learning: In the asyncapi/website repository, when you intentionally omit dependencies in React hooks (e.g., useEffect, useMemo), add an eslint-disable comment with an explanatory note above the line to justify the design choice. For example: // eslint-disable-next-line react-hooks/exhaustive-deps: intentionally omitting dependencies to avoid unnecessary re-runs. This helps reviewers understand the rationale and keeps lint guidance informative.

Applied to files:

  • components/TOC.tsx
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - asyncapi-website
  • GitHub Check: Header rules - asyncapi-website
  • GitHub Check: cypress-run
  • GitHub Check: Pages changed - asyncapi-website
  • GitHub Check: Test NodeJS PR - windows-latest
🔇 Additional comments (2)
components/TOC.tsx (2)

1-1: LGTM! Import is compatible with Next.js 15.

The useRouter import from next/router is correct for the Pages Router, which Next.js 15 continues to support with backward compatibility.


30-30: LGTM! Proper hook usage.

The router is correctly initialized at the top level of the component, following React's Rules of Hooks.

@KariraLakshya
Copy link
Contributor Author

Update: @princerajpoot20 all the checks are passed and the PR is ready for Review
Thanks!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] TOC active section highlight breaks after navigating between pages

2 participants