DEV Community

Melle Wynia
Melle Wynia

Posted on • Updated on

Quick guide to add websocket to Nuxt 3

I found it quite challenging to add websockets to Nuxt 3. The docs on how Nuxt 3 is glued together with Nitro, h3 and other services cannot always be easy to follow and these are documented separately. Here's a guide.

Our use case is simple. Once we update a resource (via an Nuxt 3 ~/server/api/*.post.ts call), a message needs to be sent to all listeners of that resource. Simply put, when a user updates a project item, all other users see the updates as well. Immediately.

Good to know before you start: it is good to keep in mind that this implementation will (likely) not work in a serverless setup. Mine is hosted as one instance, at Digital Ocean apps platform. That works fine.

Receiving messages in backend

First make sure you install package.

Then add a new Nuxt middleware layer. This allows us to initiate the logic and then wire an object with methods which allows us to send messages in the Nuxt api (located ~/server/api) via the event.context.

Note: This is a simplified example with console.logs to help you see how it should work (used versions: Nuxt ^3.8.0 and^4.7.2). Right now it does a global emit, make sure to add the appropriate auth before running in prod apps.

// ~/server/middleware/socket.middleware.ts

import { Socket, Server } from ''

let appSocket = {
  emit: (channel: string, message: string) => {
    console.log('Not initiated yet', channel, message)

export default defineEventHandler((event) => {
  event.context.appSocket = appSocket

  if ( return
  console.log('Initiating socket.middleware')

  const node = event.node = new Server(node.res.socket?.server)'connection', (socket: Socket) => {
    socket.emit('message-channel', `welcome ${}`)

    listeners.push({ channel: 'message', socket })

    appSocket.emit = (channel, message) => {, message)

    socket.on('message', (data) => {
      console.log('Relaying again for funsies: ', data)'message-channel', 'Hello, client! ' + data)

    socket.on('disconnect', () => {
      // Put optional disconnect logic here
Enter fullscreen mode Exit fullscreen mode

Sending messages from backend api

This snipplet contains all logic to connect websocket in the frontend and receive and send messages. Please note how we need to use the channel name 'message-channel'. Once you put this in your own app, replace it with more appropriate names resembling a resource, or actions on a resource.

// ~/server/api/

export default defineEventHandler(
  async (event) => {
    const body = await readBody(event)

    // Make sure to do this in a safe way
    event.context?.appSocket.emit('message-channel', body.value)
    return body.value
Enter fullscreen mode Exit fullscreen mode

Receiving and sending messages in frontend

First step is to create a socket instance on β€˜onMounted’ and start listening to the channel message-channel. There are two ways to send messages. You can utilize the websocket or call an api method, a more traditional flow.

// ~/pages/test-nuxt3-websocket.vue

<script setup lang="ts">
import io from ''
import type { Socket } from ''

const formMessage = ref('there you go')
const state = reactive({messages: []})

let socket: Socket | undefined

const sendMessage = () => {
  socket?.emit('message-channel', formMessage.value)

const sendMessageViaApi = async () => {
  socket?.emit('message-channel', formMessage.value)
  await $fetch('/api/test-message', {
    method: 'POST',
    body: { message: formMessage.value },

onMounted(async () => {
  socket = io(`${location.protocol === 'https:' ? 'wss://' : 'ws://' }${}`)

  socket.on('message-channel', (messageValue: string) => {
    try {
      console.log('Frontend received: ', messageValue)
    } catch (e) { console.error(e) }

onUnmounted(() => {

      <li v-for="message of state.messages" :key="message">
        {{ message}}
    <form @submit.prevent="sendMessage">
      <input v-model="formMessage" />
      <button>Send message</button>
      <button type="button" @click="sendMessageViaApi()">Send via api</button>
Enter fullscreen mode Exit fullscreen mode


Hopefully this guide gets you up to speed quickly for using Websockets in Nuxt 3. Best. - M

Top comments (3)

insanity54 profile image
chris grimmett • Edited

Thank you for this guide! Unfortunately I'm having trouble following.

[nitro] [uncaughtException] ReferenceError: listeners is not defined

Is there an example code repo that I could see? Thanks again.

shroomlife profile image
shroomlife πŸ„

Not working with TypeScript


  • Property 'server' does not exist on type 'Socket'.
ikudev profile image

Thanks a lot for sharing such a helpful post. It would be better to make it a nuxt module?