threshold1

Vue

Same underlying idea as the React guide — construct the client once, do mount-time work in the right lifecycle hook, trigger auth calls from real user actions — expressed with Vue's Composition API primitives instead of React's.

Where to construct the client

Put new Threshold1(...) in its own module, not inside a component's <script setup>:

// src/auth.ts
import { Threshold1 } from "@threshold1/auth";

export const auth = new Threshold1({
  apiKey: "th_test_...",
  auth: { fallback: "smart" },
});

This matters more in Vue than it might look. <script setup> compiles down to a component's setup() function, and Vue only re-runs setup() when a component is freshly instantiated — not on every reactive update, so it's not the same "runs on every render" trap React's function-component body has. But it does re-run on every mount, including a remount: if this component sits behind v-if, or is a routed page a user navigates away from and back to, constructing auth directly inside <script setup> would build a brand-new instance — and silently drop any in-memory session — each time. A separate module, imported wherever it's needed, is a true singleton for the app's entire lifetime regardless of what any individual component does. This is also just the standard Vue convention for a shared client (the same shape you'd use for an API client or a shared service), not something specific to this SDK.

The pattern

<script setup lang="ts">
import { ref, onMounted } from "vue";
import { auth } from "./auth";

const email = ref("");

// Runs once when this component mounts — the right place for anything
// that should happen automatically on load, like picking up a
// magic-link redirect.
onMounted(() => {
  auth.resumeSession().then((user) => {
    if (user) {
      // already logged in via a magic-link click
    }
  });
});

// Triggered by a real user action — the right place for register()/login().
async function handleSubmit() {
  const result = await auth.login(email.value);
  if (result.status === "success") {
    // logged in
  }
}
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="email" />
    <button type="submit">Log in</button>
  </form>
</template>

This exact pattern — live-verified end to end in a real Vite+Vue app against the real hosted API: real sendOtp()/verifyOtp() round trip, real email, real code, real session, zero console warnings or errors.

Checked rather than assumed: Vue has no equivalent to React's StrictMode double-invoking effects in development. onMounted fired exactly once on a real page load in this verification, and resumeSession() resolved exactly once — no double-firing, no state surprises to design around.

Not a shipped package

There's no @threshold1/vue, and there won't be — the SDK is deliberately framework-agnostic (see Core Concepts), and framework guides like this one exist to show the correct usage pattern for the one real package, not to hand out a thin wrapper around it. Everything above is example code you own and can adapt, not an API surface threshold1 maintains.

What's next

  • React — the same core pattern with React's primitives, plus the reasoning for why module scope matters there too, for different reasons.
  • Quickstart — the full real flow this pattern is built from.