Nuxt

How do you create runtime-only composable plugins in Nuxt 4 using app/extensions?

December 4, 2025

download ready
Thank You
Your submission has been received.
We will be in touch and contact you soon!

Advanced Plugin System

Nuxt 4's app/extensions/ auto-injects client-only composables skipping SSR serialization. Perfect for WebSocket clients, IndexedDB, browser APIs without hydration issues. Extensions provide reactive state globally via useNuxtApp().

Step-by-Step Implementation

Step 1: Create Extension Directory:-

Code

app/
  extensions/
    ws-client.ts     # Client-only composable
      

Step 2: Define Extension Composable:-

Code

// app/extensions/ws-client.ts
export default defineNuxtExtension((nuxtApp) => {
  const ws = ref<WebSocket | null>(null)
  const messages = ref<Message[]>([])
  const status = ref<'disconnected' | 'connecting' | 'connected'>('disconnected')
  
  const connect = () => {
    ws.value = new WebSocket('wss://api.example.com/notifications')
    ws.value.onopen = () => status.value = 'connected'
    ws.value.onmessage = (event) => {
      messages.value.push(JSON.parse(event.data))
    }
    ws.value.onclose = () => status.value = 'disconnected'
  }
  const send = (data: any) => ws.value?.send(JSON.stringify(data))
  
  // Global access
  nuxtApp.provide('notifications', { 
    ws, messages, status, connect, send 
  })
})
      

Step 3: Use in Any Component:-

Code

<template>
  <div>
    <div v-if="$notifications.status === 'connected'" class="status connected">
      Connected ({{ $notifications.messages.length }} messages)
    </div>
    <button @click="$notifications.connect()" v-else>
      Connect Notifications
    </button>
    <div v-for="msg in $notifications.messages" :key="msg.id">
      {{ msg.content }}
    </div>
  </div>
</template>

<script setup>
// Auto-available globally
const { $notifications } = useNuxtApp()
</script>
      

Step 4: Verify Client-Only Execution:-

Code

// Only runs client-side - no SSR
if (process.client) {
  console.log('Extension loaded:', $notifications.status.value)
}
      
Hire Now!

Need Help with Nuxt Development ?

Work with our skilled nuxt developers to accelerate your project and boost its performance.
**Hire now**Hire Now**Hire Now**Hire now**Hire now

How do you create runtime-only composable plugins in Nuxt 4 using app/extensions?

Advanced Plugin System

Nuxt 4's app/extensions/ auto-injects client-only composables skipping SSR serialization. Perfect for WebSocket clients, IndexedDB, browser APIs without hydration issues. Extensions provide reactive state globally via useNuxtApp().

Step-by-Step Implementation

Step 1: Create Extension Directory:-

Code

app/
  extensions/
    ws-client.ts     # Client-only composable
      

Step 2: Define Extension Composable:-

Code

// app/extensions/ws-client.ts
export default defineNuxtExtension((nuxtApp) => {
  const ws = ref<WebSocket | null>(null)
  const messages = ref<Message[]>([])
  const status = ref<'disconnected' | 'connecting' | 'connected'>('disconnected')
  
  const connect = () => {
    ws.value = new WebSocket('wss://api.example.com/notifications')
    ws.value.onopen = () => status.value = 'connected'
    ws.value.onmessage = (event) => {
      messages.value.push(JSON.parse(event.data))
    }
    ws.value.onclose = () => status.value = 'disconnected'
  }
  const send = (data: any) => ws.value?.send(JSON.stringify(data))
  
  // Global access
  nuxtApp.provide('notifications', { 
    ws, messages, status, connect, send 
  })
})
      

Step 3: Use in Any Component:-

Code

<template>
  <div>
    <div v-if="$notifications.status === 'connected'" class="status connected">
      Connected ({{ $notifications.messages.length }} messages)
    </div>
    <button @click="$notifications.connect()" v-else>
      Connect Notifications
    </button>
    <div v-for="msg in $notifications.messages" :key="msg.id">
      {{ msg.content }}
    </div>
  </div>
</template>

<script setup>
// Auto-available globally
const { $notifications } = useNuxtApp()
</script>
      

Step 4: Verify Client-Only Execution:-

Code

// Only runs client-side - no SSR
if (process.client) {
  console.log('Extension loaded:', $notifications.status.value)
}