stream-builder

/stream-builder scaffolds new apps from scratch and integrates Stream into existing ones. The skill handles web projects only. For native projects, see the platform skills.

What it builds

  • New apps from scratch, built as a Next.js project with Tailwind and your choice of Stream SDKs (Chat, Video, Feeds, Moderation), wired end-to-end. You get a working login screen and a token endpoint, plus one functional screen for each product you include.
  • Stream added to an existing app, where the builder detects what's already there (framework, package manager, any existing Stream setup) and adds the new SDK on top without breaking what you've already built.

A feeds-based social app scaffolded by /stream-builder

Example prompts

/stream-builder scaffold a Next.js chat app with a sidebar of channels and a message view
/stream-builder add Video calling to my existing Next.js app. I already have Chat set up
/stream-builder build a feeds-based social app: profile pages, follow button, timeline

What it knows

The builder ships reference files that cover the SDK wiring patterns that aren't obvious from the public docs:

  • Token generation endpoints, and the secrets-handling rules around them.
  • StreamChat.getInstance() vs new StreamChat(): when to use which, and why it matters in React strict mode.
  • Theme wiring for Chat React on top of the Shadcn theme the scaffold generates, including the next-themes light and dark toggle.
  • Component composition: which Stream components are headless, and which assume specific parents.
  • Per-product setup quirks:
    • Feeds: activity type schemas
    • Video: call lifecycle and connection management
    • Moderation: Dashboard policy authoring

On the client the builder uses new StreamChat(apiKey), never getInstance(), because the singleton breaks under React strict mode's mount, unmount, remount cycle in development. Connections go through a useEffect with a mounted guard and cleanup, not a useRef "run once" flag that would survive the remount and skip re-initialization. getInstance() is reserved for server-side code, where a singleton is fine.

useChatClient.ts
import { useEffect, useState } from "react";
import { StreamChat } from "stream-chat";

export function useChatClient(apiKey: string, userId: string, token: string) {
  const [client, setClient] = useState<StreamChat>();

  useEffect(() => {
    let mounted = true;
    const chat = new StreamChat(apiKey);

    chat.connectUser({ id: userId }, token).then(() => {
      if (mounted) setClient(chat);
    });

    return () => {
      mounted = false;
      chat.disconnectUser();
    };
  }, [apiKey, userId, token]);

  return client;
}

Don't reach for getInstance() on the client or guard the connection with a useRef "run once" flag. Both survive strict mode's remount in development and skip re-initialization, which surfaces as a client that silently never reconnects.

The patterns themselves live at skills/stream-builder/references/ if you want to read them directly.

What it asks before running

The builder may offer to install three open-source frontend skills that improve generic UI quality:

  • vercel-react-best-practices: React and Next.js idioms
  • web-design-guidelines: general UI polish
  • frontend-design: frontend structure

It lists them and waits for your confirmation. If you decline, the builder still works fine. The Stream reference files cover the SDK side, while these frontend skills only refine the visual polish on top.