Build a Tinder Clone With NextJS, React, Supabase and Stream | With Realtime Chat and Video Calling

PedroTechAbout 10 min readAug 19, 2025Watch original
THE SUMMARYAI-generated

Key Concepts

  • Full-Stack Dating Website: Building a complete dating platform with user accounts, profiles, matching, chat, and video calls.
  • Next.js: A React framework for building server-rendered and statically generated web applications.
  • Tailwind CSS: A utility-first CSS framework for rapidly styling HTML elements.
  • ReactJS: A JavaScript library for building user interfaces.
  • Supabase: An open-source Firebase alternative providing backend-as-a-service features like database, authentication, and storage.
  • Stream: A platform for building chat, video, and activity feed features.
  • Backend as a Service (BaaS): A cloud service model where developers outsource backend aspects of an application.
  • Postgres SQL: An open-source relational database management system.
  • Authentication: Verifying the identity of a user.
  • O Context: A React context for managing authentication state globally.
  • RLS Policies: Row Level Security policies in Postgres SQL to control data access.
  • Triggers and Functions (Postgres SQL): Automated procedures executed in response to database events.
  • Indexes (Postgres SQL): Data structures to optimize query performance.
  • Stream Chat and Video SDKs: Stream's libraries for integrating chat and video functionalities.

Setting Up the Project

  1. Initialize Next.js App: Use the command npx create next app to generate a new Next.js project.
    • Choose TypeScript, ESLint, Tailwind CSS, App Router, and Turbo pack.
  2. Create Superbase Project: Go to superbase.com, create an account, and create a new project.
    • Choose a project name, password, and database location.
  3. Configure Environment Variables: Create an .env.local file and add the Superbase URL and anonymous key.
    • SUPERBASE_URL: The URL of your Superbase project.
    • NEXT_PUBLIC_SUPERBASE_ANON_KEY: The anonymous key for your Superbase project.
  4. Integrate Superbase Client: Create a lib/superbase directory with client.ts and server.ts files.
    • Install the Superbase SSR package: npm install @superbase/superbase-js @superbase/superbase-js
    • client.ts: Sets up a Superbase client for browser components using superbase-js.
    • server.ts: Sets up a Superbase client for server components and actions, handling cookies and server-side rendering.

Authentication Setup

  1. Create Auth Route: Create a directory app/auth with a page.tsx file.
  2. Implement Sign-in/Sign-up UI:
    • Use useState to manage the sign-up/sign-in state (isSignUp).
    • Create states for email, password, loading, and error messages.
    • Build a form with email and password input fields.
    • Implement conditional rendering to switch between sign-in and sign-up forms.
  3. Handle Form Submission:
    • Create an handleAuth function to handle form submission.
    • Prevent default form submission behavior.
    • Set the loading state to true during submission.
    • Use a try...catch...finally block to handle Superbase authentication.
    • Call superbase.auth.signUp for sign-up and superbase.auth.signInWithPassword for sign-in.
    • Handle email confirmation links and display appropriate error messages.
    • Set loading to false in the finally block.
  4. Implement O Context:
    • Create a context/authContext.tsx file.
    • Use createContext to create an authentication context.
    • Define the context value type with user information, loading state, and a sign-out function.
    • Create an AuthProvider component to wrap the application.
    • Use useState and useEffect to manage the user's authentication state.
    • Check for existing Superbase sessions on component mount.
    • Subscribe to Superbase authentication state changes using superbase.auth.onAuthStateChange.
    • Create a useAuth hook to consume the authentication context.
  5. Wrap App with O Provider: In layout.tsx, wrap the application with the AuthProvider.
  6. Implement Sign-out Functionality:
    • Call superbase.auth.signOut to sign the user out.
  7. Redirect on Sign-in: Use useRouter and useEffect in the auth/page.tsx to redirect authenticated users to the homepage.

