npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@providame/vue

v0.0.15

Published

Providame Vue 3 bindings — headless composables and prebuilt auth components.

Downloads

570

Readme

@providame/vue

Vue 3 bindings for Providame — headless composables (build your own UI) and prebuilt components (drop-in), both over the same @providame/core engine.

Building a native or hybrid app (Capacitor or Ionic, or any app without a browser cookie jar)? Use a native key (nk_) instead of your publishable key — native mode comes from @providame/core and is configured on the client. See the Native apps guide.

Install

npm install @providame/vue vue

Usage

Install the plugin once, and import the stylesheet:

import { createApp } from 'vue'
import { ProvidamePlugin } from '@providame/vue'
import '@providame/vue/style.css' // required — see below

createApp(App).use(ProvidamePlugin, {
  publishableKey: 'pk_test_...',
})

The stylesheet import is required. Without it every prebuilt component renders completely unstyled — there is no runtime fallback, and no error to tell you what's wrong.

This package ships a real CSS file rather than injecting styles at runtime, so Nuxt and other SSR frameworks can inline it into the document head instead of flashing unstyled content on first paint. @providame/react and @providame/sdk-js inject at runtime and need no import — this package is the exception, deliberately.

In Nuxt, add it to nuxt.config.ts instead: css: ['@providame/vue/style.css'].

Prebuilt components

<script setup>
import { SignIn } from '@providame/vue'
</script>

<template>
  <SignIn sign-up-url="/sign-up" @complete="onComplete" />
</template>

Component props

SignIn, SignUp, and ResetPassword all accept title, subtitle, and logo. title defaults to a branding-aware heading (e.g. "Sign in to Acme") once your environment's app name loads; logo defaults to your configured branding logo.

SignUp additionally accepts:

  • collect-name (default true) — toggles the first/last name fields.
  • invite-token — required when your environment's signup-policy mode is invite_only: the single-use code from an admin-generated signup-invite email. Read it from your own app's invite-accept URL and pass it through — it threads directly to CreateSignUpParams.inviteToken. Every other signup-policy mode ignores it if set.
<SignUp :collect-name="false" invite-token="tok_..." />

Modal vs. page usage

These components serve two contexts, and the footer link behaves differently in each:

  • Page — you render <SignIn> at your own /sign-in route. Pass sign-up-url and the footer renders a normal link that navigates.
  • Modal — <SignInButton mode="modal"> handles this for you: the footer switches to sign-up in place, because navigating would tear the modal down.

Rendering these inside your own modal? Attach the switch listener instead of passing a URL, and hold the view state yourself:

<SignIn v-if="view === 'sign-in'" @switch-to-sign-up="view = 'sign-up'" />
<SignUp v-else @switch-to-sign-in="view = 'sign-in'" />

The listener takes precedence when both it and a URL are supplied.

SignInButton no longer accepts sign-up-url (nor SignUpButton sign-in-url). They had no effect in mode="redirect" and produced a modal-destroying navigation in mode="modal". Pass those URLs to <SignIn>/<SignUp> directly when rendering them as pages.

Headless

import { useSignIn } from '@providame/vue'

const signIn = useSignIn()

// Preferred — one guarded call for the common identifier + password case.
await signIn.signInWithPassword('[email protected]', 'password')

// Equivalent, longhand — useful if you need to do something between the two
// steps, e.g. show a separate password screen after the identifier is entered.
await signIn.create('[email protected]')
await signIn.attemptFirstFactor({ strategy: 'password', password: 'password' })

if (signIn.isComplete.value) {
  // signIn.session.value is set
}

Passwordless sign-in & MFA (useSignIn)

Beyond password sign-in, useSignIn() exposes passkey, email-OTP, SMS-OTP, and magic-link sign-in, plus mid-flow TOTP enrollment. The prebuilt <SignIn> above already wires password and passkey sign-in — email-OTP, SMS-OTP, and magic-link are headless-only escape hatches; build your own UI around them if you need one.

import { useSignIn } from '@providame/vue'
const signIn = useSignIn()

// Passkey — opens the flow and immediately prompts navigator.credentials.get().
await signIn.signInWithPasskey(identifier, window.location.hostname)

