Next.js の Link に Chakra UI の Link や Button のデザインを適応させる方法の Tips
環境
- Next.js
13.2.4
- Reat
18.2.0
- Reat
- @chakra-ui/react
2.5.3
- TypeScript
5.0.2
結論: Next.js v13 で Chakra UI を使う場合 Link は @chakra-ui/next-js
を使う、リンクボタンは as={NextLink}
にする
Link
cf. Getting Started with Next.js - Chakra UI
$ npm i @chakra-ui/next-js
Link
import { Link } from '@chakra-ui/next-js'; export default Page(): JSX.Element { return ( <Link href="/page">Link Text</Link> ); }
Link Button
Chakra UI の as に NextLink を渡せばOK
import NextLink from 'next/link'; import { Button } from '@chakra-ui/react'; export default Page(): JSX.Element { return ( <Button as={NextLink}>Button Label</Button> ); }
Next.js の Link コンポーネント
Next.js では SPA的なページ遷移をするには Next が用意している Link
コンポーネントを使う必要があります
Next の Link
コンポーネントは <a>
タグを出力するので同様に <a>
タグや <button>
タグを出力するコンポーネントを使用する時は passHref
を使うよう書かれています
If the child is a custom component that wraps an <a>
tag - Next.js
If the child of Link is a custom component that wraps an
<a>
tag, you must addpassHref
toLink
. This is necessary if you’re using libraries like styled-components. Without this, the<a>
tag will not have thehref
attribute, which hurts your site's accessibility and might affect SEO. If you're using ESLint, there is a built-in rulenext/link-passhref
to ensure correct usage ofpassHref
.
import Link from 'next/link' import styled from 'styled-components' // This creates a custom component that wraps an <a> tag const RedLink = styled.a` color: red; ` function NavLink({ href, name }) { return ( <Link href={href} passHref legacyBehavior> <RedLink>{name}</RedLink> </Link> ) } export default NavLink
If the child is a functional component
If the child of
Link
is a functional component, in addition to usingpassHref
andlegacyBehavior
, you must wrap the component inReact.forwardRef
:
import Link from 'next/link' // `onClick`, `href`, and `ref` need to be passed to the DOM element // for proper handling const MyButton = React.forwardRef(({ onClick, href }, ref) => { return ( <a href={href} onClick={onClick} ref={ref}> Click Me </a> ) }) function Home() { return ( <Link href="/about" passHref legacyBehavior> <MyButton /> </Link> ) } export default Home
Next.js v13 では Chakra UI のコンポーネントを使った時に passHref
で入れ子にすると Hydration failed エラーが発生する問題
Next.js v13 ではドキュメントの例に習い Chakra UI の <Link>
, <Button>
コンポーネントを使おうとすると、リロード時に <a>
タグが入れ子になっているという理由で Hydration failed エラーが発生してしまうようです
import NextLink from 'next/link'; import { Link } from '@chakra-ui/react'; export default Page(): JSX.Element { return ( <NextLink href='/page' passHref> <Link>Link Text</Link> </NextLink> ); }
=> Error: Hydration failed because the initial UI does not match what was rendered on the server. Warning: Expected server HTML to contain a matching <a> in <a>.
Button も as="a"
で a タグ扱いにすると同様のエラーが発生する
Button のデザインを使いたいリンクを作成したい時にありがちなパターンも passHref
で入れ子にすると Hydration failed エラーが発生する
import NextLink from 'next/link'; import { Button } from '@chakra-ui/react'; export default Page(): JSX.Element { return ( <NextLink href='/page' passHref> <Button as="a">Link Text</Button> </NextLink> ); }
=> Error: Hydration failed because the initial UI does not match what was rendered on the server. Warning: Expected server HTML to contain a matching <a> in <a>.
Next.js v13 で Chakra UI の Link, Buttton を使う方法
Link: @chakra-ui/next-js
を使う
cf. Getting Started with Next.js - Chakra UI
$ npm i @chakra-ui/next-js
- import NextLink from 'next/link'; - import { Link } from '@chakra-ui/react'; + import { Link } from '@chakra-ui/next-js' export default Page(): JSX.Element { return ( - <NextLink href='/page' passHref> - <Link>Link Text</Link> - </NextLink> + <Link href="/page">Link Text</Link> ); }
Button: as={NextLink}
を使う
passHref
を使うのではなく Chakra UI の as に NextLink
を指定すれば NextLink な ボタンになる
import NextLink from 'next/link'; import { Button } from '@chakra-ui/react'; export default Page(): JSX.Element { return ( - <NextLink href='/page' passHref> - <Button as="a">Link Text</Button> - </NextLink> + <Button as={NextLink}>Link Text</Button> ); }
所感
Next.js と Chakra UI を使ってサクッとサンプルを作りたかっただけなのですが、思いもしない箇所でハマってしまいました。
Hydration failed のエラーについては理解が浅かったのでまた別途調べておきたいと思いました。
おわり
[参考]