Database Setup

  1. Execute SQL Script: Copy and paste the provided SQL script into the Superbase SQL editor.
  2. SQL Script Breakdown:
    • Users Table: Stores user profile information (ID, full name, username, email, gender, birth date, bio, avatar URL, preferences, location, last active, is verified, is online, created at, updated at).
    • Likes Table: Tracks user likes with from_user_id and to_user_id, ensuring unique likes.
    • Matches Table: Stores matches between users with user_id_1, user_id_2, is_active, and created_at.
    • Indexes: Optimizes query performance on specific fields.
    • Triggers and Functions:
      • update_updated_at_column: Automatically updates the updated_at field on table updates.
      • create_match_on_mutual_like: Creates a match entry when two users like each other.
      • handle_new_user: Creates a user profile entry when a new user signs up.
    • RLS Policies: Enforces row-level security to control data access.
      • Policies ensure authenticated users can only access their own data.
      • Policies allow anyone to view profile photos.
  3. Verify Table Creation: Check the Superbase dashboard to confirm the tables, columns, and RLS policies are created.

Profile Page

  1. Create Profile Route: Create a directory app/profile with a page.tsx file.
  2. Implement UI:
    • Display user profile information (image, age, username, bio).
    • Add a button to navigate to the edit profile page.
  3. Fetch User Profile Data:
    • Create a lib/actions/profile.ts file with a getCurrentUserProfile server action.
    • Use superbase.auth.getUser to get the current user.
    • Query the users table to retrieve the user's profile data.
    • Handle errors and return null if the profile is not found.
    • Use useState and useEffect in the profile page to fetch and display the data.
  4. Edit Profile Page:
    • Create a directory app/profile/edit with a page.tsx file.
    • Implement a form with input fields for all editable profile data (full name, username, bio, gender, birth date, avatar URL).
    • Create a useState hook to manage the form data.
    • Implement a photo upload component.
    • Create a handleInputChange function to update the form data state.
  5. Photo Upload Component:
    • Create a components/photoUpload.tsx component.
    • Implement a file input field to select an image.
    • Validate the file type and size.
    • Create a uploadProfilePhoto server action in lib/actions/profile.ts.
    • Use superbase.storage.from('profile-photos').upload to upload the image to Superbase storage.
    • Generate a unique file name for the uploaded image.
    • Get the public URL of the uploaded image using superbase.storage.from('profile-photos').getPublicUrl.
    • Update the form data state with the new avatar URL.
  6. Update User Profile:
    • Create an updateUserProfile server action in lib/actions/profile.ts.
    • Use superbase.from('users').update to update the user's profile data in the database.
    • Redirect the user to the profile page after a successful update.
  7. Implement RLS Policies for Profile Photos:
    • Create a policy that allows anyone to view profile photos.
    • Create a policy that allows authenticated users to upload profile photos.

Discovery Page (Matches)

  1. Create Matches Route: Create a directory app/matches with a page.tsx file.
  2. Implement UI:
    • Display potential matches one at a time.
    • Add "Like" and "Pass" buttons.
  3. Fetch Potential Matches:
    • Create a lib/actions/matches.ts file with a getPotentialMatches server action.
    • Query the users table to retrieve potential matches, excluding the current user.
    • Filter matches based on user preferences (gender, age range, distance).
    • Use useState and useEffect in the matches page to fetch and display the data.
  4. Implement Like Functionality:
    • Create a likeUser server action in lib/actions/matches.ts.
    • Insert a new entry into the likes table with the from_user_id and to_user_id.
    • Check if the like creates a match (mutual like).
    • If a match is created, fetch the matched user's data.
  5. Implement Match Notification:
    • Create a components/matchNotification.tsx component.
    • Display the matched user's profile image and a message.
    • Add buttons to start a chat or close the notification.
    • Use useState and useEffect to show the notification for a limited time.
  6. Implement Pass Functionality:
    • Move to the next potential match in the list.

