Disabled Button state not transmitted to Link - asChild
Hello there, I have tried to implement this example in which I try to disable the button. Seems not working. The variant link added to the button isn't working either:
<Button asChild disabled={!isValid}>
<Link to={'/final'}>
Groot
</Link>
</Button>
Is there a way to disable a Link?
Have a nice day! Great work! Nico
@MagicKitty having a similar issue, did you find a fix?
Same here
try,
<Button asChild> <Link href='/final' target="_blank"> Groot </Link> </Button>
As I understand the problem: When you declare a button with asChild, what happens is the Button is never rendered in the DOM. So basically no tailwindcss classes can be passed from Button to Link with group. I cannot really figure out a good solution. The only thing that can be OKish is to style Link as a button:
const disabled = false;
<Link
to={'/'}
className={cn(
buttonVariants({ variant: 'outline' }),
disabled && 'pointer-events-none opacity-50',
)}
>
Click here
</Link>
Only using shadcn for 20 min and already disapointed :/
Only using shadcn for 20 min and already disapointed :/
I'm starting to feel the same way. Will switch to TailwindUI Catalyst when they have a Vue version available
This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.
When you are using asChild, the Button component simply passes props to the child component, in our case, to the Link component.
I am using @tanstack/router, so when the disabled prop is passed to the Link component, @tanstack/router sets the aria-disabled attribute.
Shadcn creates buttonVariants for you (this is the code from Shadcn WITHOUT SOLUTION):"
import { cva } from "class-variance-authority";
export const buttonVariants = cva(
"tw-inline-flex tw-items-center tw-justify-center tw-whitespace-nowrap tw-rounded-md tw-text-sm tw-font-medium tw-ring-offset-background tw-transition-colors focus-visible:tw-outline-none focus-visible:tw-ring-2 focus-visible:tw-ring-ring focus-visible:tw-ring-offset-2 disabled:tw-pointer-events-none disabled:tw-opacity-50",
{
variants: {
variant: {
outline: "tw-border tw-border-input tw-bg-background hover:tw-bg-accent hover:tw-text-accent-foreground",
destructive: "tw-bg-destructive tw-text-destructive-foreground hover:tw-bg-destructive/90",
secondary: "tw-bg-secondary tw-text-secondary-foreground hover:tw-bg-secondary/80",
default: "tw-bg-primary tw-text-primary-foreground hover:tw-bg-primary/90",
link: "tw-text-primary tw-underline-offset-4 hover:tw-underline",
ghost: "hover:tw-bg-accent hover:tw-text-accent-foreground",
},
size: {
lg: "tw-h-11 tw-rounded-md tw-px-8",
default: "tw-h-10 tw-px-4 tw-py-2",
sm: "tw-h-9 tw-rounded-md tw-px-3",
icon: "tw-h-10 tw-w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
I added these classes to the end of the first line of cva, and it helped
cva(
"...aria-disabled:tw-opacity-50 aria-disabled:tw-pointer-events-none",
{
variants: ...
And this will work:
import { Button } from "@/libs/shadcn/ui/button";
import { Link } from "@tanstack/react-router";
<Button variant="ghost" disabled={true} asChild>
<Link to="/interviews/$interviewId" params={{ interviewId }}>
Some link
</Link>
</Button>;
Hello there, I have tried to implement this example in which I try to disable the button. Seems not working. The variant link added to the button isn't working either:
<Button asChild disabled={!isValid}> <Link to={'/final'}> Groot </Link> </Button>Is there a way to disable a Link?
Have a nice day! Great work! Nico
My solution for this case is to set asChild to be the ! of whatever the disabled value is.
For example, in your case
<Button asChild={isValid} disabled={!isValid}>
<Link to={'/final'}>
Groot
</Link>
</Button>
I settled on this approach which is nice enough:
if (disabled) {
return (
<Button variant="link" className={className} disabled>
{children}
<ExternalLinkIcon size={"0.8em"} className="ml-1" />
</Button>
);
}
return (
<Button variant="link" asChild className={className}>
<Link href={href} target="_blank">
{children}
<ExternalLinkIcon size={"0.8em"} className="ml-1" />
</Link>
</Button>
);
Respects the idea that a disabled link isn't really a link at all!
When you are using
asChild, theButtoncomponent simply passes props to the child component, in our case, to theLinkcomponent.I am using
@tanstack/router, so when the disabled prop is passed to theLinkcomponent,@tanstack/routersets thearia-disabledattribute.Shadcn creates
buttonVariantsfor you (this is the code from Shadcn WITHOUT SOLUTION):"import { cva } from "class-variance-authority"; export const buttonVariants = cva( "tw-inline-flex tw-items-center tw-justify-center tw-whitespace-nowrap tw-rounded-md tw-text-sm tw-font-medium tw-ring-offset-background tw-transition-colors focus-visible:tw-outline-none focus-visible:tw-ring-2 focus-visible:tw-ring-ring focus-visible:tw-ring-offset-2 disabled:tw-pointer-events-none disabled:tw-opacity-50", { variants: { variant: { outline: "tw-border tw-border-input tw-bg-background hover:tw-bg-accent hover:tw-text-accent-foreground", destructive: "tw-bg-destructive tw-text-destructive-foreground hover:tw-bg-destructive/90", secondary: "tw-bg-secondary tw-text-secondary-foreground hover:tw-bg-secondary/80", default: "tw-bg-primary tw-text-primary-foreground hover:tw-bg-primary/90", link: "tw-text-primary tw-underline-offset-4 hover:tw-underline", ghost: "hover:tw-bg-accent hover:tw-text-accent-foreground", }, size: { lg: "tw-h-11 tw-rounded-md tw-px-8", default: "tw-h-10 tw-px-4 tw-py-2", sm: "tw-h-9 tw-rounded-md tw-px-3", icon: "tw-h-10 tw-w-10", }, }, defaultVariants: { variant: "default", size: "default", }, }, );I added these classes to the end of the first line of
cva, and it helpedcva( "...aria-disabled:tw-opacity-50 aria-disabled:tw-pointer-events-none", { variants: ...And this will work:
import { Button } from "@/libs/shadcn/ui/button"; import { Link } from "@tanstack/react-router"; <Button variant="ghost" disabled={true} asChild> <Link to="/interviews/$interviewId" params={{ interviewId }}> Some link </Link> </Button>;
This works perfectly fine. Just remember that if your Link component doesn't set the aria-disabled attribute you will have to do it yourself. In my case i was using Inertia so my PaginationLink (a button under the hood) got change to this
//From this
<PaginationLink
...
disabled={previousLink.url === null}
asChild>
<Link
...
>
</Link>
</PaginationLink>
//To this
<PaginationLink
...
asChild
>
<Link aria-disabled={previousLink.url === null}
...
>
...
</Link>
</PaginationLink>
Prop disabled on the button doesn't do anything.
Other thing to keep in mind is to change "tw-" for the tailwind prefix your using.
I came up with this, which basically always renders a (disabled) button when the disabled attribute is set:
- const Comp = asChild ? Slot : 'button';
+ const Comp = asChild && !disabled ? Slot : 'button';