npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

vue-streaming

v0.1.5

Published

Vue wrapper component for js-streaming (WebSocket/SSE/HTTP/Long-Polling/HLS/WebRTC).

Readme

vue-streaming

npm version Vue 3 TypeScript License: MIT Bundle Size

🌟 Features

Vue Streaming provides a unified <StreamPlayer> component that handles 6 different streaming protocols with a consistent, declarative API:

  • 🔌 WebSocket - Real-time bidirectional communication
  • 📡 Server-Sent Events (SSE) - Server-to-client event streams
  • 🌊 HTTP Streaming - Chunked transfer encoding streams
  • 🔄 Long Polling - HTTP-based persistent connections
  • 📺 HLS Video - HTTP Live Streaming for adaptive video
  • 🎥 WebRTC - Peer-to-peer real-time communication

✨ Why Choose Vue Streaming?

  • One API, Many Protocols: Switch between streaming types with just a prop change
  • Vue 3 Native: Built for Composition API with full TypeScript support
  • Headless Design: No imposed styling - complete UI control
  • Production Ready: Auto-reconnection, error handling, and backoff strategies
  • Lightweight: Thin wrapper around js-streaming core library
  • Extensible: Custom slots and event system for any use case

📦 Installation

# npm
npm install vue-streaming js-streaming

# yarn
yarn add vue-streaming js-streaming

# pnpm
pnpm add vue-streaming js-streaming

Note: Both vue-streaming and js-streaming are required. Vue Streaming is a wrapper that leverages the core js-streaming library.

Requirements

  • Vue: 3.3.0 or higher
  • Node: 16+ (for build tools)
  • Build System: Vite, Nuxt 3, or Vue CLI with TypeScript support

🚀 Quick Start

Basic WebSocket Example

<script setup lang="ts">
import { ref } from "vue";
import { StreamPlayer } from "vue-streaming";

const player = ref<InstanceType<typeof StreamPlayer>>();
const config = {
  url: "wss://echo.websocket.events",
  protocols: ["echo-protocol"],
};

function sendMessage() {
  player.value?.send("Hello WebSocket!");
}
</script>

<template>
  <div>
    <h2>WebSocket Demo</h2>
    <StreamPlayer
      ref="player"
      type="websocket"
      :config="config"
      :auto-open="true"
      @message="console.log('Received:', $event)"
      @status="console.log('Status:', $event)"
    />
    <button @click="sendMessage">Send Message</button>
  </div>
</template>

HLS Video Streaming

<script setup lang="ts">
import { StreamPlayer } from "vue-streaming";

const hlsConfig = {
  url: "https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8",
};
</script>

<template>
  <StreamPlayer
    type="hls"
    :config="hlsConfig"
    :auto-open="true"
    :autoplay="true"
    :controls="true"
    :muted="false"
  />
</template>

Server-Sent Events

<script setup lang="ts">
import { StreamPlayer } from "vue-streaming";

const sseConfig = {
  url: "/api/events",
  headers: { Authorization: "Bearer token" },
};
</script>

<template>
  <StreamPlayer
    type="sse"
    :config="sseConfig"
    :auto-open="true"
    @message="handleServerEvent"
  />
</template>

📚 API Reference

Props

| Prop | Type | Default | Description | | ------------- | ------------ | ---------- | -------------------------------------------------------------------------- | | type | StreamType | required | Protocol type: websocket, sse, http, long-polling, hls, webrtc | | config | object | required | Protocol-specific configuration object | | autoOpen | boolean | false | Automatically open connection on mount | | autoplay | boolean | false | Auto-play for video streams (HLS/WebRTC) | | controls | boolean | true | Show video controls | | playsInline | boolean | true | Play video inline on mobile | | muted | boolean | false | Start video muted | | logLimit | number | 500 | Maximum messages to keep in memory | | videoAttrs | object | {} | Additional HTML video element attributes |

Events

| Event | Payload | Description | | ---------- | -------------- | ------------------------------------------------------------------------- | | @open | void | Connection established | | @close | void | Connection closed | | @status | StreamStatus | Status change: idle, connecting, open, closing, closed, error | | @error | Error | Error occurred | | @message | any | New message/data received |

Exposed Methods & Properties

interface StreamPlayerInstance {
  // Methods
  open(): Promise<void>;
  close(): Promise<void>;
  send(data: unknown): void; // Available for WebSocket/WebRTC

  // Reactive State
  status: Ref<StreamStatus>;
  isOpen: Ref<boolean>;
  error: Ref<Error | null>;
  messages: Ref<unknown[]>;
}

Configuration Objects

Each stream type accepts different configuration options:

WebSocket Config

{
  url: string
  protocols?: string[]
  headers?: Record<string, string>
  binaryType?: 'blob' | 'arraybuffer'
}

SSE Config

{
  url: string
  headers?: Record<string, string>
  withCredentials?: boolean
  eventSourceInitDict?: EventSourceInit
}

HTTP Streaming Config

