Reference

Feature contract reference

Edit page
Exact Customer Portal registry fields for navigation, modules, widgets, audiences, and authorization policies.

Feature layers integrate with the application shell through PortalFeatureDefinition. Import the type from @nuxt-customer-portal/core/feature and register a definition from a client plugin with usePortalFeatures().

interface PortalFeatureDefinition<Action extends string = string> {
  id: string
  navigation?: readonly PortalNavigationItem[]
  modules?: readonly PortalModuleContribution[]
  dashboardWidgets?: readonly PortalDashboardWidget[]
  surfaces?: readonly PortalSurfaceContribution[]
  policy: PortalFeaturePolicy<Action>
}

Feature fields

FieldRequiredContract
idYesStable, globally unique feature identifier. Registering the same ID again replaces the previous definition.
navigationNoTop-level destinations contributed to the portal shell.
modulesNoCohesive module areas and their sidebar menus. One feature may contribute multiple modules.
dashboardWidgetsNoComponents aggregated into the shared dashboard.
surfacesNoGeneric component-name contributions to a documented host surface.
policyYesAllowed feature actions for each organization role.

The registry derives sorted navigation, modules, and dashboardWidgets collections. Navigation and modules default to order 100; widgets require an explicit order.

Audiences and roles

type PortalAudience =
  | 'public'
  | 'authenticated'
  | 'organizationAdmin'
  | 'admin'

type PortalOrganizationRole = 'owner' | 'admin' | 'member'

Audiences control shell visibility. Policies control server authorization. A hidden link does not protect an API route.

FieldTypeNotes
idstringStable identifier within the registry.
labelKeystringTranslation key, normally below features.<feature>.
iconstring?Nuxt Icon name.
tostringDestination route.
audiencesPortalAudience[]People who may see the destination.
location'main' | 'admin'?Defaults to the main application navigation.
ordernumber?Lower values appear first; default is 100.
badgePortalBadgeValue?Package-owned serializable label, color, and variant contract.

Module contribution

A module describes one selectable product area:

FieldTypeNotes
idstringStable module identifier.
labelKeystringTranslated module label.
iconstring?Module icon.
tostringLanding route for the current user.
routePrefixesstring[]Prefixes that keep the module active while navigating.
audiencesPortalAudience[]Shell visibility.
ordernumber?Module ordering; default is 100.
badgePortalBadgeValue?Optional serializable module indicator.
menuItemsPortalModuleMenuItem[]?Sidebar destinations belonging to the module.

A menu item uses id, labelKey, to, audiences, and optional icon, exact, order, and badge fields. Use exact: true when a parent route must not remain active on child pages.

Dashboard widget

FieldAllowed values
idStable string identifier
componentRegistered component name string
areaattention, main, or aside
sizefull, half, or third
orderRequired number

Widgets sort by area, then order, then ID. A feature may register an initial definition and replace it after loading server capabilities; the timesheets layer uses this to remove widgets and menu items a user cannot access.

Surface contribution

PortalSurfaceContribution has a unique id, a documented surface, a registered component-name string, and optional numeric order. The built-in cross-feature surface is administration.organization.detail. The contributing package owns the rendered component.

Policy

Define a literal action list and map every organization role deliberately:

const actions = ['read', 'create', 'update', 'manage'] as const
type Action = typeof actions[number]

const policy: PortalFeaturePolicy<Action> = {
  owner: actions,
  admin: actions,
  member: ['read', 'create', 'update']
}

System administrators bypass an organization policy. Feature routes should normally call requireFeatureAccess(event, policy, action) from @nuxt-customer-portal/core/server. It returns the authenticated session and active organization ID after enforcing both membership and the policy.

The server adapter also exposes getSession, requireSession, requireActiveOrganization, authorize, hasFeatureAccess, requireActiveOrganizationRole, and owner-only organization email-credential access. Prefer these adapters over importing Better Auth or core database internals into a feature.

For an end-to-end implementation, continue with Create a feature layer.