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 🙏

© 2026 – Pkg Stats / Ryan Hefner

kasunk99-livestream-core

v0.3.58

Published

Reusable livestream viewer/host module for React Native (Expo) — mediasoup + Socket.IO

Readme

@livestream/core

Reusable livestream viewer (and later host) module for React Native / Expo. Talks to a mediasoup + Socket.IO livestream server.

Peer dependencies

Install in the host app:

  • react, react-native
  • react-native-webrtc (required for video; use a dev build, not Expo Go)
  • mediasoup-client
  • socket.io-client

Setup

  1. Configure before using any component or hook:
import { setLivestreamConfig } from '@livestream/core';

setLivestreamConfig({
  serverHttpUrl: 'http://localhost:3000', // or your server URL
  getDisplayName: () => 'Viewer', // optional, for chat/room display
});
  1. Use the feed or hooks:
import { LiveStreamFeed } from '@livestream/core';

<LiveStreamFeed />

Or use useLiveStreams() and useViewerSocket(roomId) for custom UIs.

Components

LiveStreamFeed

| Prop | Type | Description | |---|---|---| | isScreenFocused | boolean | Only the on-screen stream connects while this is true (default true). | | onLeave | () => void | Shows a close (×) button that calls this. | | onJoinStatusChange | (joined: boolean) => void | Join status of the on-screen stream. | | onActiveStreamChange | (stream: LiveStreamInfo \| null) => void | Fires when the on-screen stream changes (by roomId), including the first stream after load, and with null when there is no stream. It doesn't fire again for a list refetch of the same room, and it's safe to pass an inline function. | | renderViewerActions | RenderViewerActions | Extra buttons in the viewer's bottom bar. Forwarded to every LiveStreamViewerItem. | | safeAreaInsets | { top: number; bottom: number } | For a full-screen (edge-to-edge) feed: keeps the top bar and bottom controls clear of the status bar and navigation bar. Memoize it. Without it, the original fixed spacing is used. | | onMinimize | () => void | Shows a minimize button to the left of ×, e.g. to enter picture-in-picture. | | onActiveStreamEnded | (stream: LiveStreamInfo) => void | Fires once when the host of the on-screen stream ends it. |

The viewer's chat list scrolls inside the vertical feed: while a finger is on a chat that has more to scroll, the feed pauses paging (a short chat still lets the swipe change streams).

LiveStreamViewerItem

Takes the same renderViewerActions, safeAreaInsets and onMinimize props, plus onStreamEnded?: () => void (the feed passes it for the on-screen item only). renderViewerActions It is called with { stream, isActive, joined, streamEnded } and its result is rendered between the emoji (☺) and like (♥) buttons, only while the pill bar is showing (not while typing or while the emoji panel is open). Return null to render nothing. Size your own button (about 40×40 matches its neighbours), and keep the function stable with useCallback.

const renderViewerActions = useCallback<RenderViewerActions>(
  ({ stream, joined, streamEnded }) =>
    joined && !streamEnded && stream.hostUserId ? (
      <MyTipButton onPress={() => openTip(stream.hostUserId!)} />
    ) : null,
  [openTip],
);

<LiveStreamFeed
  onActiveStreamChange={(stream) => setActiveRoomId(stream?.roomId ?? null)}
  renderViewerActions={renderViewerActions}
/>

Tip spotlight

When a viewer tips a stream, everyone in the room (host included) sees the tipper's round avatar in the middle of the screen with an animation, their name and "sent N coins", then it fades out. The server also adds a highlighted chat line (kind: 'tip').

  • Announce: after the tip succeeds in your app, call announceTip(amount, avatarUrl, referenceId) from the renderViewerActions context. It emits tip-spotlight on that stream's socket. The server checks the amount, rate-limits (1 per 3 s per viewer), ignores a repeated referenceId, and uses its own record of the tipper's name.
  • Viewer: LiveStreamViewerItem shows the spotlight on the on-screen stream. onTipSpotlight(spotlight) (on the feed or item) fires when one starts — use it to play a sound.
  • Host UI: useHostSocket() returns tipSpotlights and dismissTipSpotlight(id). Render <LiveTipSpotlightOverlay queue={tipSpotlights} onDone={dismissTipSpotlight} onShow={…} />. HostChatMessage.kind === 'tip' marks tip lines, and LiveChatMessage takes highlight for them.
  • Socket event: tip-spotlight → { id, peerId, displayName, avatarUrl, amount, timestamp } (LiveTipSpotlight).

Exports

  • Config: setLivestreamConfig, getBaseUrl, getSignalingWsUrl, getDisplayName
  • Services: fetchActiveStreams, ensureMediasoupGlobals
  • Hooks: useViewerSocket, useLiveStreams
  • Components: LiveStreamFeed, LiveStreamViewerItem, LiveTipSpotlightOverlay, LiveChatMessage (a chat bubble; long messages collapse to 3 lines with a "Show more" toggle — used by the viewer and reusable in a host UI)
  • Constants: LIVE_CHAT_MAX_LENGTH (150) — max characters in one chat message; the viewer's chat input enforces it, and a host UI can use it to match
  • Types: LiveStreamInfo, ProducerInfo, JoinRoomResult, RoomState, HostMediaState, LiveStreamViewerActionsContext, RenderViewerActions, LiveTipSpotlight, AnnounceTip

Server contract

  • HTTP: GET {serverHttpUrl}/api/live-streams → { streams: { roomId, viewerCount, producerCount, title?, hostDisplayName?, hostUserId?, hostAvatarUrl? }[] }
  • WebSocket: Socket.IO at same origin (ws/https). Events: join-room, create-webrtc-transport, connect-transport, produce, consume, get-producers, new-producer, room-updated, etc.