React-Router-Dom V6 Anchor Link
React Router v6 Anchor Link
TL;DRReact Router v6 原生不支援 hash link 的捲動行為(<Link to="/about#team"> 不會自動捲到 #team)。用 react-router-hash-link 套件解決,支援 smooth scroll 跟跨頁 hash。
為什麼 v6 不支援?
React Router 是 SPA router,負責路由切換但不負責瀏覽器原生 anchor 行為。瀏覽器原生的 <a href="#section"> 行為依賴 頁面元素已經渲染,SPA 切頁時 React 還沒掛載目標元素,所以 #section 找不到。
// ❌ 不會自動捲到 #team
<Link to="/about#team">關於 - 團隊</Link>
解法:react-router-hash-link
npm install react-router-hash-link
npm install --save-dev @types/react-router-hash-link
import { HashLink } from 'react-router-hash-link';
// 基本用法:跳到同一頁面的某個區塊
<HashLink to="/#section1">跳到 Section 1</HashLink>
// 跨頁面 hash 導航
<HashLink to="/about#team">關於我們 - 團隊</HashLink>
// 加 smooth scroll
<HashLink to="/#section2" smooth>
平滑捲動到 Section 2
</HashLink>
| Prop | 說明 |
|---|---|
to | 目標路徑 + #anchor(支援跨頁) |
smooth | 平滑捲動(等同 scrollIntoView({ behavior: 'smooth' })) |
scroll | 自訂捲動函式 (el) => el.scrollIntoView(...) |
elementId | 替代 hash 的方式(直接給 ID) |
套件做了什麼
簡化版內部邏輯:
import { useLocation, Link } from 'react-router-dom';
import { useEffect } from 'react';
function HashLink({ to, smooth, ...rest }) {
return (
<Link
to={to}
{...rest}
onClick={(e) => {
// 路徑切換後等下一個 tick 找元素 + 捲動
setTimeout(() => {
const id = to.split('#')[1];
const el = document.getElementById(id);
el?.scrollIntoView({ behavior: smooth ? 'smooth' : 'auto' });
}, 0);
}}
/>
);
}
重點是 setTimeout(..., 0),等 React 渲染完目標元素再去找。
自己實作(不裝套件)
如果不想多裝一個套件,可以自己寫個 hook:
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function useScrollToHash() {
const { hash } = useLocation();
useEffect(() => {
if (!hash) return;
const id = hash.replace('#', '');
const el = document.getElementById(id);
el?.scrollIntoView({ behavior: 'smooth' });
}, [hash]);
}
// 在 root component 用
function App() {
useScrollToHash();
return <Routes>...</Routes>;
}
缺點:跨頁面跳轉時,目標頁面 component mount 完成的時機可能晚於 hook,要加 setTimeout 或 useLayoutEffect 配合。直接用 react-router-hash-link 比較穩。
Next.js 對等做法
Next.js 跟 React Router 不一樣,<Link href="/about#team"> 會 自動捲動。但 SSR 後若元素還沒 hydrate 完,可能也需要等 hydration:
import Link from 'next/link';
<Link href="/about#team" scroll>
關於 - 團隊
</Link>
scroll prop 是 true 為預設(會自動捲),設 false 才會關掉。
smooth scroll 的瀏覽器支援
/* 全頁啟用 smooth scroll */
html {
scroll-behavior: smooth;
}
所有現代瀏覽器都支援(Chrome 61+ / Firefox 36+ / Safari 15.4+)。直接 CSS 一行解決就不用 JS。但要避免動畫被 prefers-reduced-motion 使用者打擾:
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
}