// Email one-time code (first factor)
await signIn.signInWithOTPEmail(identifier)
await signIn.attemptFirstFactor({ strategy: 'otp_email', code })
await signIn.resendOTPEmail()

// SMS one-time code (first factor) — identifier is still email; the code is
// texted to whatever phone number is on file for that account.
await signIn.signInWithOTPSMS(identifier)
await signIn.attemptFirstFactor({ strategy: 'otp_sms', code })
await signIn.resendOTPSMS()

// Magic link — emails a passwordless "click to sign in" link pointing at returnUrl.
await signIn.signInWithMagicLink(identifier, 'https://myapp.com/auth/callback')

// Mid-flow TOTP, reached from any of the strategies above.
if (signIn.status.value === 'needs_second_factor') {
  await signIn.attemptSecondFactor({ strategy: 'totp', code })
}
if (signIn.status.value === 'needs_mfa_enrollment') {
  await signIn.enrollTotp() // -> signIn.enrollment.value = { secret, otpauthUri }
  await signIn.verifyTotpEnrollment(code)
}

signIn.reset() // back to "idle"

firstFactors/secondFactors report what the backend will accept next; recoveryCodes is populated right after a mid-flow TOTP enrollment.

Sign-up (useSignUp)

useSignUp() mirrors useSignIn() — same reactive-state shape, for registration. The prebuilt <SignUp> already wires all of this; reach for the composable directly to build a fully custom sign-up UI.

import { useSignUp } from '@providame/vue'
const signUp = useSignUp()

await signUp.create({
  email,
  password,
  firstName, // optional
  lastName, // optional
  inviteToken, // required only when signup-policy mode is invite_only
  turnstileToken, // required only when bot-check is enabled for this environment
})

if (signUp.needsVerification.value) {
  await signUp.attemptVerification({ code })
}

// Post-registration second factor, if the environment's MFA policy requires one.
if (signUp.status.value === 'needs_second_factor') {
  await signUp.attemptSecondFactor({ strategy: 'totp', code })
}

// Forced enrollment on a brand-new account.
if (signUp.status.value === 'needs_mfa_enrollment') {
  await signUp.enrollTotp() // -> signUp.enrollment.value = { secret, otpauthUri }
  await signUp.verifyTotpEnrollment(code)
}

if (signUp.isComplete.value) {
  // signUp.session.value is set; signUp.recoveryCodes.value may carry backup codes
} else if (signUp.isWaitlisted.value) {
  // verified, but this environment's signup-policy mode is "waitlist" — no session issued yet
}

signUp.reset()

Password reset (useResetPassword, <ResetPassword>)

<script setup>
import { ResetPassword } from '@providame/vue'
</script>

<template>
  <ResetPassword sign-in-url="/sign-in" @complete="onComplete" />
</template>

The prebuilt <SignIn> already embeds this whole flow behind its "Forgot password?" link — mount <ResetPassword> on its own route only if you want it reachable directly (e.g. from a password-reset email link).

import { useResetPassword } from '@providame/vue'
const reset = useResetPassword()

await reset.create({ email })
await reset.attempt({ code, password }) // sets the new password and signs in

// Mid-flow TOTP, same shape as useSignIn/useSignUp.
if (reset.status.value === 'needs_second_factor') {
  await reset.attemptSecondFactor({ strategy: 'totp', code })
}
if (reset.status.value === 'needs_mfa_enrollment') {
  await reset.enrollTotp() // -> reset.enrollment.value = { secret, otpauthUri }
  await reset.verifyTotpEnrollment(code)
}

await reset.resend() // re-send the reset code
reset.reset() // back to "idle"

attempt()/attemptSecondFactor()/enrollTotp()/verifyTotpEnrollment()/resend() all throw if called before create() — there's no flow yet to act on.

What's in the box

Components — SignIn, SignUp, ResetPassword, UserButton, UserProfile, SignInButton, SignUpButton, SignOutButton, SignedIn, SignedOut, Protect, RedirectToSignIn, OrganizationSwitcher, OrganizationProfile, CreateOrganization.

