Skip to content
 
 

Repository files navigation

LinkUp — Video Conferencing Platform

A full-featured video conferencing web application built with Next.js, TypeScript, Clerk for authentication, and Stream for real-time video communication. LinkUp lets users instantly start meetings, schedule future calls, join via invite links, and revisit recordings — all from a clean, dark-themed dashboard.


Table of Contents


Features

  • Instant Meetings — Start a video call with a single click
  • Scheduled Meetings — Pick a future date and time; share the invite link before the call
  • Join via Link — Paste any meeting link to join a call
  • Personal Room — Every user gets a persistent room tied to their account
  • Device Setup — Camera and microphone check before entering any meeting
  • Flexible Layouts — Switch between Grid, Speaker-Left, and Speaker-Right views mid-call
  • Participant List — See everyone in the room with name and avatar
  • Call Statistics — Real-time network and quality metrics
  • End Call (Owner) — Meeting host can end the call for all participants
  • Recordings — Browse and replay past meeting recordings
  • Upcoming & Previous Calls — Dashboard views for all your scheduled and ended meetings
  • Responsive Design — Full mobile support with a slide-out navigation drawer

Tech Stack

Layer Technology
Framework Next.js 14 (App Router)
Language TypeScript
Authentication Clerk
Real-time Video Stream Video React SDK
Stream Server SDK @stream-io/node-sdk
Styling Tailwind CSS
UI Components shadcn/ui + Radix UI
Icons Lucide React
Date Picking react-datepicker
Date Utilities date-fns
Class Utilities clsx + tailwind-merge

Architecture

The diagram below shows how the major layers of LinkUp interact — from the browser through Next.js middleware, authentication, video infra, and back.

graph TB
    subgraph Client["Browser / Client"]
        UI["React Components<br/>(Next.js App Router)"]
        SW["Stream Video SDK<br/>(@stream-io/video-react-sdk)"]
    end

    subgraph NextJS["Next.js Server"]
        MW["Clerk Middleware<br/>(Route Protection)"]
        SA["Server Action<br/>stream.actions.ts<br/>(Token Generation)"]
        Pages["Pages & Layouts<br/>app/(root)/**"]
    end

    subgraph Auth["Clerk Platform"]
        ClerkAPI["Clerk Auth API"]
        Session["User Session / JWT"]
    end

    subgraph StreamPlatform["Stream Platform"]
        StreamAPI["Stream REST API"]
        WebRTC["WebRTC Media Server<br/>(TURN / SFU)"]
        Recordings["Recording Storage"]
    end

    User(("User")) -->|HTTP Request| MW
    MW -->|Authenticated?| ClerkAPI
    ClerkAPI -->|Session Token| Session
    Session -->|Authorized| Pages
    Pages -->|Render| UI
    UI -->|useUser / SignedIn| ClerkAPI
    UI -->|Initialize Client| SW
    SW -->|Request Token| SA
    SA -->|CLERK_SECRET_KEY| ClerkAPI
    SA -->|STREAM_SECRET_KEY<br/>Generate User Token| StreamAPI
    StreamAPI -->|JWT Token| SA
    SA -->|Token| SW
    SW -->|Join / Create Call| StreamAPI
    StreamAPI -->|Call Details| SW
    SW <-->|Audio / Video / Signaling| WebRTC
    StreamAPI -->|Store| Recordings
    SW -->|Query Recordings| StreamAPI

    style Client fill:#1c1f2e,color:#fff,stroke:#0E78F9
    style NextJS fill:#252a41,color:#fff,stroke:#0E78F9
    style Auth fill:#1a1a2e,color:#fff,stroke:#7c3aed
    style StreamPlatform fill:#1a2e1a,color:#fff,stroke:#16a34a
Loading

Project Structure

