Skip to main content
Vuetify0 v1.0 is here
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

usePermissions

Role-based access control with actions, subjects, and context-aware conditions.

Edit this page
Report a Bug
Open issues
View on GitHub
Copy Markdown

PreviewIntermediateJun 29, 2026

Installation

Install the Permissions plugin in your app’s entry point:

main.ts
import { createApp } from 'vue'
import { createPermissionsPlugin } from '@vuetify/v0'
import App from './App.vue'

const app = createApp(App)

app.use(
  createPermissionsPlugin({
    permissions: {
      admin: [
        [['read', 'write'], 'user', true],
        [['read', 'write'], 'post', true],
        ['delete', ['user', 'post'], true],
      ],
      editor: [
        [['read', 'write'], 'post', true],
        ['read', 'user', true],
        ['delete', 'post', (context) => context.isOwner],
      ],
      viewer: [
        ['read', ['user', 'post'], true],
      ],
    },
  })
)

app.mount('#app')

Usage

Once the plugin is installed, check permissions for specific roles in any component:

UsePermissions
<script setup lang="ts">
  import { usePermissions } from '@vuetify/v0'

  const permissions = usePermissions()
  const currentUser = { role: 'editor', id: 'user123' }
</script>

<template>
  <div>
    <button v-if="permissions.can('admin', 'delete', 'user')">
      Delete User (Admin Only)
    </button>

    <button v-if="permissions.can('editor', 'write', 'post')">
      Edit Post
    </button>

    <button
      v-if="permissions.can('editor', 'delete', 'post', { isOwner: true })"
    >
      Delete Own Post
    </button>
  </div>
</template>

Optionally register permissions at runtime:

UsePermissions
<script setup lang="ts">
  import { usePermissions } from '@vuetify/v0'

  const permissions = usePermissions()

  // Register permission at runtime
  permissions.register({
    id: 'moderator.ban.user',
    value: (context) => context.userLevel < 3
  })

  // Check the permission
  const canBan = permissions.can('moderator', 'ban', 'user', { userLevel: 2 })
</script>

Adapters

Adapters let you swap the underlying permission resolution strategy without changing your application code.

AdapterImportDescription
V0PermissionsAdapter@vuetify/v0/permissions/adapters/v0Token-based permission lookup (default)

Custom Adapters

Extend the PermissionsAdapter abstract class to integrate any backend authorization system:

src/adapters/my-permission-adapter.ts
import { PermissionsAdapter } from '@vuetify/v0/permissions/adapters'
import type { PermissionContext, PermissionTicket } from '@vuetify/v0'
import type { ID } from '@vuetify/v0'

class MyPermissionsAdapter extends PermissionsAdapter {
  can<Z extends PermissionTicket>(
    role: ID,
    action: string,
    subject: string,
    context: Record<string, any>,
    permissions: PermissionContext<Z>,
  ): boolean {
    // Delegate to your auth system
    return myAuthClient.check(String(role), `${action}:${subject}`, context)
  }
}

// Use with plugin
app.use(
  createPermissionsPlugin({
    adapter: new MyPermissionsAdapter(),
  })
)

Architecture

usePermissions uses createTokens for permission flattening and lookup:

Permissions Flow

Use controls to zoom and pan. Click outside or press Escape to close.

Permissions Flow

Reactivity

Permissions are stored in a token registry. There are no reactive properties — all interactions are through lookup methods (can(), get(), has()).

Tip

Using with reactive state Wrap can() in a computed to react to permission changes:

ts
const canEdit = computed(() => permissions.can(user.role, 'edit', 'post'))

Examples

Role-Based Workspace

An RBAC workspace where a single role switcher drives two independent consumers at once. AccessProvider builds the permission map with createPermissionsContext and provides it under a custom namespace; both Sidebar and Workspace inject that same context with usePermissions(NAMESPACE) and gate their UI through can(). Switching roles reflows the navigation (which sections are visible) and the toolbar (which actions are enabled) simultaneously, because each can() call lives inside a toRef or a method that reads the reactive role prop.

The toolbar surfaces the context-aware edge case. An editor is denied publish by default, but the permission tuple ['publish', 'documents', context => context.isOwner === true] re-evaluates dynamically when ownership context is supplied as the fourth argument to can(). Toggling the owner switch flips the publish button without touching the role definition — the difference between static role checks (RBAC) and attribute-driven conditions (ABAC).

Reach for this provider/consumer split when several disconnected parts of a screen must honor the same access rules — admin shells, document editors, settings panels. The provider owns the rule set once; every descendant reads it through injection rather than threading permissions down as props. To resolve permissions from a backend instead of static tuples, supply a custom PermissionsAdapter; to drive flag-based UI variations the same way, see useFeatures.

FileRole
context.tsShared namespace, role list, section and action data, and the permission rule map
AccessProvider.vueCreates the permissions context and provides it to the subtree
Sidebar.vueConsumer that filters navigation sections by can(role, 'view', …)
Workspace.vueConsumer that enables document actions, including the owner-gated publish check
access-control.vueEntry point that owns the role state and wraps both consumers in the provider

Quarterly report

Draft document actions

Publish is gated by ownership for editors. Toggle the owner switch to watch the context-aware condition re-evaluate without changing the role definition.

FAQ

Discord
Need help? Join our community for support and discussions ↗

API Reference

The following API details are for the usePermissions composable.
Was this page helpful?

© 2016-1970 Vuetify, LLC
Services
Ctrl+/