{
  url: string
  method?: string
  headers?: Record<string, string>
  body?: any
  signal?: AbortSignal
}

Long Polling Config

{
  url: string
  interval?: number
  headers?: Record<string, string>
  timeout?: number
}

HLS Config

{
  url: string
  hlsConfig?: any  // Passed to HLS.js
}

WebRTC Config

{
  configuration?: RTCConfiguration
  attachVideo?: boolean  // Auto-attach video stream
  // Additional WebRTC-specific options
}

🎨 Customization with Slots

Custom Log Display

<template>
  <StreamPlayer type="websocket" :config="config">
    <template #log="{ messages, status, error }">
      <div class="custom-log">
        <div class="status-bar">
          Status: <span :class="status">{{ status }}</span>
        </div>
        <div v-if="error" class="error">{{ error.message }}</div>
        <div class="messages">
          <div v-for="(msg, i) in messages" :key="i" class="message">
            {{ typeof msg === "string" ? msg : JSON.stringify(msg) }}
          </div>
        </div>
      </div>
    </template>
  </StreamPlayer>
</template>

<style scoped>
.custom-log {
  border: 2px solid #007acc;
  border-radius: 8px;
  padding: 16px;
  background: #f8f9fa;
}
.status-bar {
  font-weight: bold;
}
.status.open {
  color: green;
}
.status.error {
  color: red;
}
.messages {
  max-height: 300px;
  overflow-y: auto;
}
.message {
  padding: 4px 0;
  border-bottom: 1px solid #eee;
}
</style>

Custom Action Buttons

<template>
  <StreamPlayer type="websocket" :config="config">
    <template #actions="{ open, close, send, isOpen, status }">
      <div class="action-bar">
        <button @click="open" :disabled="isOpen">Connect</button>
        <button @click="close" :disabled="!isOpen">Disconnect</button>
        <button @click="send('ping')" :disabled="!isOpen">Ping</button>
        <span class="status-indicator" :class="status">{{ status }}</span>
      </div>
    </template>
  </StreamPlayer>
</template>

🛠️ Advanced Usage

Auto-Reconnection and Error Handling

<script setup lang="ts">
import { ref } from "vue";
import { StreamPlayer } from "vue-streaming";

const config = ref({
  url: "wss://api.example.com/ws",
  // Auto-reconnection settings
  autoReconnect: true,
  maxRetries: 5,
  heartbeatMs: 30000,
  backoff: {
    baseMs: 1000,
    maxMs: 10000,
    factor: 1.5,
    jitter: true,
  },
});

function handleError(error: Error) {
  console.error("Stream error:", error);
  // Custom error handling logic
}

function handleStatusChange(status: string) {
  if (status === "error") {
    // Handle connection errors
  } else if (status === "open") {
    console.log("Successfully connected!");
  }
}
</script>

<template>
  <StreamPlayer
    type="websocket"
    :config="config"
    @error="handleError"
    @status="handleStatusChange"
  />
</template>

Dynamic Configuration

<script setup lang="ts">
import { ref, computed } from "vue";
import { StreamPlayer } from "vue-streaming";

const apiEndpoint = ref("wss://api.example.com");
const authToken = ref("");

const dynamicConfig = computed(() => ({
  url: `${apiEndpoint.value}/ws`,
  headers: {
    Authorization: `Bearer ${authToken.value}`,
  },
}));

// Configuration changes will automatically recreate the stream
</script>

<template>
  <div>
    <input v-model="apiEndpoint" placeholder="WebSocket URL" />
    <input v-model="authToken" placeholder="Auth Token" />

    <StreamPlayer
      type="websocket"
      :config="dynamicConfig"
      :auto-open="!!authToken"
    />
  </div>
</template>

Programmatic Control

<script setup lang="ts">
import { ref, onMounted } from "vue";
import { StreamPlayer } from "vue-streaming";

const player = ref<InstanceType<typeof StreamPlayer>>();

onMounted(() => {
  // Programmatic control
  setTimeout(() => {
    player.value?.open();
  }, 1000);

  // Send periodic messages
  setInterval(() => {
    if (player.value?.isOpen) {
      player.value.send({
        type: "heartbeat",
        timestamp: Date.now(),
      });
    }
  }, 10000);
});

// Access reactive state
const connectionStatus = computed(() => player.value?.status || "idle");
const messageCount = computed(() => player.value?.messages.length || 0);
</script>

🔧 TypeScript Support

Vue Streaming is fully typed. Import types for enhanced development experience:

import type {
  StreamType,
  StreamStatus,
  StreamState,
  StreamAPI,
} from "vue-streaming";

// Component instance type
import { StreamPlayer } from "vue-streaming";
type StreamPlayerInstance = InstanceType<typeof StreamPlayer>;

// Usage in composition function
function useStreamPlayer() {
  const player = ref<StreamPlayerInstance>();
  const status = computed(() => player.value?.status || "idle");

  return { player, status };
}

🌐 Nuxt 3 / SSR Usage

