button-link.tsx

 1import { createLink, type LinkComponent } from "@tanstack/react-router";
 2import type { VariantProps } from "class-variance-authority";
 3import * as React from "react";
 4
 5import { cn } from "@/lib/utils";
 6
 7import { buttonVariants } from "./button";
 8
 9// A proper TanStack Router link that looks like a Button.
10// Replaces the `<Button asChild><Link …/></Button>` pattern,
11// giving us preloading, typed routes, and active link support.
12interface ButtonLinkProps extends VariantProps<typeof buttonVariants> {
13  className?: string;
14  children?: React.ReactNode;
15}
16
17const ButtonLinkComponent = React.forwardRef<
18  HTMLAnchorElement,
19  ButtonLinkProps & React.AnchorHTMLAttributes<HTMLAnchorElement>
20>(({ className, variant, size, children, ...props }, ref) => {
21  return (
22    <a ref={ref} className={cn(buttonVariants({ variant, size, className }))} {...props}>
23      {children}
24    </a>
25  );
26});
27ButtonLinkComponent.displayName = "ButtonLinkComponent";
28
29const CreatedButtonLink = createLink(ButtonLinkComponent);
30
31export const ButtonLink: LinkComponent<typeof ButtonLinkComponent> = (props) => {
32  return <CreatedButtonLink preload="intent" {...props} />;
33};
34
35// A nav link that uses activeProps/inactiveProps for styling.
36// Replaces the manual useMatchRoute() pattern in the header.
37const NavLinkComponent = React.forwardRef<
38  HTMLAnchorElement,
39  React.AnchorHTMLAttributes<HTMLAnchorElement>
40>(({ children, ...props }, ref) => {
41  return (
42    <a ref={ref} {...props}>
43      {children}
44    </a>
45  );
46});
47NavLinkComponent.displayName = "NavLinkComponent";
48
49const CreatedNavLink = createLink(NavLinkComponent);
50
51export const NavLink: LinkComponent<typeof NavLinkComponent> = (props) => {
52  return (
53    <CreatedNavLink
54      preload="intent"
55      className="text-muted-foreground hover:bg-accent hover:text-accent-foreground rounded-md px-3 py-1.5 text-sm font-medium transition-colors"
56      activeProps={{ className: "bg-accent text-accent-foreground" }}
57      inactiveProps={{
58        className: "text-muted-foreground hover:bg-accent hover:text-accent-foreground",
59      }}
60      {...props}
61    />
62  );
63};