Using Usertune.js with Nuxt.js for Client-Side Personalization
Complete guide to integrating client-side Usertune.js with Nuxt.js applications. Learn how to use server context for client-side personalization, SSR patterns, and performance optimization.
Technical Guide Expert
• 4 min read
Integrate client-side Usertune.js with Nuxt.js to deliver personalized content. This guide covers how to use server context for client-side personalization, SSR patterns, and performance optimization.
Installation
Start by installing the Usertune JavaScript library in your Nuxt.js project:
npm install usertune.js
This adds the official Usertune client library that's optimized for both server-side rendering and client-side hydration.
Nuxt Plugin Setup
In Nuxt.js, plugins are the recommended way to integrate third-party libraries. We'll create a client-side only plugin since Usertune.js runs in the browser.
Create a client-side plugin:
// plugins/usertune.client.js
import { Usertune } from 'usertune.js'
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const client = new Usertune({
workspace: config.public.usertuneWorkspaceId,
accessToken: config.public.usertuneAccessToken, // Optional - only for private content
timeout: 5000
})
return {
provide: {
usertune: client
}
}
})
Key points about this setup:
- The
.client.js
suffix ensures this only runs on the client side, preventing SSR issues useRuntimeConfig()
provides access to environment variables- The
provide
option makes the Usertune client available throughout your app as$usertune
- The timeout is optional but recommended for better user experience
Runtime Config
Nuxt.js uses runtime configuration to manage environment variables securely. Update your configuration file:
// nuxt.config.js
export default defineNuxtConfig({
runtimeConfig: {
public: {
usertuneWorkspaceId: process.env.NUXT_PUBLIC_USERTUNE_WORKSPACE_ID,
usertuneAccessToken: process.env.NUXT_PUBLIC_USERTUNE_ACCESS_TOKEN
}
}
})
Why use runtime config:
- Security - Keeps sensitive data out of your bundle
- Environment flexibility - Easy to switch between development, staging, and production
- Type safety - Nuxt provides TypeScript support for runtime config
- Public prefix - Variables with
public.
are safely exposed to the client side
Environment Variables
Create a .env
file in your project root to store your Usertune credentials:
# .env
NUXT_PUBLIC_USERTUNE_WORKSPACE_ID=your_workspace_id
NUXT_PUBLIC_USERTUNE_ACCESS_TOKEN=your_access_token
Important: Add .env
to your .gitignore
file to keep credentials secure. The NUXT_PUBLIC_
prefix tells Nuxt these variables are safe to expose to the client.
Composables
Nuxt.js encourages the use of composables for reusable logic. Here are the core composables you'll need:
// composables/useUsertune.js
export const useUsertune = () => {
const { $usertune } = useNuxtApp()
return $usertune
}
export const usePersonalizedContent = async (contentSlug, attributes = {}) => {
const { $usertune } = useNuxtApp()
const { data: content, pending: loading, error } = await useLazyFetch(
`usertune-content-${contentSlug}`,
async () => {
const response = await $usertune.content(contentSlug, attributes)
return response.data
},
{
default: () => null,
server: false // Only fetch on client side initially
}
)
const track = async (conversionType, conversionValue) => {
await $usertune.track(conversionType, conversionValue)
}
const refresh = async () => {
await refreshCookie(`usertune-content-${contentSlug}`)
}
return { content, loading, error, track, refresh }
}
How these composables work:
useUsertune()
- Simple wrapper to access the Usertune clientusePersonalizedContent()
- Main composable that integrates with Nuxt's data fetchinguseLazyFetch()
- Nuxt's optimized data fetching that handles caching and SSRserver: false
- Ensures personalization only happens client-side- Unique cache keys prevent conflicts between different content pieces
- Built-in error handling and loading states
Basic Page Usage
Here's how to use personalized content in a Nuxt page with proper SSR handling:
<!-- pages/index.vue -->
<template>
<div>
<div v-if="pending">Loading personalized content...</div>
<div v-else-if="error">
<!-- Fallback content -->
<h1>Welcome to Our Site</h1>
<p>Discover amazing features tailored for you.</p>
</div>
<div v-else-if="heroContent">
<h1>{{ heroContent.title }}</h1>
<p>{{ heroContent.description }}</p>
<button @click="handleCTAClick">{{ heroContent.ctaText }}</button>
</div>
</div>
</template>
<script setup>
const serverContext = useServerContext()
const { content: heroContent, pending, error, track } = await usePersonalizedContent('hero-banner', {
...serverContext,
page: 'home'
})
const handleCTAClick = async () => {
await track('hero_cta_click')
// Navigate or perform action
await navigateTo('/features')
}
// SEO with personalized content
useHead({
title: computed(() => heroContent.value?.seoTitle || 'Welcome - Your Site'),
meta: [
{
name: 'description',
content: computed(() => heroContent.value?.seoDescription || 'Default description')
}
]
})
</script>
Key features of this implementation:
- Progressive enhancement - Server renders fallback content, client adds personalization
- Error boundaries - Graceful fallback if personalization fails
- Loading states - Users see immediate feedback
- SEO optimization - Personalized content can update meta tags
- Tracking integration - Easy conversion tracking on user actions
- Nuxt navigation - Uses
navigateTo()
for SPA-style navigation
This approach ensures your site works well for SEO, performs fast, and provides personalized experiences without sacrificing user experience or search engine optimization.
Best Practices
- Use server context for client-side personalization - Pass server data to inform client-side Usertune calls
- Implement fallbacks - Always provide default content for SSR
- Cache on client side - Reduce API calls with client-side caching
- Batch requests - Minimize API calls with batch loading using
contentSlug
- Track everything - Measure personalization effectiveness with
client.track()
- Test thoroughly - Mock Usertune responses in tests
- Access token is optional - Only needed for private/personalized content
Ready to personalize your Nuxt.js app with client-side Usertune? Sign up for Usertune and start delivering personalized experiences!