Composables — useAuth, useUser, useSession, useSignIn, useSignUp, useSignInIdp, useResetPassword, useOrganization, useOrganizationList, useBranding, usePasskeyRegistrationLink, useProvidame.

Every composable returns Refs — including the functions, matching Clerk's own Vue SDK. So it's await getToken.value(), not getToken().

Organizations

<script setup>
import { OrganizationSwitcher, OrganizationProfile, useOrganization } from '@providame/vue'

// Reads token claims — makes no request
const { organization, roles, permissions, has } = useOrganization()
</script>

<template>
  <OrganizationSwitcher />
  <OrganizationProfile />
</template>

useOrganization() covers the active organization only and never fetches — the token already carries org_id/org_slug/org_roles/org_permissions. useOrganizationList() does fetch, since the full membership list isn't in a token; it also exposes setActive(orgId).

setActive() is remembered across sign-ins, so you don't call it on every login: a returning user is put back in the organization they last used, a user who has never chosen starts in the one they joined first, and clearing it sticks too.

<OrganizationProfile> gates each action on the caller's own org:sys_* permissions rather than a role name, so it works with roles you define yourself. Pass organization-id to render a specific organization instead of the session's active one.

<OrganizationSwitcher> accepts hide-create (drop "Create organization", for apps that provision organizations centrally), personal-label (relabel the no-organization option — Clerk calls it "Personal account"), and hide-personal (drop that option entirely, for a B2B-only app where every user belongs to an organization).

Gating UI

<Protect role="admin">...</Protect>
<Protect org-permission="org:sys_memberships:manage">...</Protect>
<Protect role="admin" org-role="org:admin">...</Protect>   <!-- both must pass -->

role/permission gate on environment-level grants; org-role/org-permission gate on the active organization. Omitted props are not constraints. This is UI gating only — real enforcement is your backend verifying the token, via @providame/backend.

Object-scoped access

<Protect> reads claims already in the token — synchronous, free, never fails. <Can> asks a question about one specific resource — a network call, so it needs a loading state and can fail:

<Can permission="edit" resource="document:42">
  <template #loading><Spinner /></template>
  <template #fallback>You can't edit this document.</template>
  <button>Edit</button>
</Can>

Use <Protect role="admin"> for "is this user an admin"; use <Can permission="edit" resource="document:42"> for "can this user edit this document". <Can> fails closed — a network error renders #fallback, never the default slot. useCan({ permission, resource }) is the headless equivalent, returning { allowed, isLoading, error }.

Answers are cached per-question for the life of the session (cleared on sign-in/out and organization switch) — mounting <Can> for the same resource twice on one page costs one request, not two.

<Can> also accepts an optional context prop (a plain object) that passes extra values through for conditional/attribute-based permissions — e.g. checking a permission that's only granted below a certain amount or within a certain region.

Branding & the underlying client

useBranding() is called internally by every prebuilt component above — reach for it directly only if you're building a fully custom UI and want the same environment-configured colors/logo applied to it:

import { useBranding } from '@providame/vue'

// Fetched once and memoized on the client instance — mounting several
// components that call this on one page costs a single request, not one
// per component. Also applies the colors as global CSS custom properties
// (--providame-accent/-bg/-fg and their -dark counterparts).
const branding = useBranding() // Ref<Branding>

branding.value.logoUrl // string | undefined
branding.value.primaryColor
branding.value.appName

useProvidame() returns the underlying Providame client instance every composable in this package is built on — every other composable calls this internally, so you only need it directly to reach a client method this package doesn't already wrap:

import { useProvidame } from '@providame/vue'

const providame = useProvidame()

await providame.configuration() // { turnstileSiteKey, tosUrl, privacyUrl, helpUrl, supportEmail, ... }
await providame.branding() // same data useBranding() applies automatically
providame.organizations // OrganizationsApi — used internally by <OrganizationProfile>

Theming

Every component accepts an appearance prop:

<SignIn :appearance="{ variables: { colorPrimary: '#111827' } }" />

Components also fetch your environment's configured branding (colors, logo, light and dark) on mount and apply it automatically, so an unstyled appearance prop is usually unnecessary.

Docs

Full reference: /docs/sdk/vue in your Providame dashboard.

Build

npm run build