Technical Guide

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.

JO

Technical Guide Expert

• 4 min read

Nuxt.js Vue.js Usertune SSR Personalization

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 client
  • usePersonalizedContent() - Main composable that integrates with Nuxt's data fetching
  • useLazyFetch() - Nuxt's optimized data fetching that handles caching and SSR
  • server: 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

  1. Use server context for client-side personalization - Pass server data to inform client-side Usertune calls
  2. Implement fallbacks - Always provide default content for SSR
  3. Cache on client side - Reduce API calls with client-side caching
  4. Batch requests - Minimize API calls with batch loading using contentSlug
  5. Track everything - Measure personalization effectiveness with client.track()
  6. Test thoroughly - Mock Usertune responses in tests
  7. 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!