Next.js の Link に Chakra UI の Link や Button のデザインを適応させる方法の Tips
環境
- Next.js
13.2.4
- @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>
);
}
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 of Link is a custom component that wraps an <a>
tag, you must add passHref
to Link
. This is necessary if you’re using libraries like styled-components. Without this, the <a>
tag will not have the href
attribute, which hurts your site's accessibility and might affect SEO. If you're using ESLint, there is a built-in rule next/link-passhref
to ensure correct usage of passHref
.
import Link from 'next/link'
import styled from 'styled-components'
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 using passHref
and legacyBehavior
, you must wrap the component in React.forwardRef
:
import Link from 'next/link'
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
cf. next/link | Next.js
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 のデザインを使いたいリンクを作成したい時にありがちなパターンも 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>
);
}
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 のエラーについては理解が浅かったのでまた別途調べておきたいと思いました。
おわり
[参考]