LinkUp/
├── app/
│   ├── (auth)/                     # Public auth routes
│   │   ├── sign-in/[[...sign-in]]/ # Clerk sign-in page
│   │   └── sign-up/[[...sign-up]]/ # Clerk sign-up page
│   ├── (root)/                     # Protected routes (auth required)
│   │   ├── (home)/
│   │   │   ├── page.tsx            # Dashboard
│   │   │   ├── upcoming/           # Upcoming meetings
│   │   │   ├── previous/           # Ended meetings
│   │   │   ├── recordings/         # Meeting recordings
│   │   │   └── personal-room/      # Personal room
│   │   ├── meeting/[id]/           # Active meeting room
│   │   └── layout.tsx              # Wraps with StreamVideoProvider
│   ├── constants/index.ts          # Nav links, avatar images
│   ├── globals.css                 # Global styles + Stream overrides
│   └── layout.tsx                  # Root layout (ClerkProvider)
│
├── components/
│   ├── ui/                         # shadcn/ui primitives
│   ├── Navbar.tsx                  # Top navigation bar
│   ├── Sidebar.tsx                 # Left navigation sidebar
│   ├── MobileNav.tsx               # Mobile drawer nav
│   ├── MeetingTypeList.tsx         # Home page action cards + meeting logic
│   ├── HomeCard.tsx                # Single action card
│   ├── MeetingModal.tsx            # Reusable meeting dialog
│   ├── CallList.tsx                # Upcoming / previous / recordings list
│   ├── MeetingCard.tsx             # Single meeting or recording card
│   ├── MeetingSetup.tsx            # Pre-meeting device check
│   ├── MeetingRoom.tsx             # Active call UI with layout controls
│   ├── EndCallButton.tsx           # Host-only end call button
│   └── Loader.tsx                  # Loading spinner
│
├── hooks/
│   ├── useGetCalls.ts              # Fetch all user calls (upcoming/ended/recordings)
│   ├── useGetCallById.ts           # Fetch a single call by ID
│   └── use-toast.ts                # Toast notification hook
│
├── actions/
│   └── stream.actions.ts           # Server action: Stream JWT token generation
│
├── poviders/
│   └── StreamClientProvidet.tsx    # StreamVideoClient initialization provider
│
├── lib/
│   └── utils.ts                    # cn() classname utility
│
├── middleware.ts                    # Clerk route protection middleware
├── next.config.mjs
├── tailwind.config.ts
└── tsconfig.json

Getting Started

Prerequisites

  • Node.js 18+
  • A Clerk account
  • A Stream account (Video & Audio product)

Installation

# 1. Clone the repository
git clone https://github.com/your-username/LinkUp.git
cd LinkUp

# 2. Install dependencies
npm install

# 3. Set up environment variables
cp .env.example .env.local
# Fill in all required keys (see Environment Variables section)

# 4. Run the development server
npm run dev

Open http://localhost:3000 in your browser.

Build for Production

npm run build
npm start

Environment Variables

Create a .env.local file in the project root:

# Clerk Authentication
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...

NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/

# Stream Video
NEXT_PUBLIC_STREAM_API_KEY=<your-stream-api-key>
STREAM_SECRET_KEY=<your-stream-secret-key>

# App
NEXT_PUBLIC_BASE_URL=http://localhost:3000

Where to find these keys:


Key Design Decisions

Decision Rationale
Next.js App Router Server Components reduce client-side JS; route groups enable clean auth vs. protected layout separation
Clerk Middleware Declarative route protection without manual redirect logic in every page
Stream Server Action for token Keeps STREAM_SECRET_KEY server-side only — never exposed to the browser
shadcn/ui Components live in the repo; fully customizable without fighting a library's override system
StreamVideoProvider in protected layout Initializes the video client once at the layout level so all child pages share the same client instance

Scripts

Command Description
npm run dev Start local development server
npm run build Build for production
npm start Start production server
npm run lint Run ESLint

About

A robust, real-time video conferencing application built with Next.js, Stream, Clerk, and Tailwind CSS. This app offers seamless video and audio communication, allowing users to connect and collaborate effortlessly.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages