@casl/vue

中文文档
3.1.0发布于 2026/10/28MITTypeScriptESM + CJS零依赖构建来源已验证近 30 天下载 32.8万

Vue plugin for CASL which makes it easy to add permissions in any Vue application

原始使用文档
文档目录12 节
  1. Installation
  2. Getting started
  3. The plugin
  4. provideAbility hook
  5. Can component
  6. TypeScript support
  7. Augment Vue types
  8. Composition API
  9. Options API
  10. Update Ability instance
  11. Want to help?
  12. License

CASL Vue

@casl/vue NPM version Support

This package allows to integrate @casl/ability with Vue 3 application. So, you can show or hide UI elements based on user ability to see them.

Installation

npm install @casl/vue @casl/ability
# or
yarn add @casl/vue @casl/ability
# or
pnpm add @casl/vue @casl/ability
For Vue 2.x
npm install @casl/[email protected] @casl/ability
# or
yarn add @casl/[email protected] @casl/ability
# or
pnpm add @casl/[email protected] @casl/ability

Getting started

This package provides a Vue plugin, several hooks for new Vue Composition API and Can component.

The plugin

The plugin provides reactive Ability instance and optionally defines $ability and $can global properties, in the same way as it was for Vue 2.x. The only difference with the previous version is that it requires Ability instance to be passed as a mandatory argument:

import { createApp } from 'vue';
import { abilitiesPlugin } from '@casl/vue';
import ability from './services/ability';

createApp()
  .use(abilitiesPlugin, ability, {
    useGlobalProperties: true
  })
  .mount('#app');

Later, we can use either $ability or $can method in any component:

<template>
  <div v-if="$can('create', 'Post')">
    <a @click="createPost">Add Post</a>
  </div>
</template>

globalProperties is the same concept as global variables which may make life a bit more complicated because any component has access to them (i.e., implicit dependency) and we need to ensure they don't introduce name collisions by prefixing them. So, instead of exposing $ability and $can as globals, we can use provide/inject API to inject $ability:

createApp()
  .use(abilitiesPlugin, ability)
  .mount('#app');

And to inject an Ability instance in a component, we can use ABILITY_TOKEN:

<template>
  <div>
    <div v-if="$ability.can('create', 'Post')">
      <a @click="createPost">Add Post</a>
    </div>
  </div>
</template>

<script>
import { ABILITY_TOKEN } from '@casl/vue';

export default {
  inject: {
    $ability: { from: ABILITY_TOKEN }
  }
}
</script>

This is a bit more verbose but allows us to be explicit. This works especially good with new Composition API:

<template>
  <div>
    <div v-if="can('create', 'Post')">
      <a @click="createPost">Add Post</a>
    </div>
  </div>
</template>

<script>
import { useAbility } from '@casl/vue';

export default {
  setup() {
    // some code
    const { can } = useAbility();

    return {
      // other props
      can
    };
  }
}
</script>

provideAbility hook

Very rarely, we may need to provide a different Ability instance for a sub-tree of components, and to do this we can use provideAbility hook:

<template>
  <!-- a template -->
</template>

<script>
import { provideAbility } from '@casl/vue';
import { defineAbility } from '@casl/ability';

export default {
  setup() {
    const myCustomAbility = defineAbility((can) => {
      // ...
    });

    provideAbility(myCustomAbility)
  }
}
</script>

See CASL guide to learn how to define Ability instance.

Can component

There is an alternative way we can check permissions in the app, by using Can component. Can component is not registered by the plugin, so we can decide whether we want to use component or v-if + $can method. Also, this helps tree shaking to remove it if we decide to not use it.

To register component globally, we can use global API (we can also register component locally in components that use it):

import { Can, abilitiesPlugin } from '@casl/vue';

createApp()
  .use(abilitiesPlugin, ability)
  .component(Can.name, Can) // component registration
  .mount('#app');

And this is how we can use it:

<template>
  <Can I="create" a="Post">
    <a @click="createPost">Add Post</a>
  </Can>
</template>

It accepts default slot and 5 properties:

  • do - name of the action (e.g., read, update). Has an alias I

  • on - checked subject. Has a, an, this aliases

  • field - checked field

    <template>
      <Can I="read" :this="post" field="title">
        Yes, you can do this! ;)
      </Can>
    </template>
    
  • not - inverts ability check and show UI if user cannot do some action:

    <template>
      <Can not I="create" a="Post">
        You are not allowed to create a post
      </Can>
    </template>
    
  • passThrough - renders children in spite of what ability.can returns. This is useful for creating custom components based on Can. For example, if you need to disable button based on user permissions:

    <template>
      <div>
        <Can I="delete" a="Post" passThrough v-slot="{ allowed, ability, reason }">
          <button :disabled="!allowed" :title="reason">Delete post</button>
        </Can>
      </div>
    </template>
    

Property names and aliases

As you can see from the code above, the component name and its property names and values create an English sentence, actually a question. The example above reads as "Can I delete a Post?".

There are several other property aliases which allow constructing a readable question. And here is a guidance to help you do this:

  • use the a (or an) alias when you check by Type

    <Can I="read" a="Post">...</Can>
    
  • use this alias when you check action on a particular instance. So, the question can be read as "Can I read this particular post?"

    <Can I="read" :this="post">...</Can>
    
  • use do and on if you are bored and don't want to make your code more readable :)

    <Can do="read" :on="post">...</Can>
    <Can do="read" :on="post" field="title">...</Can>
    

Component vs reactive Ability

Let's consider PROS and CONS of both solutions in order to make the decision.

Reactive Ability:

PROS:

  • easy to use
  • declarative in template with v-if
  • easy to pass as a prop to another component
  • easy to use in complex boolean expressions (either in js or in template)

