my blog https://overreacted.io
1"use client";
2
3import { useTransition } from "react";
4import NextLink from "next/link";
5import { useRouter } from "next/navigation";
6import { MouseEvent } from "react";
7
8function isModifiedEvent(event: MouseEvent<HTMLAnchorElement>) {
9 const eventTarget = event.currentTarget;
10 const target = eventTarget.getAttribute("target");
11 return (
12 (target && target !== "_self") ||
13 event.metaKey ||
14 event.ctrlKey ||
15 event.shiftKey ||
16 event.altKey ||
17 (event.nativeEvent && event.nativeEvent.which === 2)
18 );
19}
20
21export default function Link({
22 className,
23 children,
24 style,
25 href,
26 target,
27 ...rest
28}: {
29 className?: string;
30 children?: React.ReactNode;
31 style?: React.CSSProperties;
32 href: string;
33 target?: string;
34} & React.ComponentProps<typeof NextLink>) {
35 const router = useRouter();
36 const [isNavigating, trackNavigation] = useTransition();
37 const isExternal = /^https?:\/\//.test(href);
38 if (!target && isExternal) {
39 target = "_blank";
40 }
41 return (
42 <NextLink
43 {...rest}
44 target={target}
45 href={href}
46 onClick={(e) => {
47 if (!isModifiedEvent(e)) {
48 e.preventDefault();
49 trackNavigation(() => {
50 router.push(e.currentTarget.href);
51 });
52 }
53 }}
54 className={[className, "scale-100 active:scale-100"].join(" ")}
55 style={{
56 ...style,
57 transform: isNavigating ? "scale(1)" : "",
58 opacity: isNavigating ? 0.85 : 1,
59 transition: "transform 0.2s ease-in-out, opacity 0.2s 0.4s linear",
60 }}
61 >
62 {children}
63 </NextLink>
64 );
65}