Essentials

Examples

Real-world examples using the useTawk() composable — custom chat buttons, visitor identification, multi-widget setups, and more.

Custom Chat Button with Unread Badge

Replace the default Tawk.to widget bubble with your own custom button. Hide the native bubble and show your own UI instead.

components/ChatButton.vue
<script setup lang="ts">
const {
  isHidden,
  unreadCount,
  status,
  showWidget,
  hideWidget,
  onLoad,
  onUnreadCountChanged
} = useTawk()

const cleanupFns: Array<() => void> = []

onMounted(() => {
  // Hide the native bubble once loaded
  cleanupFns.push(
    onLoad(() => hideWidget())
  )

  // Track unread count changes
  cleanupFns.push(
    onUnreadCountChanged((count) => {
      console.log('Unread:', count)
    })
  )
})

onUnmounted(() => cleanupFns.forEach(fn => fn()))
</script>

<template>
  <button
    class="fixed bottom-6 right-6 w-14 h-14 rounded-full bg-green-500 text-white shadow-lg flex items-center justify-center"
    @click="isHidden ? showWidget() : hideWidget()"
  >
    💬
    <span
      v-if="unreadCount > 0"
      class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center"
    >
      {{ unreadCount }}
    </span>
  </button>
</template>

Identify Authenticated Users

When a user logs into your app, forward their identity to Tawk.to so agents see who they're talking to.

app.vue
<script setup lang="ts">
const { visitor, setAttributes } = useTawk()
const user = useUser() // your auth composable

watchEffect(() => {
  if (user.value) {
    // Set basic visitor info
    visitor({
      name: user.value.name,
      email: user.value.email,
      hash: user.value.tawkHash // HMAC-SHA256 from your backend
    })

    // Set extra attributes visible in the dashboard
    setAttributes({
      plan: user.value.plan,
      accountId: user.value.id
    })
  }
})
</script>
The hash field enables Tawk.to Secure Mode. Generate it server-side using HMAC-SHA256 on the visitor's email with your Tawk.to API key.

Show Agent Status

Display real-time agent availability to let users know if live chat is available before they open the widget.

components/AgentStatus.vue
<script setup lang="ts">
const { status } = useTawk()

const statusLabel = computed(() => ({
  online: 'We\'re online',
  away: 'We\'re away',
  offline: 'Leave a message'
}[status.value]))

const statusColor = computed(() => ({
  online: 'text-green-500',
  away: 'text-yellow-500',
  offline: 'text-gray-400'
}[status.value]))
</script>

<template>
  <div class="flex items-center gap-2">
    <span class="w-2 h-2 rounded-full" :class="statusColor" />
    <span>{{ statusLabel }}</span>
  </div>
</template>

Track Chat Events for Analytics

Fire custom analytics events when visitors start or end chats.

plugins/chat-analytics.client.ts
export default defineNuxtPlugin(() => {
  const { onChatStarted, onChatEnded, onOfflineSubmit } = useTawk()

  onChatStarted(() => {
    // Example: Google Analytics 4
    gtag('event', 'chat_started')
  })

  onChatEnded(() => {
    gtag('event', 'chat_ended')
  })

  onOfflineSubmit((data) => {
    gtag('event', 'offline_form_submitted', { email: data.email })
  })
})

Multi-Widget / Multi-Brand

Switch the Tawk.to widget at runtime based on the current route — useful for multi-brand or multi-tenant apps.

layouts/default.vue
<script setup lang="ts">
const { switchWidget } = useTawk()
const route = useRoute()

const widgetMap: Record<string, { propertyId: string, widgetId: string }> = {
  '/support': { propertyId: 'support-property-id', widgetId: 'support-widget' },
  '/sales':   { propertyId: 'sales-property-id',   widgetId: 'sales-widget'   }
}

watch(() => route.path, (path) => {
  const widget = widgetMap[path]
  if (widget) switchWidget(widget)
})
</script>

Hide Widget on Specific Pages

Use hideWidget / showWidget based on the current page, e.g. hide on checkout pages.

plugins/widget-visibility.client.ts
export default defineNuxtPlugin(() => {
  const { hideWidget, showWidget, onLoad } = useTawk()
  const route = useRoute()

  const hiddenRoutes = ['/checkout', '/payment']

  // Apply after widget loads
  onLoad(() => {
    if (hiddenRoutes.includes(route.path)) {
      hideWidget()
    }
  })

  // Re-evaluate on route change
  watch(() => route.path, (path) => {
    if (hiddenRoutes.includes(path)) {
      hideWidget()
    } else {
      showWidget()
    }
  })
})