Skip to content

useNavPermission ​

The menu's own gates. A signed-in user sees what their role allows; a guest sees the whole menu; a switched-off feature is gone for everybody.

Import ​

ts
import { useNavPermission } from '@/layouts/useNavPermission'

Signature ​

ts
function useNavPermission(): {
  can: (action: string, subject: string) => boolean
  isFeatureEnabled: (key: string) => boolean
}

Returns ​

MemberTypeDescription
can(action, subject) => booleantrue for a guest. For a signed-in user, plain CASL.
isFeatureEnabled(key) => booleanWhether config.features[key] is not false. Applies to guests too.

Example ​

vue
<script setup lang="ts">
import type { NavItem } from '@/layouts/types'
import { useNavPermission } from '@/layouts/useNavPermission'
import { computed } from 'vue'

const props = defineProps<{ items: NavItem[] }>()

const { can, isFeatureEnabled } = useNavPermission()

const visible = computed(() => props.items.filter((item) => {
  if (item.feature && !isFeatureEnabled(item.feature))
    return false
  if (item.permission) {
    const [action, subject] = item.permission.split(' ')
    return can(action, subject)
  }
  return true
}))
</script>

<template>
  <nav>
    <RouterLink v-for="item in visible" :key="item.label" :to="item.to ?? ''">
      {{ item.label }}
    </RouterLink>
  </nav>
</template>

Notes ​

  • A guest sees the whole menu, on purpose. Dropping items is right for a signed-in user with a narrow role. For someone who has not signed in, a page missing from the menu reads as a page that was never built — which is the wrong impression for a demo or a marketing visit.
  • Nothing is unlocked by that. The router guard still sends every gated route to login. This decides what is listed, not what is reachable.
  • isFeatureEnabled is the second gate, and it has no guest exception. A feature switched off in the config is off for everyone, because there is no "sign in to see it" story behind it.
  • It reads config.features live, so switching a feature in Settings updates the menu without a reload.
  • An unknown key is treated as enabled: only an explicit false hides an item.