A link allows a user to navigate to a page inside or outside the application.
<Flex orientation="vertical" gap="10"> <Flex items="baseline" gap="10" wrap> <Link to="/docs/components/media/icon">Default (typographical)</Link> <Link to="/docs/components/media/icon" variant="typographical">Typographical</Link> <Link to="/docs/components/media/icon" variant="unstyled">Unstyled</Link> <Link to="/docs/components/media/icon" isDisabled>Disabled</Link> <Link onPress={() => navigate('/docs/components/media/icon')}>JavaScript link</Link> <Link to="https://www.youtube.com/watch?v=dQw4w9WgXcQ">External link</Link> </Flex> <Flex items="baseline" gap="10" wrap> <Link to="/docs/components/media/icon" variant="button" buttonVariant="primary" size="sm">Link styled as button</Link> <Link to="/docs/components/media/icon" variant="button" buttonVariant="primary" size="md">Link styled as button</Link> <Link to="/docs/components/media/icon" variant="button" buttonVariant="primary" size="lg">Link styled as button</Link> </Flex> <Flex items="baseline" gap="10" wrap> <Link to="/docs/components/media/icon" variant="button" buttonVariant="secondary" size="sm">Link styled as button</Link> <Link to="/docs/components/media/icon" variant="button" buttonVariant="secondary" size="md">Link styled as button</Link> <Link to="/docs/components/media/icon" variant="button" buttonVariant="secondary" size="lg">Link styled as button</Link> </Flex> <Flex items="baseline" gap="10" wrap> <Link to="/docs/components/media/icon" variant="button" buttonVariant="danger" size="sm">Link styled as button</Link> <Link to="/docs/components/media/icon" variant="button" buttonVariant="danger" size="md">Link styled as button</Link> <Link to="/docs/components/media/icon" variant="button" buttonVariant="danger" size="lg">Link styled as button</Link> </Flex> <Flex items="baseline" gap="10" wrap> <Link to="/docs/components/media/icon" variant="button" buttonVariant="ghost" size="sm">Link styled as button</Link> <Link to="/docs/components/media/icon" variant="button" buttonVariant="ghost" size="md">Link styled as button</Link> <Link to="/docs/components/media/icon" variant="button" buttonVariant="ghost" size="lg">Link styled as button</Link> </Flex></Flex>Here's the <Link /> component in action.
import { NavLink, type NavLinkProps } from '@remix-run/react'import { type ElementRef, forwardRef, useRef, type MutableRefObject, type RefCallback } from 'react'import { useLink, mergeProps, type AriaLinkOptions, useHover, useFocusRing } from 'react-aria'import { cn } from '#app/utils/tailwind-merge.ts'
export interface LinkProps extends AriaLinkOptions, Omit<NavLinkProps, 'download' | 'onBlur' | 'onFocus' | 'onKeyDown' | 'onKeyUp' | 'to'> { to?: NavLinkProps['to'] variant?: 'default' | 'typographical' | 'unstyled' | 'button' | 'ghost' buttonVariant?: 'primary' | 'secondary' | 'danger' | 'ghost' size?: 'sm' | 'md' | 'lg'}
const Link = forwardRef<ElementRef<typeof NavLink>, LinkProps>(({ to = '/', variant = 'default', buttonVariant = 'primary', size, children, className, ...props }, ref) => { const privateRef = useRef(null) const { linkProps, isPressed } = useLink(props, privateRef) const { hoverProps, isHovered } = useHover(props) const { focusProps, isFocused, isFocusVisible } = useFocusRing() const mergedProps = mergeProps(props, linkProps, hoverProps, focusProps) return ( <NavLink ref={mergeRefs(privateRef, ref)} to={to} className={cn('link', variant, variant === 'button' ? buttonVariant : null, size, className)} {...mergedProps} data-focused={isFocused || undefined} data-hovered={isHovered || undefined} data-pressed={isPressed || undefined} data-focus-visible={isFocusVisible || undefined} data-current={!!props['aria-current'] || undefined} data-disabled={props.isDisabled || undefined} > {children} </NavLink> )})Link.displayName = 'Link'
type MutableRefList<T> = Array<RefCallback<T> | MutableRefObject<T> | undefined | null>
export function mergeRefs<T>(...refs: MutableRefList<T>): RefCallback<T> { return (val: T) => { setRef(val, ...refs) }}export function setRef<T>(val: T, ...refs: MutableRefList<T>): void { refs.forEach(ref => { if (typeof ref === 'function') { ref(val) } else if (ref != null) { ref.current = val } })}
export { Link }
.link { @apply rounded-sm;
&.typographical { @apply underline; } &.unstyled { /* Purposefully empty */ }}