Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.tech projects.
Want to start a new fullstack project?
Use the nuxt-base-starter (Frontend) together with the nest-server-starter (Backend) to initialize a complete fullstack project with this package pre-configured.
Both starters are designed to work seamlessly together and serve as reference implementations showing how to use all features in a real application.
Quick initialization:
npx @lenne.tech/cli fullstack init my-project
npm install @lenne.tech/nuxt-extensions better-auth
# Optional: For passkey support
npm install @better-auth/passkey
# Optional: For TUS file uploads
npm install tus-js-client- Better-Auth Integration - Login, 2FA, Passkey/WebAuthn support
- Cookie/JWT Dual-Mode - Automatic fallback from cookies to JWT
- TUS File Upload - Resumable uploads with pause/resume
- Transition Components - Ready-to-use Vue transition wrappers
- i18n Support - English and German translations (works without i18n too)
- Auto-imports - All composables and components are auto-imported
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@lenne.tech/nuxt-extensions'],
ltExtensions: {
// Auth configuration
auth: {
enabled: true, // Enable auth features
baseURL: '', // API base URL (empty = use Nuxt proxy)
basePath: '/iam', // Better-Auth endpoint prefix
loginPath: '/auth/login', // Login redirect path
twoFactorRedirectPath: '/auth/2fa', // 2FA redirect path
// Plugin options
enableAdmin: true, // Admin plugin
enableTwoFactor: true, // 2FA plugin
enablePasskey: true, // Passkey/WebAuthn plugin
// Interceptor options
interceptor: {
enabled: true, // 401 auto-handler
publicPaths: ['/auth/login', '/auth/register'],
},
},
// TUS upload configuration
tus: {
defaultEndpoint: '/files/upload',
defaultChunkSize: 5 * 1024 * 1024, // 5MB
},
// i18n configuration (optional)
i18n: {
autoMerge: true, // Auto-merge locales with @nuxtjs/i18n
},
},
});<script setup>
// Auth composable (auto-imported)
const {
user,
isAuthenticated,
isLoading,
signIn,
signOut,
authenticateWithPasskey,
registerPasskey,
twoFactor,
} = useLtAuth();
// Login with email/password
async function handleLogin(email: string, password: string) {
const result = await signIn.email({ email, password });
if (result.requiresTwoFactor) {
navigateTo('/auth/2fa');
}
}
// Passkey login
async function handlePasskeyLogin() {
const result = await authenticateWithPasskey();
if (result.success) {
navigateTo('/dashboard');
}
}
</script>
<template>
<div v-if="isAuthenticated">
Welcome, {{ user?.name }}!
<button @click="signOut()">Logout</button>
</div>
</template>You can extend the auth client with additional Better Auth plugins:
Option 1: Plugin Registration (recommended)
Create a Nuxt plugin to register plugins before the auth client is initialized:
// plugins/auth-plugins.client.ts
import { registerLtAuthPlugins } from '@lenne.tech/nuxt-extensions/lib';
import { organizationClient, magicLinkClient } from 'better-auth/client/plugins';
export default defineNuxtPlugin(() => {
registerLtAuthPlugins([
organizationClient(),
magicLinkClient(),
]);
});Note: In Vue components,
registerLtAuthPluginsis auto-imported. In.tsfiles (like Nuxt plugins), import from@lenne.tech/nuxt-extensions/lib.
Option 2: Direct Factory Usage
For full control, create the auth client directly with your plugins:
import { createLtAuthClient } from '@lenne.tech/nuxt-extensions/lib';
import { organizationClient } from 'better-auth/client/plugins';
const authClient = createLtAuthClient({
plugins: [organizationClient()],
});
// Use authClient.organization.* methodsAvailable Better Auth Plugins:
organizationClient- Organization/team managementmagicLinkClient- Passwordless email loginoneTapClient- Google One Tap loginanonymousClient- Anonymous/guest sessions- See Better Auth Plugins for full list
<script setup>
const {
addFiles,
uploads,
totalProgress,
isUploading,
pauseUpload,
resumeUpload,
cancelUpload,
} = useLtTusUpload({
endpoint: '/api/files/upload',
onSuccess: (item) => console.log('Uploaded:', item.url),
onError: (item, error) => console.error('Failed:', error),
});
const { formatFileSize } = useLtFile();
function handleFileSelect(event: Event) {
const input = event.target as HTMLInputElement;
if (input.files) {
addFiles(Array.from(input.files));
}
}
</script>
<template>
<div>
<input type="file" multiple @change="handleFileSelect" />
<div v-for="upload in uploads" :key="upload.id">
<span>{{ upload.file.name }}</span>
<span>{{ formatFileSize(upload.progress.bytesUploaded) }} / {{ formatFileSize(upload.progress.bytesTotal) }}</span>
<span>{{ upload.progress.percentage }}%</span>
<button v-if="upload.status === 'uploading'" @click="pauseUpload(upload.id)">
Pause
</button>
<button v-if="upload.status === 'paused'" @click="resumeUpload(upload.id)">
Resume
</button>
</div>
<div v-if="isUploading">
Total Progress: {{ totalProgress.percentage }}%
</div>
</div>
</template><template>
<!-- Fade transition -->
<LtTransitionFade>
<div v-if="show">Content with fade</div>
</LtTransitionFade>
<!-- Fade with scale -->
<LtTransitionFadeScale :start-duration="200" :leave-duration="150">
<div v-if="show">Content with fade and scale</div>
</LtTransitionFadeScale>
<!-- Slide from right -->
<LtTransitionSlide>
<div v-if="show">Content slides from right</div>
</LtTransitionSlide>
<!-- Slide from bottom -->
<LtTransitionSlideBottom>
<div v-if="show">Content slides from bottom</div>
</LtTransitionSlideBottom>
<!-- Slide from left -->
<LtTransitionSlideRevert>
<div v-if="show">Content slides from left</div>
</LtTransitionSlideRevert>
</template><script setup>
const { share } = useLtShare();
async function handleShare() {
await share('Check this out!', 'Amazing content');
// Uses native share on mobile, clipboard fallback on desktop
}
</script>// Tailwind TypeScript helper
const buttonClasses = tw`bg-blue-500 hover:bg-blue-700 text-white`;
// Crypto utilities (for WebAuthn)
const hash = await ltSha256('password');
const base64 = ltArrayBufferToBase64Url(buffer);
const uint8 = ltBase64UrlToUint8Array(base64String);The package works with or without @nuxtjs/i18n:
| Setup | Language | Text Source |
|---|---|---|
| Without i18n | German | Hardcoded fallback texts |
With i18n, Locale: de |
German | From de.json |
With i18n, Locale: en |
English | From en.json |
| With i18n, other Locale | English | Fallback to en.json |
| Composable | Description |
|---|---|
useLtAuth() |
Better-Auth integration with session, passkey, 2FA |
useLtTusUpload() |
TUS protocol file uploads with pause/resume |
useLtFile() |
File utilities (size formatting, URLs) |
useLtShare() |
Web Share API with clipboard fallback |
| Component | Description |
|---|---|
<LtTransitionFade> |
Opacity fade transition |
<LtTransitionFadeScale> |
Fade with scale transition |
<LtTransitionSlide> |
Slide from right transition |
<LtTransitionSlideBottom> |
Slide from bottom transition |
<LtTransitionSlideRevert> |
Slide from left transition |
| Utility | Description |
|---|---|
tw |
Tailwind TypeScript helper |
ltSha256() |
SHA256 hash function |
ltArrayBufferToBase64Url() |
ArrayBuffer to base64url conversion |
ltBase64UrlToUint8Array() |
Base64url to Uint8Array conversion |
createLtAuthClient() |
Auth client factory for custom configuration |
registerLtAuthPlugins() |
Register custom Better Auth plugins |
| Project | Description |
|---|---|
| nuxt-base-starter | Frontend starter template (uses this package) |
| nest-server-starter | Backend starter template (Better-Auth backend) |
| @lenne.tech/nest-server | Backend framework with Better-Auth support |
| @lenne.tech/cli | CLI tool for fullstack project initialization |
+-------------------------------------------------------------+
| Your Fullstack App |
+-----------------------------+-------------------------------+
| Frontend | Backend |
| (nuxt-base-starter) | (nest-server-starter) |
+-----------------------------+-------------------------------+
| @lenne.tech/nuxt-extensions| @lenne.tech/nest-server |
| - useLtAuth() | - CoreBetterAuthModule |
| - useLtTusUpload() | - CoreFileModule |
| - <LtTransition*> | - CoreUserModule |
+-----------------------------+-------------------------------+
# Install dependencies
npm install
# Generate type stubs
npm run dev:prepare
# Develop with playground
npm run dev
# Build the module
npm run build
# Run tests
npm run test