Chat Functionality

  1. Create Chat Route: Create a directory app/chat with a page.tsx file for the chat list and a app/chat/[userId]/page.tsx file for individual chats.
  2. Implement Chat List UI:
    • Display a list of users the current user has matched with.
    • Show the last message and timestamp for each chat.
  3. Fetch User Matches for Chat List:
    • Create a getUserMatches server action in lib/actions/matches.ts.
    • Query the matches table to retrieve all matches for the current user.
    • Fetch the profile data for each matched user.
  4. Implement Individual Chat UI:
    • Display messages between the current user and the selected user.
    • Add an input field for sending new messages.
  5. Stream Integration:
    • Create a Stream account and obtain API keys.
    • Install Stream Chat and Video SDKs: npm install stream-chat stream-chat-react @stream-io/video-react-sdk
    • Create a lib/actions/stream.ts file for Stream-related server actions.
    • Implement a getStreamUserToken server action to generate a Stream user token.
    • Initialize a Stream Chat client in the streamChatInterface component.
    • Connect the user to Stream using the generated token.
    • Create a createOrGetChannel server action to create or retrieve a Stream channel for the two users.
    • Watch the Stream channel for new messages.
  6. Sending Messages:
    • Implement a handleSendMessage function to send messages using the Stream Chat client.
    • Update the local messages state with the new message.
  7. Displaying Messages:
    • Map through the messages array and display each message with appropriate styling.
    • Justify message alignment based on the sender (me or other).
  8. Typing Indicator:
    • Listen for typing.start and typing.stop events on the Stream channel.
    • Update the isTyping state accordingly.
    • Display a typing indicator in the UI when the other user is typing.
    • Send typing indicator events when the user types in the input field.
  9. Scroll to Bottom:
    • Use refs to manage the scroll position of the messages container.
    • Implement a scrollToBottom function to scroll to the bottom of the messages.
    • Use useEffect to trigger scrollToBottom when new messages are added.
    • Implement a "Scroll to Bottom" button that appears when the user scrolls up.

Video Call Functionality

  1. Create Video Call Button: Add a button to the chat header to initiate a video call.
  2. Implement Video Call Logic:
    • Create a createVideoCall server action in lib/actions/stream.ts.
    • Generate a unique call ID.
    • Send a message to the other user with the call ID.
    • Implement a handleVideoCall function in the streamChatInterface component.
    • Use useImperativeHandle to expose the handleVideoCall function to the parent component.
  3. Implement Incoming Call Notification:
    • Listen for video call invitation messages.
    • Display an incoming call notification with options to accept or decline.
  4. Video Call Component:
    • Create a components/videoCall.tsx component.
    • Initialize a Stream Video client.
    • Join or create a Stream Video call.
    • Use Stream Video components to display the video call UI.
  5. Implement Decline and Accept Call Functionality:
    • Update the UI to show the video call component when the call is accepted.
    • Remove the incoming call notification when the call is declined or accepted.
  6. Implement Get Stream Video Token:
    • Create a getStreamVideoToken server action in lib/actions/stream.ts.
    • Generate a Stream Video token for the user.

Additional Notes

  • The transcript emphasizes the use of modern industry-standard technologies.
  • The tutorial assumes an intermediate level of knowledge in React, Next.js, and related technologies.
  • The transcript focuses on functionality and provides CSS styling code snippets without detailed explanations.
  • The transcript includes detailed explanations of SQL queries, triggers, and RLS policies.
  • The transcript provides a script to generate fake user profiles for testing purposes.
  • The transcript highlights the benefits of using Stream for building chat and video features.
  • The transcript includes error handling and loading states for a better user experience.
  • The transcript provides clear instructions and code snippets for each step of the development process.
  • The transcript includes a detailed explanation of how to use the useImperativeHandle hook to pass functions from a child component to a parent component.
  • The transcript includes a detailed explanation of how to use a simple hash function to generate unique IDs for the channel and call IDs.
  • The transcript includes a detailed explanation of how to use the Stream Video React SDK to implement video call functionality.
  • The transcript includes a detailed explanation of how to use the Stream Chat React SDK to implement chat functionality.
  • The transcript includes a detailed explanation of how to use the Stream Chat React SDK to implement typing indicators.
  • The transcript includes a detailed explanation of how to use the Stream Chat React SDK to implement scroll to bottom functionality.
  • The transcript includes a detailed explanation of how to use the Stream Chat React SDK to implement incoming call notifications.
  • The transcript includes a detailed explanation of how to use the Stream Chat React SDK to implement decline and accept call functionality.

Conclusion

This transcript provides a comprehensive guide to building a full-stack dating website with modern technologies. It covers everything from setting up the project and database to implementing authentication, profile management, matching, chat, and video call functionality. The use of Superbase and Stream simplifies the backend and real-time communication aspects, allowing developers to focus on building a feature-rich and engaging user experience. The detailed explanations and code snippets make this a valuable resource for intermediate to advanced developers looking to build a complex web application.

AI summaries can miss context or contain errors. Check important details against the original video.

Go a little deeper.

Have a question about this video? Load its transcript to open the video chat.