CONS:

  • more expensive to check, conditions are re-evaluated on each re-render

Can Component:

PROS:

  • declarative
  • can cache permissions check results until props or ability changes (currently does not)

CONS:

  • more expensive to create
  • adds nesting in template
  • harder to use in complex boolean expressions
  • harder to pass permission check as a prop to another component

Despite the fact that reactive ability check is a bit more expensive, they are still very fast and it's recommended to use reactive ability instead of <Can> component.

TypeScript support

The package is written in TypeScript, so don't worry that you need to keep all the properties and aliases in mind. If you use TypeScript, your IDE will suggest you the correct usage and TypeScript will warn you if you make a mistake.

There are few ways to use TypeScript in a Vue app, depending on your preferences. But let's first define our AppAbility type:

import { MongoAbility } from '@casl/ability';

type Actions = 'create' | 'read' | 'update' | 'delete';
type Subjects = 'Article' | 'User'

export type AppAbility = MongoAbility<[Actions, Subjects]>;

Augment Vue types

There is no other way for TypeScript to know types of global properties without augmentation. To do this, let's add src/shims-ability.d.ts file with the next content:

import { AppAbility } from './AppAbility'

declare module 'vue' {
  interface ComponentCustomProperties {
    $ability: AppAbility;
    $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean;
  }
}

Composition API

With composition API, we don't need to augment Vue types and can use useAbility hook:

import { useAbility } from '@casl/vue';
import { AppAbility } from './AppAbility';

export default {
  setup(props) {
    const { can } = useAbility<AppAbility>();

    return () => can('read', 'Post') ? 'Yes' : 'No';
  }
}

Additionally, we can create a separate useAppAbility hook, so we don't need to import useAbility and AppAbility in every component we want to check permissions but instead just import a single hook:

import { useAbility } from '@casl/vue';
import { AppAbility } from '../AppAbility';

export const useAppAbility = () => useAbility<AppAbility>();

Options API

It's also possible to use @casl/vue and TypeScript with options API. By default, ABILITY_TOKEN is typed as InjectionKey<Ability>, to cast it to InjectionKey<AppAbility>, we need to use a separate variable:

import { InjectionKey } from 'vue';
import { ABILITY_TOKEN } from '@casl/vue';

// previous content that defines `AppAbility`

export const TOKEN = ABILITY_TOKEN as InjectionKey<AppAbility>;

and now, when we inject AppAbility instance, we will have the correct types:

<script lang="ts">
import { defineComponent } from 'vue';
import { TOKEN } from './AppAbility';

export default defineComponent({
  inject: {
    ability: { from: TOKEN }
  },
  created() {
    this.ability // AppAbility instance
  }
});
</script>

Read Vue TypeScript for more details

Update Ability instance

Majority of applications that need permission checking support have something like AuthService or LoginService or Session service (name it as you wish) which is responsible for user login/logout functionality. Whenever user login (and logout), we need to update Ability instance with new rules. Usually you will do this in your LoginComponent.

Let's imagine that server returns user with a role on login:

<template>
  <form @submit.prevent="login">
    <input type="email" v-model="email" />
    <input type="password" v-model="password" />
    <button type="submit">Login</button>
  </form>
</template>

<script>
import { AbilityBuilder, createMongoAbility } from '@casl/ability';
import { ABILITY_TOKEN } from '@casl/vue';
import { AppAbility } from '../ability';

export default {
  name: 'LoginForm',
  inject: {
    $ability: { from: ABILITY_TOKEN }
  },
  data: () => ({
    email: '',
    password: ''
  }),
  methods: {
    login() {
      const { email, password } = this;
      const params = { method: 'POST', body: JSON.stringify({ email, password }) };

      return fetch('path/to/api/login', params)
        .then(response => response.json())
        .then(({ user }) => this.updateAbility(user));
    },
    updateAbility(user) {
      const { can, rules } = new AbilityBuilder<AppAbility>(createMongoAbility);

      if (user.role === 'admin') {
        can('manage', 'all');
      } else {
        can('read', 'all');
      }

      this.$ability.update(rules);
    }
  }
};
</script>

See Define rules to get more information of how to define Ability.

Want to help?

Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on guidelines for contributing.

If you'd like to help us sustain our community and project, consider to become a financial contributor on Open Collective

See Support CASL for details.

License

MIT License

版本与发布记录

@casl/vue 共发布 41 个版本,首次发布于 2018/03/18;过去 12 个月发布了 6 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.1.0latest2026/10/2811 天前
3.0.12026/07/074 个月前
3.0.02026/05/215 个月前
3.0.0-rcnext2026/05/086 个月前
2.2.62026/01/189 个月前
2.2.52025/12/3110 个月前

其他发布通道:canary → 0.1.0-alpha.ade5be4a,release-1.2.x → 1.2.3,next → 3.0.0-rc。

关于 @casl/vue 的常见问题

@casl/vue 是什么?

Vue plugin for CASL which makes it easy to add permissions in any Vue application

如何安装 @casl/vue?

在项目目录运行 npm install @casl/vue,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:vue(^3.0.0)、@casl/ability(^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.1.0 || ^6.0.0 || ^7.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@casl/vue 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@casl/vue 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@casl/vue 可以商用吗?

@casl/vue 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@casl/vue 的下载量有多少?

根据 npm 官方下载统计,@casl/vue 在 2026/10/09 – 2026/11/07 这 30 天内被下载 328,126 次,日均约 10,938 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@casl/vue 还在维护吗?

最新版本 3.1.0 发布于 2026/10/28(11 天前);至今共发布 41 个版本,其中过去 12 个月发布了 6 个;首次发布于 2018/03/18。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。