36

I have a single-page app that i've created using vue, and the nav links are all done using router-link tags. There are a couple of items in the nav that my boss wants to have in the nav but disabled so that people can get a glimpse of some features that will be coming soon. However I can't figure out how to completely disable a router-link!

preventDefault does nothing, @click.native.prevent="stopClick()" does nothing (i tried sending it to a function to see if that would prevent the click but it just calls the function and routes anyway despite the prevent), adding a disabled class and setting a css rule of pointer-events: none; does nothing. I'm not sure what else to try, is the only way around this to make the disabled links normal text and not router-links?

19 Answers 19

49

You can use

<component
  :is="isDisabled ? 'span' : 'router-link'"
  to="/link"
>
  /link
</component>
6
  • 3
    Wondering why this is not the accepted answer, even 2 years later
    – Luciano
    Commented May 19, 2022 at 13:25
  • 4
    @Luciano, this is not working for me in Vue 3 and vue-router 4.0.13. But what actually work is ` :to="isDisabled ? '' : '/link'" `
    – Disorder
    Commented May 19, 2022 at 13:45
  • @Disorder I'm just testing it (Vue 2 vue-router 3.x.x) and your solution works, but unfortunately, it shows a link that does nothing, so in my case using :is looks prettier. Anyways I will take your suggestion for future Vue 3 projects. Thanks
    – Luciano
    Commented May 19, 2022 at 13:51
  • 3
    Well no... :is works with <component> ... so... change <router-link to <component and it works like a charm.
    – Joeri
    Commented May 31, 2022 at 19:06
  • If isDisabled is true, this leads to the problem that the text /link will not be shown. Commented Nov 14, 2022 at 13:11
26

There is nothing built in, and probably won't ever be. That said, what worked great for me is to use CSS.

<router-link to="/my-route" :class="{ disabled: someBoolean }" />
.disabled {
    opacity: 0.5;
    pointer-events: none;
}

The opacity makes it look disabled, and the pointer-events: none; makes it so you don't need to also handle :hover styles, or set the cursor style.

2
  • 1
    This answer is cleanest, and everyone needs to see that Github post. Later I'd scrutinize accessibility aspects, ie is the disabled link still tabbable or do screen readers ignore it?
    – Kalnode
    Commented May 27, 2023 at 13:14
  • 1
    pointer-events: none; isn't enough. The link will still work when using a keyboard (tab + enter).
    – rzb
    Commented Jun 16 at 23:17
23

There is still no native solution today. But there is an open PR for this on the vue-router repo : https://github.com/vuejs/vue-router/pull/2098.

A workaround is to use :

<router-link 
  :disabled="!whateverActivatesThisLink" 
  :event="whateverActivatesThisLink ? 'click' : ''"
  to="/link"
>
  /link
</router-link>
1
10

I don't think there's a suitable solution for this problem since router links do not have the disabled attribute, but one trick would be using tag="button" in order to add the required attribute as follows:

<router-link 
     to="/link"
     tag="button"
     :disabled="true"
>
  Link
</router-link>
2
  • That's because router-link renders as html anchor tag, and <a> also doesn't support disabled attr. Adding tag="button" will make routerlink renders as button instead, and therefore the button will be naturally usable with :disabled.
    – SC Kim
    Commented Apr 23, 2020 at 23:23
  • Sadly, this will not reflect the semantics of the HTML element :(
    – manniL
    Commented Jan 12, 2022 at 10:19
9

Method 1: Prevent the click event

The trick is to handle an event on a capture phase and stop it from propagating up top.

<router-link 
  to="/path"
  @click.native.capture.stop
>
  Go to page
</router-link>

Or imperatively:

<router-link 
  to="/path"
  @click.native.capture="handleClick"
>
  Go to page
</router-link>
function handleClick(event) {
  if (passesSomeCheck) event.stopPropagation();
}

This might be very useful if you want to get the resolved path from Vue Router to force a page load without SPA navigation.

function handleClick(event) {
  if (loadWithoutSpa) {
    event.stopPropagation();
    window.location.href = event.currentTarget.href;
  };
}

Method 2: Change default event to an empty string

<router-link 
  to="/path"
  event
>
  Go to page
</router-link>

Method 3: Use an <a> tag

<a :href="$router.resolve(route).href">
  Go to page
</a>

Where route can be exactly the same thing you pass to a to prop on router-link.

3
  • 1
    Using event.preventDefault() instead of event.stopPropagation() worked for me to disable the router-link Commented Dec 3, 2021 at 10:19
  • I wouldn't recommend it since event will no longer propagate and for example would not trigger global event listeners.
    – CyberAP
    Commented Dec 4, 2021 at 11:24
  • FYI, event is deprecated in VueRouter4.
    – Kalnode
    Commented May 27, 2023 at 13:10
5

Just set to="" then the link doesn't go anywhere.

1
  • This still renders a functional link that points to the current page, which is not desired.
    – Kalnode
    Commented May 27, 2023 at 13:11
3

As I had to use <router-link> with custom attribute (and added custom meta to routes), I was able to solve it like this:

<router-link :to="route.path"
                  custom
                  v-slot="{href, route, navigate, isActive, isExactActive}">
                <a v-bind="$attrs"
                :href="href"
                @click="checkClick($event, navigate, route.meta)">
                ...
                <slot></slot>
            </a>
</router-link>

an then the function

checkClick(event: any, navigate: any, meta: { enabled: boolean }) {
            if (meta.enabled) {
                navigate(event);
                return;
            }
            event.preventDefault();
        },
2

You can try:

  <router-link
   :event="whateverActivatesThisLink ? 'click' : ''"
  >
  Go to page
  </router-link>

Or write a cleaner code with computed:

  <router-link
  :event="handleWhatEverEvent"
  >
  Go to page
  </router-link>

computed: {
  handleWhatEverEvent() {
    return this.whateverActivatesThisLink ? 'click' : '';
  }
}
0

To prevent a click you can directly access to event property of the router-link element like this (and you can use native click to do something else) :

<router-link 
:event="clickable ? 'click' : ''" 
@click.native="!clickable ? doOtherThing : ''" > Link </router-link>
0

You can set route guard per-route

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        //here you can set if condition 
        if (conditionTrue) {
          //redirect to other route
          next({path: '/bar'});
        } else {
          next();
        }
      }          
    }
  ]
})

