Vue.js integration: reusable components
This page provides production-ready wrapper components for our SDK. For the underlying patterns these components are built on, see Component patterns.
Reusable wrapper components
For production apps, create reusable wrapper components that encapsulate SDK logic. This approach provides a cleaner API, reduces boilerplate, and makes components easier to test and maintain.
Available wrapper components
The following wrapper components cover all Weavr SDK integration patterns:
Input + tokenizeTokenize Replace a card's primary account number (PAN) with a unique digital token that stands in for the real card during a transaction. When a cardholder adds a card to Apple Pay or Google Pay via push provisioning, the wallet provider stores a device-specific token rather than the underlying PAN, so the real card number isn't exposed on the device or shared with merchants. pattern:
| Component | Purpose |
|---|---|
WeavrPasswordInput | Password input with tokenizeTokenize Replace a card's primary account number (PAN) with a unique digital token that stands in for the real card during a transaction. When a cardholder adds a card to Apple Pay or Google Pay via push provisioning, the wallet provider stores a device-specific token rather than the underlying PAN, so the real card number isn't exposed on the device or shared with merchants. |
WeavrPasscodeInput | Passcode input (4-digit) with tokenizeTokenize Replace a card's primary account number (PAN) with a unique digital token that stands in for the real card during a transaction. When a cardholder adds a card to Apple Pay or Google Pay via push provisioning, the wallet provider stores a device-specific token rather than the underlying PAN, so the real card number isn't exposed on the device or shared with merchants. |
WeavrConfirmPassword | Password + confirm password pair |
Authenticated display pattern:
| Component | Purpose |
|---|---|
WeavrCardNumber | Display card number (requires auth) |
WeavrCvv | Display CVVCVV Card Verification Value - the 3-digit security code printed on a payment card, used to authenticate card-not-present transactions. Weavr returns CVV in tokenized form on `GET /managed_cards/{id}` (with a stepped-up token); the value is only detokenized inside the SDK's secure CVV display component. (requires auth) |
WeavrCardPin | Display card PINPIN Personal Identification Number - the numeric code a cardholder enters to authorize chip-and-PIN purchases and ATM withdrawals. PIN is only present on physical managed cards. Weavr returns it tokenized on `GET /managed_cards/{id}` (with a stepped-up token), and the SDK detokenizes it inside a secure PIN display component. (requires auth) |
Authenticated input pattern:
| Component | Purpose |
|---|---|
WeavrCardPinCapture | Capture/set card PINPIN Personal Identification Number - the numeric code a cardholder enters to authorize chip-and-PIN purchases and ATM withdrawals. PIN is only present on physical managed cards. Weavr returns it tokenized on `GET /managed_cards/{id}` (with a stepped-up token), and the SDK detokenizes it inside a secure PIN display component. (requires auth) |
Full-screen verification pattern:
| Component | Purpose |
|---|---|
WeavrKyc | Consumer KYCKYC Know Your Customer - the identity verification process for consumer identities. This process allows you to seamlessly and securely verify your user's identity. Weavr will ask users to submit the necessary information and documentation so that they can get approved by financial providers. verification (requires auth) |
WeavrKyb | Corporate KYBKYB Know Your Business - the identity verification process for corporate identities. This process allows you to seamlessly and securely verify your business customer's identity. Weavr will ask users to submit the necessary information and documentation so that they can get approved by financial providers. verification (requires auth) |
WeavrDirectorKyc | Stakeholder due diligence (directors, UBOs, no auth) |
Provider:
| Component | Purpose |
|---|---|
WeavrAuthProvider | Authentication context provider |
All authenticated components can be used standalone (pass authToken prop) or within a WeavrAuthProvider which handles authentication once for all children.
Recommended directory structure
Organize wrapper components by their integration pattern:
src/components/weavr/
├── input-tokenize/
│ ├── WeavrPasswordInput.vue
│ ├── WeavrPasscodeInput.vue
│ ├── WeavrConfirmPassword.vue
│ └── index.ts
├── authenticated-display/
│ ├── WeavrCardNumber.vue
│ ├── WeavrCvv.vue
│ ├── WeavrCardPin.vue
│ └── index.ts
├── authenticated-input/
│ ├── WeavrCardPinCapture.vue
│ └── index.ts
├── verification/
│ ├── WeavrKyc.vue
│ ├── WeavrKyb.vue
│ ├── WeavrDirectorKyc.vue
│ └── index.ts
├── provider/
│ ├── WeavrAuthProvider.vue
│ └── index.ts
├── composables/
│ ├── useWeavrAuth.ts
│ └── index.ts
└── index.ts
The root index.ts re-exports all components for convenient imports:
// src/components/weavr/index.ts
export * from "./input-tokenize";
export * from "./authenticated-display";
export * from "./authenticated-input";
export * from "./verification";
export * from "./provider";
export * from "./composables";
Password input wrapper
<!-- src/components/weavr/WeavrPasswordInput.vue -->
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
const props = withDefaults(
defineProps<{
name?: string;
placeholder?: string;
maxlength?: number;
disabled?: boolean;
style?: Record<string, any>;
}>(),
{
name: "password",
placeholder: "Enter password",
maxlength: 50,
disabled: false,
}
);
const emit = defineEmits<{
(e: "ready"): void;
(e: "change", isValid: boolean): void;
(e: "submit"): void;
(e: "error", error: Error): void;
}>();
const containerRef = ref<HTMLElement | null>(null);
const isReady = ref(false);
const hasError = ref(false);
let form: any = null;
let input: any = null;
// Expose tokenize method to parent
const tokenize = (): Promise<string> => {
return new Promise((resolve, reject) => {
if (!form) {
reject(new Error("Form not initialized"));
return;
}
form.tokenize((tokens: Record<string, string>) => {
const token = tokens[props.name];
if (token) {
resolve(token);
} else {
reject(
new Error("No token received - input may be empty or invalid")
);
}
});
});
};
defineExpose({ tokenize });
onMounted(() => {
if (!window.OpcUxSecureClient) {
hasError.value = true;
emit("error", new Error("Weavr SDK not loaded"));
return;
}
try {
form = window.OpcUxSecureClient.form();
input = form.input(props.name, "password", {
placeholder: props.placeholder,
maxlength: props.maxlength,
disabled: props.disabled,
style: props.style,
});
input.on("ready", () => {
isReady.value = true;
emit("ready");
});
input.on("change", (event: any) => {
emit("change", event?.valid ?? false);
});
input.on("submit", () => {
emit("submit");
});
input.mount(containerRef.value);
} catch (e) {
hasError.value = true;
emit("error", e instanceof Error ? e : new Error("Unknown error"));
}
});
onUnmounted(() => {
try {
if (input && typeof input.unmount === "function") {
input.unmount();
}
} catch (e) {
console.error("Error unmounting WeavrPasswordInput:", e);
}
});
</script>
<template>
<div class="weavr-password-input">
<div v-if="hasError" class="weavr-error">Failed to load secure input</div>
<div v-else ref="containerRef" class="weavr-input-container"></div>
</div>
</template>
<style scoped>
.weavr-input-container {
min-height: 40px;
}
</style>
Usage:
<script setup lang="ts">
import { ref } from "vue";
import WeavrPasswordInput from "@/components/weavr/WeavrPasswordInput.vue";
const passwordInput = ref<InstanceType<typeof WeavrPasswordInput> | null>(
null
);
const isValid = ref(false);
const handleSubmit = async () => {
try {
const token = await passwordInput.value?.tokenize();
console.log("Password token:", token);
// Send token to your server
} catch (e) {
console.error("Tokenization failed:", e);
}
};
</script>
<template>
<form @submit.prevent="handleSubmit">
<WeavrPasswordInput
ref="passwordInput"
placeholder="Enter your password"
@change="isValid = $event"
@submit="handleSubmit"
/>
<button type="submit" :disabled="!isValid">Login</button>
</form>
</template>
Card number display wrapper
<!-- src/components/weavr/WeavrCardNumber.vue -->
<script setup lang="ts">
import { ref, onMounted, onUnmounted, inject } from "vue";
const props = defineProps<{
token: string;
style?: Record<string, any>;
}>();
const emit = defineEmits<{
(e: "ready"): void;
(e: "error", error: Error): void;
}>();
// Inject auth state from provider (if available)
const weavrAuth = inject<{ isAuthenticated: boolean } | null>(
"weavrAuth",
null
);
const containerRef = ref<HTMLElement | null>(null);
const isLoading = ref(true);
const error = ref<string | null>(null);
let span: any = null;
const mountSpan = () => {
try {
span = window.OpcUxSecureClient.span("cardNumber", props.token, {
style: props.style || {
fontSize: "16px",
fontFamily: "monospace",
letterSpacing: "2px",
},
});
span.mount(containerRef.value);
isLoading.value = false;
emit("ready");
} catch (e) {
error.value = "Failed to display card number";
emit("error", e instanceof Error ? e : new Error("Unknown error"));
}
};
onMounted(() => {
if (!window.OpcUxSecureClient) {
error.value = "SDK not loaded";
emit("error", new Error("Weavr SDK not loaded"));
return;
}
// Mount the span. When used standalone, the caller is responsible
// for having already called associate(); when used inside
// WeavrAuthProvider, the provider handles that for you.
mountSpan();
});
onUnmounted(() => {
try {
if (span && typeof span.unmount === "function") {
span.unmount();
}
} catch (e) {
console.error("Error unmounting WeavrCardNumber:", e);
}
});
</script>
<template>
<div class="weavr-card-number">
<span v-if="isLoading" class="loading">Loading...</span>
<span v-else-if="error" class="error">{{ error }}</span>
<span v-else ref="containerRef"></span>
</div>
</template>
CVV display wrapper
<!-- src/components/weavr/WeavrCvv.vue -->
<script setup lang="ts">
import { ref, onMounted, onUnmounted, inject } from "vue";
const props = defineProps<{
token: string;
style?: Record<string, any>;
}>();
const emit = defineEmits<{
(e: "ready"): void;
(e: "error", error: Error): void;
}>();
const weavrAuth = inject<{ isAuthenticated: boolean } | null>(
"weavrAuth",
null
);
const containerRef = ref<HTMLElement | null>(null);
const isLoading = ref(true);
const error = ref<string | null>(null);
let span: any = null;
onMounted(() => {
if (!window.OpcUxSecureClient) {
error.value = "SDK not loaded";
emit("error", new Error("Weavr SDK not loaded"));
return;
}
try {
span = window.OpcUxSecureClient.span("cvv", props.token, {
style: props.style || { fontSize: "16px", fontFamily: "monospace" },
});
span.mount(containerRef.value);
isLoading.value = false;
emit("ready");
} catch (e) {
error.value = "Failed to display CVV";
emit("error", e instanceof Error ? e : new Error("Unknown error"));
}
});
onUnmounted(() => {
try {
if (span && typeof span.unmount === "function") {
span.unmount();
}
} catch (e) {
console.error("Error unmounting WeavrCvv:", e);
}
});
</script>
<template>
<div class="weavr-cvv">
<span v-if="isLoading" class="loading">...</span>
<span v-else-if="error" class="error">{{ error }}</span>
<span v-else ref="containerRef"></span>
</div>
</template>
Passcode input wrapper
For 4-digit passcode inputs (used in registration/login flows):
<!-- src/components/weavr/WeavrPasscodeInput.vue -->
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
const props = withDefaults(
defineProps<{
name?: string;
placeholder?: string;
maxlength?: number;
disabled?: boolean;
style?: Record<string, any>;
}>(),
{
name: "passcode",
placeholder: "Enter 4-digit passcode",
maxlength: 4,
disabled: false,
}
);
const emit = defineEmits<{
(e: "ready"): void;
(e: "change", isValid: boolean): void;
(e: "submit"): void;
(e: "error", error: Error): void;
}>();
const containerRef = ref<HTMLElement | null>(null);
const hasError = ref(false);
let form: any = null;
let input: any = null;
const tokenize = (): Promise<string> => {
return new Promise((resolve, reject) => {
if (!form) {
reject(new Error("Form not initialized"));
return;
}
form.tokenize((tokens: Record<string, string>) => {
const token = tokens[props.name];
if (token) {
resolve(token);
} else {
reject(new Error("No token received"));
}
});
});
};
defineExpose({ tokenize });
onMounted(() => {
if (!window.OpcUxSecureClient) {
hasError.value = true;
emit("error", new Error("Weavr SDK not loaded"));
return;
}
try {
form = window.OpcUxSecureClient.form();
input = form.input(props.name, "passCode", {
placeholder: props.placeholder,
maxlength: props.maxlength,
disabled: props.disabled,
style: props.style || {
base: {
fontSize: "20px",
letterSpacing: "8px",
textAlign: "center",
fontFamily: "monospace",
},
},
});
input.on("ready", () => emit("ready"));
input.on("change", (event: any) => emit("change", event?.valid ?? false));
input.on("submit", () => emit("submit"));
input.mount(containerRef.value);
} catch (e) {
hasError.value = true;
emit("error", e instanceof Error ? e : new Error("Unknown error"));
}
});
onUnmounted(() => {
try {
if (input && typeof input.unmount === "function") {
input.unmount();
}
} catch (e) {
console.error("Error unmounting:", e);
}
});
</script>
<template>
<div class="weavr-passcode-input">
<div v-if="hasError" class="weavr-error">Failed to load secure input</div>
<div v-else ref="containerRef" class="weavr-input-container"></div>
</div>
</template>
Card PIN display wrapper
For displaying card PINPIN Personal Identification Number - the numeric code a cardholder enters to authorize chip-and-PIN purchases and ATM withdrawals. PIN is only present on physical managed cards. Weavr returns it tokenized on `GET /managed_cards/{id}` (with a stepped-up token), and the SDK detokenizes it inside a secure PIN display component. (requires authentication):
<!-- src/components/weavr/WeavrCardPin.vue -->
<script setup lang="ts">
import { ref, onMounted, onUnmounted, inject } from "vue";
const props = defineProps<{
token: string;
style?: Record<string, any>;
}>();
const emit = defineEmits<{
(e: "ready"): void;
(e: "error", error: Error): void;
}>();
const weavrAuth = inject<{ isAuthenticated: boolean } | null>(
"weavrAuth",
null
);
const containerRef = ref<HTMLElement | null>(null);
const isLoading = ref(true);
const error = ref<string | null>(null);
let span: any = null;
onMounted(() => {
if (!window.OpcUxSecureClient) {
error.value = "SDK not loaded";
emit("error", new Error("Weavr SDK not loaded"));
return;
}
try {
span = window.OpcUxSecureClient.span("cardPin", props.token, {
style: props.style || {
fontSize: "16px",
fontFamily: "monospace",
letterSpacing: "4px",
},
});
span.mount(containerRef.value);
isLoading.value = false;
emit("ready");
} catch (e) {
error.value = "Failed to display PIN";
emit("error", e instanceof Error ? e : new Error("Unknown error"));
}
});
onUnmounted(() => {
try {
if (span && typeof span.unmount === "function") {
span.unmount();
}
} catch (e) {
console.error("Error unmounting:", e);
}
});
</script>
<template>
<div class="weavr-card-pin">
<span v-if="isLoading" class="loading">****</span>
<span v-else-if="error" class="error">{{ error }}</span>
<span v-else ref="containerRef"></span>
</div>
</template>
Consumer KYC wrapper
Full-screen identity verification for consumersConsumers Individual persons who can be onboarded as identities on Weavr. Consumer identities represent individual customers and require Know Your Customer (KYC) verification. For consumers, the card owner and card assignee are typically the same person. (requires authentication):
<!-- src/components/weavr/WeavrKyc.vue -->
<script setup lang="ts">
import { ref, onMounted, inject } from "vue";
const props = withDefaults(
defineProps<{
reference: string; // From API: POST /consumers/kyc
authToken?: string; // Optional if using WeavrAuthProvider
lang?: string;
containerId?: string;
}>(),
{
lang: "en",
containerId: "weavr-kyc-container",
}
);
const emit = defineEmits<{
(e: "ready"): void;
(e: "submitted"): void;
(e: "approved"): void;
(e: "rejected"): void;
(e: "message", message: string, additionalInfo: any): void;
(e: "error", error: Error): void;
}>();
const weavrAuth = inject<{
isAuthenticated: boolean;
authToken: string;
} | null>("weavrAuth", null);
const isLoading = ref(true);
const hasError = ref(false);
const errorMessage = ref<string | null>(null);
const initializeKyc = () => {
try {
window.OpcUxSecureClient.consumer_kyc().init({
selector: `#${props.containerId}`,
reference: props.reference,
lang: props.lang,
onMessage: (message: string, additionalInfo: any) => {
emit("message", message, additionalInfo);
if (message === "kycSubmitted") emit("submitted");
else if (message === "kycApproved") emit("approved");
else if (message === "kycRejected") emit("rejected");
},
onError: (error: string) => {
hasError.value = true;
errorMessage.value = error;
emit("error", new Error(error));
},
});
isLoading.value = false;
emit("ready");
} catch (e) {
hasError.value = true;
errorMessage.value = e instanceof Error ? e.message : "Unknown error";
emit("error", e instanceof Error ? e : new Error("Unknown error"));
}
};
onMounted(() => {
if (!window.OpcUxSecureClient) {
hasError.value = true;
errorMessage.value = "Weavr SDK not loaded";
emit("error", new Error("SDK not loaded"));
return;
}
if (weavrAuth?.isAuthenticated) {
initializeKyc();
} else if (props.authToken) {
window.OpcUxSecureClient.associate(
`Bearer ${props.authToken}`,
() => initializeKyc(),
(e: Error) => {
hasError.value = true;
errorMessage.value = "Authentication failed";
emit("error", e);
}
);
} else {
hasError.value = true;
errorMessage.value = "Auth token required";
emit(
"error",
new Error(
"Auth token required - use WeavrAuthProvider or pass authToken prop"
)
);
}
});
</script>
<template>
<div class="weavr-kyc">
<div v-if="isLoading" class="kyc-loading">
<slot name="loading">Loading verification...</slot>
</div>
<div v-if="hasError" class="kyc-error">
<slot name="error" :error="errorMessage">{{ errorMessage }}</slot>
</div>
<div :id="containerId" :class="{ hidden: isLoading || hasError }"></div>
</div>
</template>
<style scoped>
.weavr-kyc {
min-height: 500px;
}
.kyc-loading {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
}
.kyc-error {
padding: 1rem;
color: #dc3545;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
border-radius: 4px;
}
.hidden {
display: none;
}
</style>
Stakeholder due diligence wrapper
Personal verification for directors, UBOs, and other stakeholders accessed via email link. Doesn't require authentication:
<!-- src/components/weavr/WeavrDirectorKyc.vue -->
<script setup lang="ts">
import { ref, onMounted } from "vue";
const props = withDefaults(
defineProps<{
reference: string; // From email link
lang?: string;
containerId?: string;
}>(),
{
lang: "en",
containerId: "weavr-director-kyc-container",
}
);
const emit = defineEmits<{
(e: "ready"): void;
(e: "submitted"): void;
(e: "approved"): void;
(e: "rejected"): void;
(e: "message", message: string, additionalInfo: any): void;
(e: "error", error: Error): void;
}>();
const isLoading = ref(true);
const hasError = ref(false);
const errorMessage = ref<string | null>(null);
onMounted(() => {
if (!window.OpcUxSecureClient) {
hasError.value = true;
errorMessage.value = "Weavr SDK not loaded";
emit("error", new Error("SDK not loaded"));
return;
}
try {
// Stakeholder due diligence doesn't require associate()
window.OpcUxSecureClient.kyc().init(
`#${props.containerId}`,
{ reference: props.reference },
(message: string, additionalInfo: any) => {
emit("message", message, additionalInfo);
if (message === "kycSubmitted") emit("submitted");
else if (message === "kycApproved") emit("approved");
else if (message === "kycRejected") emit("rejected");
else if (message === "error") {
hasError.value = true;
errorMessage.value = additionalInfo;
emit("error", new Error(additionalInfo));
}
},
{ lang: props.lang }
);
isLoading.value = false;
emit("ready");
} catch (e) {
hasError.value = true;
errorMessage.value = e instanceof Error ? e.message : "Unknown error";
emit("error", e instanceof Error ? e : new Error("Unknown error"));
}
});
</script>
<template>
<div class="weavr-director-kyc">
<div v-if="isLoading" class="kyc-loading">
<slot name="loading">Loading verification...</slot>
</div>
<div v-if="hasError" class="kyc-error">
<slot name="error" :error="errorMessage">{{ errorMessage }}</slot>
</div>
<div :id="containerId" :class="{ hidden: isLoading || hasError }"></div>
</div>
</template>
Unlike other authenticated components, WeavrDirectorKyc does not require authentication via associate(). Each stakeholder receives a verification link via email containing the reference token.
Authentication provider pattern
For pages displaying multiple authenticated components (card details, PINPIN Personal Identification Number - the numeric code a cardholder enters to authorize chip-and-PIN purchases and ATM withdrawals. PIN is only present on physical managed cards. Weavr returns it tokenized on `GET /managed_cards/{id}` (with a stepped-up token), and the SDK detokenizes it inside a secure PIN display component., etc.), use a provider component to handle authentication once and share the auth state with all children.
Authentication provider
<!-- src/components/weavr/WeavrAuthProvider.vue -->
<script setup lang="ts">
import { ref, provide, onMounted, watch } from "vue";
const props = defineProps<{
authToken: string;
}>();
const emit = defineEmits<{
(e: "authenticated"): void;
(e: "error", error: Error): void;
}>();
const isAuthenticated = ref(false);
const isLoading = ref(true);
const error = ref<string | null>(null);
// Provide auth state to all descendant components
provide("weavrAuth", {
isAuthenticated,
authToken: props.authToken,
});
const authenticate = () => {
if (!window.OpcUxSecureClient) {
error.value = "Weavr SDK not loaded";
isLoading.value = false;
emit("error", new Error("SDK not loaded"));
return;
}
if (!props.authToken) {
error.value = "Auth token is required";
isLoading.value = false;
emit("error", new Error("Auth token is required"));
return;
}
window.OpcUxSecureClient.associate(
`Bearer ${props.authToken}`,
() => {
isAuthenticated.value = true;
isLoading.value = false;
emit("authenticated");
},
(e: Error) => {
error.value = "Authentication failed";
isLoading.value = false;
emit("error", e);
}
);
};
onMounted(() => {
authenticate();
});
// Re-authenticate if token changes
watch(
() => props.authToken,
(newToken, oldToken) => {
if (newToken && newToken !== oldToken) {
isAuthenticated.value = false;
isLoading.value = true;
error.value = null;
authenticate();
}
}
);
</script>
<template>
<div class="weavr-auth-provider">
<div v-if="isLoading" class="auth-loading">
<slot name="loading">Authenticating...</slot>
</div>
<div v-else-if="error" class="auth-error">
<slot name="error" :error="error">{{ error }}</slot>
</div>
<slot v-else></slot>
</div>
</template>
Using the provider with card components
<script setup lang="ts">
import { ref, onMounted } from "vue";
import WeavrAuthProvider from "@/components/weavr/WeavrAuthProvider.vue";
import WeavrCardNumber from "@/components/weavr/WeavrCardNumber.vue";
import WeavrCvv from "@/components/weavr/WeavrCvv.vue";
const authToken = ref("");
const cardNumberToken = ref("");
const cvvToken = ref("");
onMounted(async () => {
// Fetch tokens from your API
const response = await fetch("/api/card/tokens");
const data = await response.json();
authToken.value = data.authToken;
cardNumberToken.value = data.cardNumberToken;
cvvToken.value = data.cvvToken;
});
</script>
<template>
<WeavrAuthProvider
v-if="authToken"
:auth-token="authToken"
@error="console.error('Auth failed:', $event)"
>
<template #loading>
<div class="skeleton">Loading card details...</div>
</template>
<template #error="{ error }">
<div class="error-message">
Failed to authenticate: {{ error }}
<button @click="$router.push('/login')">Re-login</button>
</div>
</template>
<div class="card-details">
<div class="field">
<label>Card Number</label>
<WeavrCardNumber :token="cardNumberToken" />
</div>
<div class="field">
<label>CVV</label>
<WeavrCvv :token="cvvToken" />
</div>
</div>
</WeavrAuthProvider>
</template>
Composable for consuming auth context
Create a composable to access auth state in deeply nested components:
// src/composables/useWeavrAuth.ts
import { inject, computed } from "vue";
interface WeavrAuthContext {
isAuthenticated: boolean;
authToken: string;
}
export function useWeavrAuth() {
const context = inject<WeavrAuthContext | null>("weavrAuth", null);
if (!context) {
console.warn(
"useWeavrAuth must be used within a WeavrAuthProvider component"
);
}
return {
isAuthenticated: computed(() => context?.isAuthenticated ?? false),
authToken: computed(() => context?.authToken ?? ""),
hasProvider: computed(() => context !== null),
};
}
Usage in a nested component:
<script setup lang="ts">
import { useWeavrAuth } from "@/composables/useWeavrAuth";
const { isAuthenticated, hasProvider } = useWeavrAuth();
// Component can check if it's within a provider
if (!hasProvider.value) {
console.warn("This component should be used within WeavrAuthProvider");
}
</script>
Full example with wrapper components
Here's a complete card details page using wrapper components and the provider:
<!-- src/views/CardDetailsView.vue -->
<script setup lang="ts">
import { ref, onMounted } from "vue";
import WeavrAuthProvider from "@/components/weavr/WeavrAuthProvider.vue";
import WeavrCardNumber from "@/components/weavr/WeavrCardNumber.vue";
import WeavrCvv from "@/components/weavr/WeavrCvv.vue";
interface CardData {
authToken: string;
cardNumberToken: string;
cvvToken: string;
cardholderName: string;
expiryDate: string;
}
const cardData = ref<CardData | null>(null);
const isLoading = ref(true);
const fetchError = ref<string | null>(null);
onMounted(async () => {
try {
const response = await fetch("/api/cards/current");
if (!response.ok) throw new Error("Failed to fetch card data");
cardData.value = await response.json();
} catch (e) {
fetchError.value = e instanceof Error ? e.message : "Unknown error";
} finally {
isLoading.value = false;
}
});
</script>
<template>
<div class="card-details-view">
<h1>Card Details</h1>
<div v-if="isLoading" class="loading">Loading card information...</div>
<div v-else-if="fetchError" class="error">{{ fetchError }}</div>
<WeavrAuthProvider
v-else-if="cardData"
:auth-token="cardData.authToken"
@authenticated="console.log('Ready to display card')"
@error="console.error('Auth error:', $event)"
>
<template #loading>
<div class="card-skeleton">
<div class="skeleton-line"></div>
<div class="skeleton-line short"></div>
</div>
</template>
<div class="card-container">
<div class="card-front">
<div class="card-number-row">
<WeavrCardNumber :token="cardData.cardNumberToken" />
</div>
<div class="card-info-row">
<div class="cardholder">
<label>Cardholder</label>
<span>{{ cardData.cardholderName }}</span>
</div>
<div class="expiry">
<label>Expires</label>
<span>{{ cardData.expiryDate }}</span>
</div>
<div class="cvv">
<label>CVV</label>
<WeavrCvv :token="cardData.cvvToken" />
</div>
</div>
</div>
</div>
</WeavrAuthProvider>
</div>
</template>
<style scoped>
.card-container {
max-width: 400px;
margin: 2rem auto;
}
.card-front {
background: linear-gradient(135deg, #1a1a2e, #16213e);
color: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.card-number-row {
font-size: 1.5rem;
letter-spacing: 2px;
margin-bottom: 2rem;
}
.card-info-row {
display: flex;
gap: 2rem;
}
.card-info-row label {
display: block;
font-size: 0.75rem;
text-transform: uppercase;
opacity: 0.7;
margin-bottom: 0.25rem;
}
</style>
Next steps
- Troubleshooting-Error handling, common pitfalls, and solutions