Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Building a Full-Stack Discord Clone with Next.js 13, Socket.IO, Prisma, and MySQL

Tech 1

Prerequisites

Before proceeding, ensure familiarity with:

  • Core Next.js concepts (App Router, Server Componants)
  • React fundamentals (hooks, component composition)
  • Prisma ORM usage (schema definition, migrations)
  • Tailwind CSS utility-first styling
  • MySQL database modeling and connectivity

Project Initialization

Start a new Next.js application with TypeScript and Tailwind support:

npx create-next-app@latest discord-clone --typescript --tailwind --app --src-dir

Then integrate shadcn/ui components:

npx shadcn-ui@latest init

Follow the prompts to configure for Next.js App Router and Tailwind. After setup, launch the dev server:

npm run dev

Layout and Styling Adjustments

Update app/globals.css to enforce full viewport height across all root elements:

html,
body,
:root {
  height: 100%;
}

Adopt a logical directory structure: group authentication-related routes under (auth) (a route group that doesn’t affect URL paths), and main application routes under (main). This improves maintainability without altering routing behavior.

Authentication with Clerk

Integrate Clerk for secure user management:

npm install @clerk/nextjs

Add required environment variables to .env.local:

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_example_key
CLERK_SECRET_KEY=sk_test_example_secret
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up

Create middleware.ts at the project root:

import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware();

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
};

Wrap the root layout (app/layout.tsx) with ClerkProvider:

import { ClerkProvider } from '@clerk/nextjs';
import { Inter } from 'next/font/google';
import './globals.css';

const inter = Inter({ subsets: ['latin'] });

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body className={inter.className}>{children}</body>
      </html>
    </ClerkProvider>
  );
}

Auth Route Setup

Inside (auth), create dynamic route segments:

  • [[...sign-in]]/page.tsx:
'use client';

import { SignIn } from '@clerk/nextjs';

export default function SignInPage() {
  return <SignIn />;
}
  • [[...sign-up]]/page.tsx:
'use client';

import { SignUp } from '@clerk/nextjs';

export default function SignUpPage() {
  return <SignUp />;
}

Theme Management

Install and configure theme persistence:

npm install next-themes

Create components/providers/theme-provider.tsx:

'use client';

import * as React from 'react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
import { ThemeProviderProps } from 'next-themes/dist/types';

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

Update app/layout.tsx to wrap children with the provider:

import { ThemeProvider } from '@/components/providers/theme-provider';

// … inside body
<ThemeProvider
  attribute="class"
  defaultTheme="light"
  enableSystem={false}
  storageKey="discord-theme"
>
  {children}
</ThemeProvider>

Add a theme toggle component at components/mode-toggle.tsx:

'use client';

import * as React from 'react';
import { Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import { Button } from '@/components/ui/button';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';

export function ModeToggle() {
  const { setTheme } = useTheme();

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="icon">
          <Sun className="h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
          <Moon className="absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
          <span className="sr-only">Toggle theme</span>
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end">
        <DropdownMenuItem onClick={() => setTheme('light')}>Light</DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme('dark')}>Dark</DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme('system')}>System</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

If DropdownMenu is missing, scaffold it using:

npx shadcn-ui@latest add dropdown-menu

Database Layer with Prisma

Install Prisma CLI and initialize the ORM:

npm install -D prisma
npx prisma init

This generates prisma/schema.prisma and a .env file with DATABASE_URL. Configure the latter to point to your local or remote MySQL instance.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.