34

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?

movac
  • 1,576
  • 3
  • 21
  • 45

16 Answers16

36

You can use

<router-link 
  :is="isDisabled ? 'span' : 'router-link'"
  to="/link"
>
  /link
</router-link>
  • 1
    Wondering why this is not the accepted answer, even 2 years later – Luciano May 19 '22 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 May 19 '22 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 May 19 '22 at 13:51
  • 3
    Well no... :is works with ... so... change – Joeri May 31 '22 at 19:06
  • If isDisabled is true, this leads to the problem that the text /link will not be shown. – ShadowGames Nov 14 '22 at 13:11
  • So, make your own link component? This sounds silly. – Kalnode May 27 '23 at 13:05
25

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.

Tyler Collier
  • 11,489
  • 9
  • 73
  • 80
  • 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 May 27 '23 at 13:14
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>
BTL
  • 4,311
  • 3
  • 24
  • 29
  • 10
    deprecated in Vue Router v4 https://next.router.vuejs.org/guide/migration/#removal-of-event-and-tag-props-in-router-link – d-_-b Feb 17 '21 at 23:51
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>
Majed Badawi
  • 27,616
  • 4
  • 25
  • 48
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.

CyberAP
  • 1,195
  • 1
  • 11
  • 17
5

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

Paolo
  • 20,112
  • 21
  • 72
  • 113
pieterjanse
  • 101
  • 3
  • 8
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();
        },
yogee
  • 159
  • 1
  • 11
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' : '';
  }
}
1

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

Disorder
  • 167
  • 2
  • 13
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>
Foxlab
  • 564
  • 3
  • 9
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

ElizabethSetton
  • 379
  • 1
  • 3
0

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.

Kalnode
  • 9,386
  • 3
  • 34
  • 62
Sineth Lakshitha
  • 619
  • 6
  • 14
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' }"
>
Adam
  • 121
  • 1
  • 2
-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

onewaveadrian
  • 434
  • 1
  • 6
  • 17
-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>
drocha87
  • 579
  • 4
  • 13
-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')
   }

}
Baraja Swargiary
  • 381
  • 2
  • 10