-
Notifications
You must be signed in to change notification settings - Fork 34
Open
Description
- We have an existing nextjs app directory project that is already using next/link everywhere and would be hard to apply consistent use of the foresight link
- I started from an example I found of replacing all next/link imports using tsconfig
- I was able to get the prefetching working, but due to recent changes to nextjs (15.3.4) and react (19.1.0, ref vs forwardedRef changes), there must be something off. When clicking the prefetched links (you can see with the
<link>?_rsc=XXXXnetwork requests), it appears to cause a full page reload. I can't seem to find why it isn't using the cached RSC payload instead.
tsconfig.json
"paths": {
...
"next/link": ["overrides/next/link"]
}
...
The provided forsight link example had to be modified due to how we can't import anything from next/link since that would cause a circular reference. The LinkProps type is not exported from next/dist/client/link.
src/overrides/next/link.tsx
'use client'
import type { ComponentProps } from 'react'
import NextUnifiedLink from 'next/dist/client/link'
import { useRouter } from 'next/navigation'
import useForesight from '@/hooks/useForesight'
import { type ForesightRegisterOptions } from 'js.foresight'
interface ForesightLinkProps
extends ComponentProps<typeof NextUnifiedLink>,
Omit<ForesightRegisterOptions, 'element' | 'callback'> {
children?: React.ReactNode
}
export function ForesightLink({ children, className, ...props }: ForesightLinkProps) {
const router = useRouter()
const { elementRef, registerResults } = useForesight<HTMLAnchorElement>({
callback: () => {
const hrefString =
typeof props.href === 'string' ? props.href : (props.href as { pathname: string }).pathname
router.prefetch(hrefString)
},
hitSlop: props.hitSlop,
name: props.name,
meta: props.meta,
reactivateAfter: props.reactivateAfter,
})
return (
<NextUnifiedLink
{...props}
prefetch={registerResults?.isTouchDevice ?? false}
ref={elementRef}
className={className}
>
{children}
</NextUnifiedLink>
)
}
export const Link = ForesightLink
export default Link
src/hooks/useForsight.tsx (i had to modify this component so that the example in the docs worked. The docs example requires us to reference registerResults.current?.isTouchDevice, rather than registerResults?.isTouchDevice
import { useEffect, useRef, useState } from 'react'
import {
ForesightManager,
type ForesightRegisterOptionsWithoutElement,
type ForesightRegisterResult,
} from 'js.foresight'
export default function useForesight<T extends HTMLElement = HTMLElement>(
options: ForesightRegisterOptionsWithoutElement
) {
const elementRef = useRef<T>(null)
const [registerResults, setRegisterResults] = useState<ForesightRegisterResult | null>(null)
useEffect(() => {
if (!elementRef.current) return
const result = ForesightManager.instance.register({
element: elementRef.current,
...options,
})
setRegisterResults(result)
}, [options])
return { elementRef, registerResults }
}
Metadata
Metadata
Assignees
Labels
No labels