diff --git a/.changeset/cold-buckets-divide.md b/.changeset/cold-buckets-divide.md
new file mode 100644
index 000000000..1e528bad1
--- /dev/null
+++ b/.changeset/cold-buckets-divide.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+fix nested a tag causing hydration error
diff --git a/packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx b/packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx
index 2a246d13c..4218f760e 100644
--- a/packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx
+++ b/packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx
@@ -4,7 +4,7 @@ import {
SiteInsightsLinkPosition,
} from '@gitbook/api';
-import { Link } from '@/components/primitives';
+import { LinkBox, LinkOverlay } from '@/components/primitives';
import { Image } from '@/components/utils';
import { resolveContentRef } from '@/lib/references';
import { type ClassValue, tcls } from '@/lib/tailwind';
@@ -44,7 +44,6 @@ export async function RecordCard(
+ // We don't use `Link` directly here because we could end up in a situation where
+ // a link is rendered inside a link, which is not allowed in HTML.
+ // It causes an hydration error in React.
+
+
{body}
-
+
);
}
diff --git a/packages/gitbook/src/components/RootLayout/globals.css b/packages/gitbook/src/components/RootLayout/globals.css
index 15c09614e..2bb3e1a22 100644
--- a/packages/gitbook/src/components/RootLayout/globals.css
+++ b/packages/gitbook/src/components/RootLayout/globals.css
@@ -148,6 +148,13 @@
width: 100%;
}
}
+
+ .elevate-link {
+ & a[href]:not(.link-overlay) {
+ position: relative;
+ z-index: 20;
+ }
+ }
}
html {
diff --git a/packages/gitbook/src/components/primitives/Link.tsx b/packages/gitbook/src/components/primitives/Link.tsx
index 5cb026d3a..cf9d2c24c 100644
--- a/packages/gitbook/src/components/primitives/Link.tsx
+++ b/packages/gitbook/src/components/primitives/Link.tsx
@@ -3,6 +3,7 @@
import NextLink, { type LinkProps as NextLinkProps } from 'next/link';
import React from 'react';
+import { tcls } from '@/lib/tailwind';
import { type TrackEventInput, useTrackEvent } from '../Insights';
// Props from Next, which includes NextLinkProps and all the things anchor elements support.
@@ -75,6 +76,46 @@ export const Link = React.forwardRef(function Link(
);
});
+/**
+ * A box used to contain a link overlay.
+ * It is used to create a clickable area that can contain other elements.
+ */
+export const LinkBox = React.forwardRef(function LinkBox(
+ props: React.BaseHTMLAttributes,
+ ref: React.Ref
+) {
+ const { children, className, ...domProps } = props;
+ return (
+
+ {children}
+
+ );
+});
+
+/**
+ * A link overlay that can be used to create a clickable area on top of other elements.
+ * It is used to create a link that covers the entire area of the element without encapsulating it in a link tag.
+ * This is useful to avoid nesting links inside links.
+ */
+export const LinkOverlay = React.forwardRef(function LinkOverlay(
+ props: LinkProps,
+ ref: React.Ref
+) {
+ const { children, className, ...domProps } = props;
+ return (
+
+ {children}
+
+ );
+});
+
/**
* Check if a link is external, compared to an origin.
*/