For server-side rendering, wrap the component to prevent hydration issues:

<template>
  <div>
    <ClientOnly>
      <StreamPlayer type="websocket" :config="config" :auto-open="true" />
      <template #fallback>
        <div>Loading stream player...</div>
      </template>
    </ClientOnly>
  </div>
</template>

Or create an async component:

<script setup lang="ts">
import { defineAsyncComponent } from "vue";

const StreamPlayer = defineAsyncComponent(() =>
  import("vue-streaming").then((m) => m.StreamPlayer)
);
</script>

📱 Real-World Examples

Chat Application

<script setup lang="ts">
import { ref } from "vue";
import { StreamPlayer } from "vue-streaming";

const message = ref("");
const chatPlayer = ref<InstanceType<typeof StreamPlayer>>();

const config = {
  url: "wss://chat.example.com/ws",
  protocols: ["chat-v1"],
};

function sendMessage() {
  if (message.value.trim()) {
    chatPlayer.value?.send({
      type: "message",
      content: message.value,
      timestamp: new Date().toISOString(),
    });
    message.value = "";
  }
}

function handleChatMessage(msg: any) {
  if (msg.type === "message") {
    // Handle incoming chat message
    console.log(`${msg.user}: ${msg.content}`);
  }
}
</script>

<template>
  <div class="chat-container">
    <StreamPlayer
      ref="chatPlayer"
      type="websocket"
      :config="config"
      :auto-open="true"
      @message="handleChatMessage"
    >
      <template #log="{ messages }">
        <div class="chat-messages">
          <div v-for="msg in messages" :key="msg.id" class="message">
            <strong>{{ msg.user }}:</strong> {{ msg.content }}
          </div>
        </div>
      </template>

      <template #actions="{ isOpen }">
        <div class="chat-input">
          <input
            v-model="message"
            @keyup.enter="sendMessage"
            :disabled="!isOpen"
            placeholder="Type a message..."
          />
          <button @click="sendMessage" :disabled="!isOpen">Send</button>
        </div>
      </template>
    </StreamPlayer>
  </div>
</template>

Live Data Dashboard

<script setup lang="ts">
import { ref, computed } from "vue";
import { StreamPlayer } from "vue-streaming";

const metrics = ref<any[]>([]);

const config = {
  url: "/api/metrics/stream",
  headers: { Accept: "text/event-stream" },
};

function handleMetricsUpdate(data: any) {
  if (data.type === "metrics") {
    metrics.value = [...metrics.value, data.payload].slice(-50); // Keep last 50
  }
}

const latestMetrics = computed(
  () => metrics.value[metrics.value.length - 1]?.payload || {}
);
</script>

<template>
  <div class="dashboard">
    <h1>Live Metrics Dashboard</h1>

    <div class="metrics-grid">
      <div class="metric-card">
        <h3>CPU Usage</h3>
        <div class="metric-value">{{ latestMetrics.cpu }}%</div>
      </div>
      <div class="metric-card">
        <h3>Memory</h3>
        <div class="metric-value">{{ latestMetrics.memory }}MB</div>
      </div>
    </div>

    <StreamPlayer
      type="sse"
      :config="config"
      :auto-open="true"
      @message="handleMetricsUpdate"
    >
      <template #log="{ messages, status }">
        <div class="connection-status">
          Status: <span :class="status">{{ status }}</span> | Updates:
          {{ messages.length }}
        </div>
      </template>
    </StreamPlayer>
  </div>
</template>

🐛 Troubleshooting

Common Issues

Connection fails immediately

// Check if the URL is correct and accessible
const config = {
  url: "ws://localhost:3000/ws", // http:// for ws://, https:// for wss://
};

CORS issues with SSE/HTTP

const config = {
  url: "/api/stream",
  headers: {
    "Access-Control-Allow-Origin": "*",
  },
};

Video not playing (HLS/WebRTC)

<StreamPlayer
  type="hls"
  :config="hlsConfig"
  :autoplay="true"
  :muted="true"  <!-- Required for autoplay in most browsers -->
  :plays-inline="true"
/>

Messages not displaying

// Check if logLimit is sufficient
<StreamPlayer :log-limit="1000" />

// Or handle messages manually
@message="msg => console.log('Received:', msg)"

Browser Compatibility

  • WebSocket: All modern browsers
  • SSE: All modern browsers (IE/Edge requires polyfill)
  • HLS: Requires HLS.js for non-Safari browsers
  • WebRTC: Modern browsers (check specific API support)

Performance Tips

  1. Limit message history: Use reasonable logLimit values
  2. Debounce rapid updates: Use shallowRef for message arrays
  3. Clean up properly: Component handles cleanup automatically
  4. Use object URLs: For large binary data in WebRTC/HLS

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

git clone https://github.com/yourusername/vue-streaming.git
cd vue-streaming
npm install
npm run dev

Building

npm run build
npm run test

📄 License

MIT © Vue Streaming Contributors


DocumentationExamplesIssues

Made with ❤️ for the Vue.js community