WebSockets and Real-time Communication
Enable real-time features in your Nuxt application with WebSockets for live updates, notifications, and collaborative features.
Why WebSockets?
- ✅ Bi-directional communication between client and server
- ✅ Real-time data updates without polling
- ✅ Lower latency than HTTP polling
- ✅ Perfect for chat, notifications, live dashboards
WebSocket Setup in Nuxt
Install Dependencies
npm install ws socket.io socket.io-client
Server-side WebSocket (Nitro)
// server/plugins/websocket.ts
import { Server } from 'socket.io'
import type { NitroApp } from 'nitropack'
export default defineNitroPlugin((nitroApp: NitroApp) => {
const io = new Server(3001, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST']
}
})
io.on('connection', (socket) => {
console.log('Client connected:', socket.id)
socket.on('message', (data) => {
console.log('Received:', data)
// Broadcast to all clients
io.emit('message', data)
})
socket.on('disconnect', () => {
console.log('Client disconnected:', socket.id)
})
})
nitroApp.hooks.hook('close', () => {
io.close()
})
})
Client-side Connection
<script setup lang="ts">
import { io } from 'socket.io-client'
const socket = ref<any>(null)
const messages = ref<string[]>([])
const connected = ref(false)
onMounted(() => {
socket.value = io('http://localhost:3001')
socket.value.on('connect', () => {
connected.value = true
console.log('Connected to WebSocket')
})
socket.value.on('message', (data: string) => {
messages.value.push(data)
})
socket.value.on('disconnect', () => {
connected.value = false
console.log('Disconnected from WebSocket')
})
})
onUnmounted(() => {
if (socket.value) {
socket.value.disconnect()
}
})
function sendMessage(message: string) {
if (socket.value && connected.value) {
socket.value.emit('message', message)
}
}
</script>
<template>
<div>
<div v-if="connected" class="status">Connected</div>
<div v-else class="status">Disconnected</div>
<div v-for="msg in messages" :key="msg">
{{ msg }}
</div>
</div>
</template>
Connection Management
Automatic Reconnection
// app/composables/useWebSocket.ts
import { io, Socket } from 'socket.io-client'
export function useWebSocket(url: string) {
const socket = ref<Socket | null>(null)
const connected = ref(false)
const reconnectAttempts = ref(0)
const maxReconnectAttempts = 5
function connect() {
socket.value = io(url, {
reconnection: true,
reconnectionAttempts: maxReconnectAttempts,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
})
socket.value.on('connect', () => {
connected.value = true
reconnectAttempts.value = 0
console.log('WebSocket connected')
})
socket.value.on('disconnect', () => {
connected.value = false
console.log('WebSocket disconnected')
})
socket.value.on('reconnect_attempt', (attempt) => {
reconnectAttempts.value = attempt
console.log(`Reconnect attempt ${attempt}`)
})
socket.value.on('reconnect_failed', () => {
console.error('Failed to reconnect')
})
}
function disconnect() {
if (socket.value) {
socket.value.disconnect()
socket.value = null
}
}
onMounted(() => {
connect()
})
onUnmounted(() => {
disconnect()
})
return {
socket: readonly(socket),
connected: readonly(connected),
reconnectAttempts: readonly(reconnectAttempts),
connect,
disconnect
}
}
Real-time Notifications
Server-side Notification System
// server/api/notifications/send.post.ts
import { getSocketIO } from '~/server/utils/socket'
export default defineEventHandler(async (event) => {
const { userId, message, type } = await readBody(event)
const io = getSocketIO()
// Send to specific user
io.to(`user-${userId}`).emit('notification', {
message,
type,
timestamp: new Date().toISOString()
})
return { success: true }
})
Client-side Notification Handler
<script setup lang="ts">
import { useWebSocket } from '~/composables/useWebSocket'
interface Notification {
message: string
type: 'info' | 'warning' | 'error' | 'success'
timestamp: string
}
const { socket } = useWebSocket('http://localhost:3001')
const notifications = ref<Notification[]>([])
watchEffect(() => {
if (socket.value) {
socket.value.on('notification', (notification: Notification) => {
notifications.value.unshift(notification)
// Auto-remove after 5 seconds
setTimeout(() => {
const index = notifications.value.indexOf(notification)
if (index > -1) {
notifications.value.splice(index, 1)
}
}, 5000)
})
}
})
</script>
<template>
<div class="notifications">
<div
v-for="notif in notifications"
:key="notif.timestamp"
:class="['notification', notif.type]"
>
{{ notif.message }}
</div>
</div>
</template>
Live Dashboard Example
<script setup lang="ts">
import { useWebSocket } from '~/composables/useWebSocket'
interface DashboardData {
users: number
revenue: number
orders: number
}
const { socket, connected } = useWebSocket('http://localhost:3001')
const dashboardData = ref<DashboardData>({
users: 0,
revenue: 0,
orders: 0
})
watchEffect(() => {
if (socket.value) {
socket.value.on('dashboard:update', (data: DashboardData) => {
dashboardData.value = data
})
// Request initial data
socket.value.emit('dashboard:subscribe')
}
})
onUnmounted(() => {
if (socket.value) {
socket.value.emit('dashboard:unsubscribe')
}
})
</script>
<template>
<div class="dashboard">
<div v-if="!connected" class="loading">
Connecting...
</div>
<div v-else class="stats">
<div class="stat">
<h3>Active Users</h3>
<p>{{ dashboardData.users }}</p>
</div>
<div class="stat">
<h3>Revenue</h3>
<p>${{ dashboardData.revenue.toFixed(2) }}</p>
</div>
<div class="stat">
<h3>Orders</h3>
<p>{{ dashboardData.orders }}</p>
</div>
</div>
</div>
</template>
Room-based Communication
// Server-side
io.on('connection', (socket) => {
socket.on('join:room', (roomId: string) => {
socket.join(roomId)
io.to(roomId).emit('user:joined', socket.id)
})
socket.on('leave:room', (roomId: string) => {
socket.leave(roomId)
io.to(roomId).emit('user:left', socket.id)
})
socket.on('room:message', ({ roomId, message }) => {
io.to(roomId).emit('message', {
userId: socket.id,
message,
timestamp: new Date()
})
})
})
Best Practices
- ✅ Always handle disconnections gracefully
- ✅ Implement automatic reconnection with backoff
- ✅ Use rooms for targeted messaging
- ✅ Validate all WebSocket messages
- ✅ Implement authentication for WebSocket connections
- ✅ Monitor connection status in UI
- ✅ Clean up listeners on component unmount
- ✅ Use compression for large payloads
- ✅ Implement heartbeat/ping-pong for connection health
Error Handling
const { socket } = useWebSocket('http://localhost:3001')
watchEffect(() => {
if (socket.value) {
socket.value.on('connect_error', (error) => {
console.error('Connection error:', error)
// Show user-friendly message
})
socket.value.on('error', (error) => {
console.error('Socket error:', error)
})
}
})
WebSockets enable powerful real-time features. Use them wisely for features that truly benefit from real-time updates, and always consider fallback strategies for unreliable connections.