A practical implementation of the Bycoders frontend challenge featuring video search, a compelling home experience, robust state management, and locally-persisted search history. Includes Google OAuth authentication and video upload capabilities.
Features • Architecture • Getting Started • Quality • Scripts
- Smart Video Search - Real-time search with YouTube Data API v3
- Featured Content - Curated video recommendations on homepage
- Google OAuth - Seamless authentication with Google accounts
- Video Upload - Direct upload integration with YouTube
- Persistent History - Local storage for search history via Zustand
- Modern UI - Built with Radix UI primitives and TailwindCSS 4
- Server Components - Optimized with Next.js App Router architecture
- Error Boundaries - Graceful error handling with Sentry capture
- Storybook - Component documentation with a11y and interaction tests
- Docker Ready - Containerized development environment
- Framework: Next.js 16.0 (App Router)
- React: 19.2 (with React Compiler)
- TypeScript: 5.x (strict mode)
- Styling: TailwindCSS 4.x + Radix UI
- State Management: Zustand 5.x
- Form Handling: React Hook Form + Zod 4 validation
- Session Management: Iron Session
- Unit/Component: Jest 30 + Testing Library
- E2E: Playwright 1.58
- Component Docs: Storybook 10 (a11y addon, Vitest integration)
- Error Tracking: Sentry 10 (client + server + edge)
- Code Quality: Biome 2 (linting & formatting)
- Performance: Lighthouse CI
- Bundle Budgets: size-limit
- Commits: Conventional Commits with Commitizen
- CI/CD: GitHub Actions
This project follows a feature-based architecture with clear separation of concerns between server and client components:
App Router (page.tsx) - SERVER
↓
Feature Component (home.tsx) - SERVER
↓ (slots with React.Suspense)
├─→ Server Components (UserMenu, FeaturedVideos) - SERVER
│ ↓
│ Internal API Routes (/api/*) - SERVER
│ ↓
│ Services (YouTube API Service) - SERVER
│
└─→ Container (home.container.tsx) - CLIENT
↓
Hooks (useHomeLogic) - CLIENT
↓
Stores (Zustand + localStorage) - CLIENT
↓
View (home.view.tsx) - CLIENT
- Server Components: Handle data fetching and API calls (default in Next.js App Router)
- Client Components: Manage interactivity, state, and user interactions (
'use client') - Suspense Boundaries: Enable streaming and progressive loading states
- Container/View Pattern: Separates business logic (container) from presentation (view)
- Testability: Views are pure functions of props
- Reusability: Logic extracted to hooks can be shared across features
- Maintainability: UI changes don't affect business logic
- Node 22.14+ (use
nvm use) or Docker - A YouTube Data API v3 key
- A Google Cloud OAuth 2.0 Client (Client ID + Secret)
- A Sentry project DSN (optional, for error tracking)
-
Copy the environment file:
cp .env.example .env
-
Fill in the required variables (see table below):
| Variable | Required | Description |
|---|---|---|
YOUTUBE_API_KEY |
Yes | YouTube Data API v3 key (Google Cloud Console) |
NEXT_PUBLIC_APP_URL |
Yes | Base URL of the app, e.g. http://localhost:3000 |
GOOGLE_CLIENT_ID |
Yes | OAuth 2.0 Client ID (Google Cloud Console) |
GOOGLE_CLIENT_SECRET |
Yes | OAuth 2.0 Client Secret |
GOOGLE_REDIRECT_URI |
Yes | OAuth callback URL, e.g. http://localhost:3000/api/auth/callback |
SESSION_SECRET |
Yes | Session encryption secret — minimum 32 characters |
SENTRY_DSN |
No | Sentry DSN for server-side error capture |
NEXT_PUBLIC_SENTRY_DSN |
No | Sentry DSN for client-side error capture |
VERCEL_URL |
No | Injected automatically on Vercel deployments |
PORT |
No | Server port (default: 3000) |
-
Install dependencies:
npm install
-
Run the development server:
npm run dev
If you prefer a containerized environment, use the provided Makefile commands:
-
Start the container (with logs):
make up
Builds and runs the app in development mode with hot-reload.
-
Start in background (detached):
make up-silent
-
Other commands:
make down # Stop containers make logs # Tail container logs make restart # Restart containers make shell # Open a shell inside the container
React Error Boundaries wrap all critical UI regions. Unhandled errors are automatically captured in Sentry with full context (user, session, stack trace).
API routes are protected by rate limiting middleware to prevent abuse and quota exhaustion of the YouTube API.
All responses include production-grade headers configured in next.config.ts:
| Header | Value |
|---|---|
Content-Security-Policy |
Allowlists YouTube, Google, and self origins |
Strict-Transport-Security |
max-age=63072000; includeSubDomains; preload |
X-Frame-Options |
DENY |
X-Content-Type-Options |
nosniff |
Referrer-Policy |
strict-origin-when-cross-origin |
Permissions-Policy |
Restricts fullscreen and picture-in-picture to self |
Coverage is enforced at 80% minimum (statements, branches, functions, lines) — the CI pipeline fails if thresholds are not met.
Automated performance audits run on every build:
| Category | Threshold |
|---|---|
| Performance | ≥ 85 |
| Accessibility | ≥ 90 |
| SEO | 100 |
| Best Practices | ≥ 90 |
size-limit enforces JS bundle budgets to prevent performance regressions from unchecked dependency growth.
| Script | Description |
|---|---|
npm run dev |
Start development server with hot reload |
npm run build |
Production build |
npm run start |
Start production server |
npm run type-check |
TypeScript type checking (tsc --noEmit) |
npm run lint |
Static analysis with Biome |
npm run format |
Auto-format with Biome |
npm run test |
Run Jest test suite |
npm run test:watch |
Jest in watch mode |
npm run test:coverage |
Jest with coverage report |
npm run test:e2e |
Playwright E2E tests |
npm run test:e2e:ui |
Playwright interactive UI runner |
npm run test:e2e:headed |
Playwright in headed mode (debug) |
npm run lighthouse:ci |
Run Lighthouse CI audit |
npm run storybook |
Storybook dev server on port 6006 |
npm run build-storybook |
Build Storybook static files |
npm run commit:create |
Interactive commit with Commitizen |
npm run size-limit |
Check JS bundle size budgets |
Every pull request triggers the GitHub Actions workflow, which runs in order:
- Lint — Biome static analysis
- Type check —
tsc --noEmit - Tests — Jest unit/component suite with coverage thresholds
- E2E — Playwright end-to-end tests
- Build — Next.js production build
- Lighthouse CI — Performance and accessibility audit
- Bundle size — size-limit budget check
All steps must pass before a PR can be merged.