Skip to content

Instantly share code, notes, and snippets.

@Phryxia
Last active January 17, 2024 07:25
Show Gist options
  • Save Phryxia/677bb0f4aeb14418e847d6946ba390ed to your computer and use it in GitHub Desktop.
Save Phryxia/677bb0f4aeb14418e847d6946ba390ed to your computer and use it in GitHub Desktop.
React wrapper for vanilla anchor element considering `href` to internal pages
import { AnchorHTMLAttributes, MouseEvent as ReactMouseEvent, useCallback } from 'react'
const InternalUrlRegexp = /^\.?\//
const StartingDotRegexp = /^\./
export function Anchor({
children,
href,
onClick,
...rest
}: AnchorHTMLAttributes<HTMLAnchorElement>) {
const handleClick = useCallback(
(e: ReactMouseEvent<HTMLAnchorElement, MouseEvent>) => {
onClick?.(e)
const isAbsoluteHost = href?.startsWith(location.origin)
const isRelativeHost = href?.match(InternalUrlRegexp)
if (!isAbsoluteHost && !isRelativeHost) return
e.preventDefault()
if (isAbsoluteHost) {
history.pushState(null, '', href)
} else {
history.pushState(null, '', origin + href?.replace(StartingDotRegexp, ''))
}
},
[onClick],
)
return (
<a onClick={handleClick} href={href} {...rest}>
{children}
</a>
)
}
@Phryxia
Copy link
Author

Phryxia commented Jan 17, 2024

When using react, it's verty tidious to disable default behavior of anchor element while preserving accessibilities. You may want to develop your own routing system while not using react-router kind of things.

If you're using next.js, DO NOT use this becasue <Link> from next/link will does it for you. Also this may break next.js's internal routing invariants inside of the history objects.

Behavior

  • When href starts with / or ./, it push url which href is appended, with null history state.
  • When href starts with current origin, it push given href with null history state.
  • Otherwise it behaves equivalently to anchor element.

Note

  • This component doesn't provide page routing solution.
  • When handling click event on internal link, you'll get exact event object which default behavior is already disabled.
  • When handling click event on external link, you'll get exact event object which default behavior is not disabled.
  • For internal url, onClick is called before history is pushed.

Usage

function Foo() {
  return (
    <div>
      <Anchor href="/page-a">Go to A</Anchor>
      <Anchor href="./page-b">Go to B</Anchor>
      <Anchor href="https://my.website.com:4577/page-c">Go to C</Anchor>
      <Anchor href="https://google.com">Go to google</Anchor>
      <Anchor href="#title">Go to title in this page</Anchor>
    </div>
  )
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment