Essential utility functions and React hooks for building accessible React Aria UI components
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Client-side routing integration, synthetic link handling, and programmatic navigation utilities for React applications.
Components and hooks for integrating with client-side routing systems.
/**
* Provides client-side navigation context to nested components
* @param props - Router configuration
* @returns JSX element providing router context
*/
function RouterProvider(props: {
navigate: (path: string) => void;
useHref?: (href: string) => string;
children: ReactNode;
}): JSX.Element;
/**
* Accesses router context for navigation
* @returns Router object with navigation methods
*/
function useRouter(): Router;
interface Router {
/** Whether native browser navigation should be used */
isNative: boolean;
/** Open link with modifier key handling */
open: (target: HTMLAnchorElement, modifiers: Modifiers, setOpening?: boolean) => void;
/** Transform href for routing system */
useHref?: (href: string) => string;
}Usage Examples:
import { RouterProvider, useRouter } from "@react-aria/utils";
// App-level router setup
function App() {
const navigate = (path: string) => {
// Your router's navigation function
history.push(path);
};
const useHref = (href: string) => {
// Transform href for your routing system
return `/app${href}`;
};
return (
<RouterProvider navigate={navigate} useHref={useHref}>
<MainContent />
</RouterProvider>
);
}
// Using router in components
function NavigationComponent() {
const router = useRouter();
const handleLinkClick = (href: string, modifiers: Modifiers) => {
if (router.isNative) {
// Use browser navigation
window.location.href = href;
} else {
// Use client-side navigation
router.open(document.createElement('a'), modifiers);
}
};
return <div>Navigation component</div>;
}Functions for programmatically opening links with modifier key support.
/**
* Programmatically opens links with modifier key support
* @param target - Anchor element to open
* @param modifiers - Object with modifier key states
* @param setOpening - Whether to set opening flag for popup blockers
*/
function openLink(
target: HTMLAnchorElement,
modifiers: Modifiers,
setOpening?: boolean
): void;
/**
* Determines if link should use client-side navigation
* @param link - Anchor element to check
* @param modifiers - Modifier key states
* @returns Boolean indicating if router should handle navigation
*/
function shouldClientNavigate(
link: HTMLAnchorElement,
modifiers: Modifiers
): boolean;
/**
* Handles link clicks with modifier key support
* @param target - Anchor element clicked
* @param modifiers - Modifier key states from event
*/
function handleLinkClick(
target: HTMLAnchorElement,
modifiers: Modifiers
): void;
interface Modifiers {
metaKey: boolean;
ctrlKey: boolean;
altKey: boolean;
shiftKey: boolean;
}Usage Examples:
import { openLink, shouldClientNavigate, handleLinkClick } from "@react-aria/utils";
function SmartLink({ href, children, onClick, ...props }) {
const linkRef = useRef<HTMLAnchorElement>(null);
const handleClick = (e: MouseEvent) => {
if (!linkRef.current) return;
const modifiers = {
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
altKey: e.altKey,
shiftKey: e.shiftKey
};
// Check if we should use client-side navigation
if (shouldClientNavigate(linkRef.current, modifiers)) {
e.preventDefault();
handleLinkClick(linkRef.current, modifiers);
} else {
// Let browser handle navigation
openLink(linkRef.current, modifiers, true);
}
onClick?.(e);
};
return (
<a
ref={linkRef}
href={href}
onClick={handleClick}
{...props}
>
{children}
</a>
);
}
// Link with programmatic opening
function ProgrammaticLink({ href, openInNewTab = false }) {
const linkRef = useRef<HTMLAnchorElement>(null);
const handleButtonClick = () => {
if (!linkRef.current) return;
const modifiers = {
metaKey: openInNewTab,
ctrlKey: openInNewTab,
altKey: false,
shiftKey: false
};
openLink(linkRef.current, modifiers, true);
};
return (
<div>
<a ref={linkRef} href={href} style={{ display: 'none' }} />
<button onClick={handleButtonClick}>
Open Link {openInNewTab ? '(New Tab)' : ''}
</button>
</div>
);
}Utilities for creating non-anchor elements that behave like links.
/**
* Creates data attributes for synthetic links
* @param props - Link DOM properties
* @returns Object with data-* attributes for non-anchor elements
*/
function getSyntheticLinkProps(props: LinkDOMProps): DOMAttributes<HTMLElement>;
/**
* Hook that creates data attributes for synthetic links
* @param props - Link DOM properties
* @returns Object with data-* attributes for non-anchor elements
*/
function useSyntheticLinkProps(props: LinkDOMProps): DOMAttributes<HTMLElement>;
/**
* Hook that processes link props with router context
* @param props - Optional link properties
* @returns Link props with router-processed href
*/
function useLinkProps(props?: LinkDOMProps): LinkDOMProps;
interface LinkDOMProps extends DOMProps {
href?: string;
target?: string;
rel?: string;
download?: boolean | string;
ping?: string;
referrerPolicy?: string;
}Usage Examples:
import { useSyntheticLinkProps, useLinkProps, getSyntheticLinkProps } from "@react-aria/utils";
// Button that acts like a link
function LinkButton({ href, children, ...props }) {
const linkProps = useLinkProps({ href, ...props });
const syntheticProps = useSyntheticLinkProps(linkProps);
return (
<button
{...syntheticProps}
onClick={(e) => {
// Handle click as link
const link = document.createElement('a');
link.href = href;
handleLinkClick(link, {
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
altKey: e.altKey,
shiftKey: e.shiftKey
});
}}
>
{children}
</button>
);
}
// Card component that's entirely clickable
function ClickableCard({ href, title, description }) {
const syntheticProps = getSyntheticLinkProps({ href });
return (
<div
{...syntheticProps}
className="card clickable"
role="link"
tabIndex={0}
onClick={(e) => {
const link = document.createElement('a');
link.href = href;
openLink(link, {
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
altKey: e.altKey,
shiftKey: e.shiftKey
});
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
const link = document.createElement('a');
link.href = href;
openLink(link, { metaKey: false, ctrlKey: false, altKey: false, shiftKey: false });
}
}}
>
<h3>{title}</h3>
<p>{description}</p>
</div>
);
}
// Router-aware link component
function RouterLink({ href, children, ...props }) {
const linkProps = useLinkProps({ href, ...props });
const router = useRouter();
return (
<a
{...linkProps}
onClick={(e) => {
const modifiers = {
metaKey: e.metaKey,
ctrlKey: e.ctrlKey,
altKey: e.altKey,
shiftKey: e.shiftKey
};
if (shouldClientNavigate(e.currentTarget, modifiers)) {
e.preventDefault();
if (router.isNative) {
window.location.href = href;
} else {
// Use router navigation
router.open(e.currentTarget, modifiers);
}
}
}}
>
{children}
</a>
);
}Complex navigation scenarios with conditional routing and state management:
import {
useRouter,
shouldClientNavigate,
useLinkProps,
useSyntheticLinkProps
} from "@react-aria/utils";
function AdvancedNavigation() {
const router = useRouter();
const [isNavigating, setIsNavigating] = useState(false);
const handleNavigation = useCallback(async (href: string, modifiers: Modifiers) => {
const link = document.createElement('a');
link.href = href;
if (shouldClientNavigate(link, modifiers)) {
setIsNavigating(true);
try {
// Perform client-side navigation with loading state
await router.navigate(href);
} finally {
setIsNavigating(false);
}
} else {
// Use browser navigation
openLink(link, modifiers, true);
}
}, [router]);
return (
<nav>
<NavigationItem
href="/dashboard"
onClick={handleNavigation}
isLoading={isNavigating}
>
Dashboard
</NavigationItem>
</nav>
);
}
// Context menu with link actions
function ContextMenu({ href, onClose }) {
const menuActions = [
{
label: 'Open',
action: () => {
const link = document.createElement('a');
link.href = href;
openLink(link, { metaKey: false, ctrlKey: false, altKey: false, shiftKey: false });
onClose();
}
},
{
label: 'Open in New Tab',
action: () => {
const link = document.createElement('a');
link.href = href;
openLink(link, { metaKey: true, ctrlKey: false, altKey: false, shiftKey: false });
onClose();
}
},
{
label: 'Copy Link',
action: () => {
navigator.clipboard.writeText(href);
onClose();
}
}
];
return (
<ul role="menu">
{menuActions.map(action => (
<li key={action.label}>
<button onClick={action.action}>
{action.label}
</button>
</li>
))}
</ul>
);
}interface DOMProps {
id?: string;
}
interface LinkDOMProps extends DOMProps {
href?: string;
target?: string;
rel?: string;
download?: boolean | string;
ping?: string;
referrerPolicy?: string;
}
interface Modifiers {
metaKey: boolean;
ctrlKey: boolean;
altKey: boolean;
shiftKey: boolean;
}
interface Router {
isNative: boolean;
open: (target: HTMLAnchorElement, modifiers: Modifiers, setOpening?: boolean) => void;
useHref?: (href: string) => string;
}Install with Tessl CLI
npx tessl i tessl/npm-react-aria--utils