Or you can set globally

routes: [
  {path: '/foo', component: Foo, meta:{conditionalRoute: true}}
]; 

router.beforeEach((to, from, next) => { 
    if (to.matched.some(record => record.meta.conditionalRoute)) { 
        // this route requires condition/permission to be accessed
        if (!checkCondition ) { 
            //check condition is false
            next({ path: '/'});
        } else { 
            //check condition is true
            next();
        } 
    } else { 
        next(); // Don't forget next
    } 
})

For more info: https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards

0

Idk why nobody tried replace to prop like this:

<router-link :to="disabled ? '' : '/link'">
  Link
</router-link>

But it's 100% work with any version of Vue and vue-router. Another answers are not working for me with Vue 3 and vue-router 4

1
  • This still renders a functional link that points to the current page, which is not desired.
    – Kalnode
    Commented May 27, 2023 at 13:08
0

You could try using a dynamic component to change the element, it seems to work well now that tag prop has been removed.

<component 
    :is="disabled ? 'span' : 'router-link'"
    :to="{ path: '/path' }"
>
0

Ended up using v-if/v-else. Note the disabled attribute on the v-else button:

<router-link v-if="shouldBeDisabled" :to="somewhere">
    <p-button class="w-full main-button" icon="pi pi-link" label="Detail" />
</router-link>
<p-button v-else class="w-full main-button" disabled icon="pi pi-link" label="Detail" />
0

One way would be to just switch to a different component, like to a <a> tag. This brings the benefit, that it keeps the same styling. And as the <a> tag does not have a to attribute it will not do anything.

<template>
  <component
    :is="disabled ? 'a' : 'router-link'"
    :class="{ 'disabled': disabled }"
    to="/link"
  >
    /link
  </component>
</template>
<style>
a.disabled {
        cursor: not-allowed;
        opacity: 0.5;

        &:hover {
            text-decoration: none;
        }
    }
</style>

Or simply switch between two components via v-if / v-else
This has the benefit that it does not invalidate the HTML with custom attributes and it is explicit.

<template>
    <router-link
        v-if="!disabled"
        to="/link"
    >
        <slot />
    </router-link>
    <a
        v-else
        :class="{ 'disabled': disabled }"
        href="javascript:void(0)"
    >
        <slot />
    </a>
</template>
0

Add Button tag and set onclick to router push:

  <button @click="router.push('/')" disabled>
    MyButton-Text
  </button>
-1

Update for Router v4:

Using a boolean variable status to determine if the link is active or not and passing the link (ctalink) as a variable as well.

Stumbled in here coming from nuxt-link implementation that broke with the update, so from experience this works likewise.

    <router-link
      v-slot="{ navigate }"
      :to="ctalink"
      custom
    >
      <div @click="status ? navigate(ctalink) : null">
        <div :class="status ? 'text-green' : 'text-gray'"> 
          Click me when active 
        </div>
      </div>
    </router-link>

Source: https://next.router.vuejs.org/api/#router-link-s-v-slot

-1

NuxtLink (Vue Router v4)

To me what worked like a charm was the code below. This is a real code that I'm using in my application with nuxt and tailwind.

              <nuxt-link
                v-slot="{ navigate }"
                :to="`lesson/${lesson.lesson_id}`"
                append
                custom
              >
                <button
                  class="focus:outline-none"
                  :class="{
                    'text-gray-500 cursor-default': !lesson.released,
                  }"
                  :disabled="!lesson.released"
                  @click="navigate"
                >
                  {{ lesson.title }}
                </button>
              </nuxt-link>
1
  • 1
    Using <a> (NuxtLink is <a>) with <button> inside is bad. <a> content is 'Transparent, but there must be no interactive content descendant, a element descendant, or descendant with the tabindex attribute specified.' html.spec.whatwg.org/multipage/…
    – Eduardo
    Commented Jan 6, 2023 at 7:37
-1

If using PrimeVue...

You can use class="p-disabled" to disable a link.

Change it dynamically as follows:

:class="isDisable ?  'p-disabled' : ''"

If you use it for a <div>, All the things inside the <div></div> will be disabled.

1
  • It looks disabled but you can still click it Commented Jul 23 at 10:29
-4

use div element or span element instead of in combination with @click event

<div @click="handleRouteNavigation">
your content..
</div>

in script methods define

handleRouteNavigation(){

  if(conditionSatisfied){
      this.$router.push('/mylink')
   }

}
1
  • 1
    You will loose all benefits of <a> tag like open link in new tab and so on
    – Disorder
    Commented May 19, 2022 at 13:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.