Build a Real-Time Slack Clone With Nextjs, React, Tailwind, Auth.js | Part 1/2 (2024)

Code With AntonioAbout 21 min readMay 27, 2025Watch original
THE SUMMARYAI-generated

Key Concepts

  • Slack Clone: Building a real-time communication platform similar to Slack.
  • Next.js: A React framework for building web applications.
  • Tailwind CSS: A utility-first CSS framework.
  • Shadcn UI: A collection of accessible and reusable components built with Radix UI and Tailwind CSS.
  • Bun: A fast JavaScript package manager, runtime, and bundler.
  • Convex: A serverless application platform with a globally replicated database.
  • Convex Auth: Authentication solution for Convex applications using NextAuth.js.
  • Oauth: An open standard for access delegation, commonly used for authentication.
  • EJS: A templating engine for JavaScript.
  • Yoai: A state management library.
  • Lucid React: A library of React icons.
  • React Use: A collection of useful React hooks.
  • CVA (Class Variance Authority): A utility for creating type-safe and composable class name variants.
  • React Icons: A library of customizable React icons.
  • React Hook Form: A library for building performant and flexible forms with React.
  • Zod: A TypeScript-first schema declaration and validation library.
  • Have I Been Pwned: A service that aggregates data breaches and allows users to check if their accounts have been compromised.
  • MutableRefObject: A React type for mutable ref objects.
  • Delta (Quill): A data format used by the Quill editor to represent rich text content.
  • Op (Quill): An operation in the Quill Delta format.
  • Dynamic Import: A feature in JavaScript that allows importing modules asynchronously.
  • Use Memo: A React hook that memoizes the result of a computation.
  • Use Effect: A React hook that performs side effects in functional components.
  • Use Layout Effect: A React hook that performs side effects synchronously after all DOM mutations.
  • Use State: A React hook that adds state to functional components.
  • Use Ref: A React hook that creates a mutable reference to a value that persists across renders.
  • Use Callback: A React hook that memoizes a callback function.
  • Use Router: A Next.js hook that provides access to the router object.
  • Use Search Params: A Next.js hook that provides access to the URL's query parameters.
  • Use Params: A Next.js hook that provides access to the dynamic route parameters.
  • Use Pathname: A Next.js hook that provides access to the current route pathname.
  • Use Transition: A React hook that allows you to update the state without blocking the UI.
  • Use Defered Value: A React hook that allows you to defer updating a part of the UI.
  • Use Id: A React hook that generates a unique ID.
  • Use Context: A React hook that consumes a context.
  • Use Reducer: A React hook that manages state with a reducer function.
  • Use Imperative Handle: A React hook that customizes the instance value that is exposed to parent components when using ref.
  • Use Debug Value: A React hook that displays a label for custom hooks in React DevTools.
  • Use Callback Ref: A React hook that provides a callback ref.
  • Use Event Listener: A React hook that adds an event listener to a target.
  • Use Timeout: A React hook that manages a timeout.
  • Use Interval: A React hook that manages an interval.
  • Use Animation Frame: A React hook that manages an animation frame.
  • Use Local Storage: A React hook that persists state in local storage.
  • Use Session Storage: A React hook that persists state in session storage.
  • Use Cookie: A React hook that manages cookies.
  • Use Media Query: A React hook that detects media query changes.
  • Use Online: A React hook that detects online status.
  • Use Geolocation: A React hook that retrieves geolocation data.
  • Use Battery: A React hook that retrieves battery status.
  • Use Title: A React hook that updates the document title.
  • Use Script: A React hook that loads a script.
  • Use Style Tag: A React hook that injects a style tag.
  • Use Hotkeys: A React hook that binds hotkeys to elements.
  • Use Lock Body Scroll: A React hook that locks the body scroll.
  • Use Measure: A React hook that measures the size of an element.
  • Use Window Size: A React hook that retrieves the window size.
  • Use Click Away: A React hook that detects clicks outside of an element.
  • Use Hover: A React hook that detects hover state.
  • Use Long Press: A React hook that detects long press gestures.
  • Use Drag: A React hook that manages drag and drop interactions.
  • Use Drop: A React hook that manages drop interactions.
  • Use Resize Observer: A React hook that observes element resize events.
  • Use Intersection Observer: A React hook that observes element intersection events.
  • Use Mutation Observer: A React hook that observes DOM mutations.
  • Use Virtual List: A React hook that virtualizes a list.
  • Use Infinite Scroll: A React hook that implements infinite scrolling.
  • Use Speech Recognition: A React hook that uses the Speech Recognition API.
  • Use Speech Synthesis: A React hook that uses the Speech Synthesis API.
  • Use Clipboard: A React hook that uses the Clipboard API.
  • Use Broadcast Channel: A React hook that uses the Broadcast Channel API.
  • Use Share: A React hook that uses the Web Share API.
  • Use Wake Lock: A React hook that uses the Wake Lock API.
  • Use Fullscreen: A React hook that uses the Fullscreen API.
  • Use Idle: A React hook that detects idle state.
  • Use Page Visibility: A React hook that detects page visibility changes.
  • Use Preferred Dark Mode: A React hook that detects preferred dark mode.
  • Use Reduced Motion: A React hook that detects reduced motion preference.
  • Use Supported: A React hook that detects API support.
  • Use Promise: A React hook that manages a promise.
  • Use Async: A React hook that manages an asynchronous operation.
  • Use Async Fn: A React hook that manages an asynchronous function.
  • Use Async State: A React hook that manages asynchronous state.
  • Use Event Listener: A React hook that adds an event listener to a target.
  • Use Timeout: A React hook that manages a timeout.
  • Use Interval: A React hook that manages an interval.
  • Use Animation Frame: A React hook that manages an animation frame.
  • Use Local Storage: A React hook that persists state in local storage.
  • Use Session Storage: A React hook that persists state in session storage.
  • Use Cookie: A React hook that manages cookies.
  • Use Media Query: A React hook that detects media query changes.
  • Use Online: A React hook that detects online status.
  • Use Geolocation: A React hook that retrieves geolocation data.
  • Use Battery: A React hook that retrieves battery status.
  • Use Title: A React hook that updates the document title.
  • Use Script: A React hook that loads a script.
  • Use Style Tag: A React hook that injects a style tag.
  • Use Hotkeys: A React hook that binds hotkeys to elements.
  • Use Lock Body Scroll: A React hook that locks the body scroll.
  • Use Measure: A React hook that measures the size of an element.
  • Use Window Size: A React hook that retrieves the window size.
  • Use Click Away: A React hook that detects clicks outside of an element.
  • Use Hover: A React hook that detects hover state.
  • Use Long Press: A React hook that detects long press gestures.
  • Use Drag: A React hook that manages drag and drop interactions.
  • Use Drop: A React hook that manages drop interactions.
  • Use Resize Observer: A React hook that observes element resize events.
  • Use Intersection Observer: A React hook that observes element intersection events.
  • Use Mutation Observer: A React hook that observes DOM mutations.
  • Use Virtual List: A React hook that virtualizes a list.
  • Use Infinite Scroll: A React hook that implements infinite scrolling.
  • Use Speech Recognition: A React hook that uses the Speech Recognition API.
  • Use Speech Synthesis: A React hook that uses the Speech Synthesis API.
  • Use Clipboard: A React hook that uses the Clipboard API.
  • Use Broadcast Channel: A React hook that uses the Broadcast Channel API.
  • Use Share: A React hook that uses the Web Share API.
  • Use Wake Lock: A React hook that uses the Wake Lock API.
  • Use Fullscreen: A React hook that uses the Fullscreen API.
  • Use Idle: A React hook that detects idle state.
  • Use Page Visibility: A React hook that detects page visibility changes.
  • Use Preferred Dark Mode: A React hook that detects preferred dark mode.
  • Use Reduced Motion: A React hook that detects reduced motion preference.
  • Use Supported: A React hook that detects API support.

Project Setup and Configuration

  • Installing Next.js:
    • Requires Node.js installed (version >= specified in Next.js documentation).
    • Verify Node.js version using node -v.
    • Verify npx version (comes with Node.js).
    • Automatic installation using npx create-next-app@latest.
  • Using Bun (Optional):
    • Bun is a JavaScript package manager, runtime, and bundler.
    • Install Bun: Refer to Bun's official website.
    • Verify Bun version using bun -v and bunx -v.
    • Use bunx create-next-app@latest instead of npx.
  • Creating a Next.js Project:
    • Run bunx create-next-app@latest or npx create-next-app@latest.
    • Project name: e.g., "slack-tutorial".
    • TypeScript: Yes.
    • Tailwind CSS: Yes.
    • Source directory: Yes.
    • App Router: Yes.
    • Customize default import alias: No.
  • Opening the Project in Visual Studio Code:
    • Open the project folder in VS Code.
    • Verify the project structure: src folder, app folder, globals.css, favicon, layout.tsx, page.tsx, tailwind.config.js.
  • Running the App:
    • Navigate to the project directory in the terminal.
    • Run bun run dev or npm run dev.
    • Access the app in the browser at localhost:3000.
  • Adding Shadcn UI:
    • Shadcn UI is not a component library but a collection of reusable components.
    • Install Shadcn UI components using the CLI.
    • Initialize Shadcn UI: bunx shadcn-ui@latest init.
    • Select default style and color.
    • Enable CSS variables.
  • Adding a Button Component:
    • Run bunx shadcn-ui@latest add button.
    • The button component is located in src/components/ui/button.tsx.
  • Modifying the Page:
    • Modify src/app/page.tsx to display "Hello World".
    • Add Tailwind CSS classes to style the text.
  • Tailwind CSS Intelligence Extension:
    • Install the Tailwind CSS Intelligence extension for VS Code.
    • This extension provides autocompletion, hover information, and color previews for Tailwind CSS classes.
  • Verifying Tailwind Configuration:
    • Ensure that tailwind.config.js includes the src folder in the content array.
  • Using the Button Component:
    • Import the button component: import { Button } from "@/components/ui/button".
    • Render the button component: <Button>Click me</Button>.
  • Customizing the Button Component:
    • Modify the button variants in src/components/ui/button.tsx.
    • Add a new variant called "slack" with custom background color, text color, and hover effect.

Implementing Authentication Screens

  • Global CSS Configuration:
    • Modify src/app/globals.css to set the height of html, body, and #root to 100%.
  • Creating the Authentication Feature:
    • Create a features folder in src.
    • Create an auth folder inside features.
    • Create a components folder inside auth.
    • Create an AuthScreen.tsx file inside auth/components.
  • Auth Screen Component:
    • Mark the component as a client component using "use client".
    • Export a functional component called AuthScreen.
    • Return a div with the text "Auth Screen".
  • Integrating the Auth Screen:
    • Import the AuthScreen component into src/app/page.tsx.
    • Render the AuthScreen component in page.tsx.
  • Defining Types:
    • Create a types.ts file inside the auth folder.
    • Export a type called SignInFlow which can be either "sign-in" or "sign-up".
  • Managing State:
    • Use the useState hook to manage the current sign-in flow (either "sign-in" or "sign-up").
    • Set the default state to "sign-in".
  • Styling the Auth Screen:
    • Add Tailwind CSS classes to the main div to center the content and set the background color.
  • Creating Sign-in and Sign-up Cards:
    • Create SignInCard.tsx and SignUpCard.tsx files inside auth/components.
    • Create functional components for each card, returning a div with the respective text.
  • Dynamically Rendering Cards:
    • In AuthScreen.tsx, conditionally render either the SignInCard or SignUpCard based on the current state.
  • Installing Additional Components:
    • Run bunx shadcn-ui@latest add card input separator.
    • Install react-icons: bun add react-icons.
  • Developing the Sign-in Card:
    • Replace the div in SignInCard.tsx with a Card component.
    • Add a CardHeader, CardTitle, CardDescription, and CardContent components.
    • Add an HTML form element inside the CardContent.
    • Add Input components for email and password.
    • Add a Button component to submit the form.
    • Add a Separator component.
    • Add buttons for "Continue with Google" and "Continue with GitHub".
    • Add a paragraph with a link to the sign-up page.
  • Adding Icons:
    • Import icons from react-icons.
    • Position the icons using absolute positioning.
  • Developing the Sign-up Card:
    • Copy the content of SignInCard.tsx to SignUpCard.tsx.
    • Modify the text and input fields to match the sign-up form.
    • Add an additional input field for confirming the password.
    • Modify the paragraph to link to the sign-in page.
  • Passing State:
    • Pass the setState function to both SignInCard and SignUpCard as props.
  • Defining Props:
    • Create interfaces for the props of SignInCard and SignUpCard.
    • Define the setState prop as a function that accepts a new SignInFlow state.
  • Implementing State Changes:
    • Add an onClick handler to the sign-up/sign-in links to call the setState function and change the current state.
  • Controlling Inputs:
    • Use the useState hook to control the values of the input fields.
    • Update the onChange handlers to update the state with the new input values.

Integrating Convex for Data Management

  • Installing Convex:
    • Run bun add convex.
  • Initializing Convex:
    • Run bunx convex dev.
    • Create a new Convex project.
    • Log in with GitHub if prompted.
  • Understanding the Convex Development Environment:
    • Two processes need to be running concurrently:
      • bun run dev (Next.js application)
      • bunx convex dev (Convex backend)
    • Ensure that the Convex functions are ready.
  • Creating a Schema:
    • Create a convex/schema.ts file.
    • Define the schema using defineSchema and defineTable.
    • Export the schema as the default export.
  • Exposing a Database Query:
    • Create a convex/tasks.ts file.
    • Import query from @/convex/_generated/server.
    • Define a get constant using the query function.
    • Implement the handler to fetch tasks from the database.
  • Adding the Convex Client Provider:
    • Create a src/components/ConvexClientProvider.tsx file.
    • Copy the code from the Convex documentation.
    • Ensure that the NEXT_PUBLIC_CONVEX_URL environment variable is set.
  • Wrapping the App with the Convex Client Provider:
    • Import the ConvexClientProvider into src/app/layout.tsx.
    • Wrap the children prop with the ConvexClientProvider.
  • Fetching Tasks:
    • Create a src/app/test/page.tsx file.
    • Mark the component as a client component using "use client".
    • Import useQuery from convex/react.
    • Import the API from @/convex/_generated/api.
    • Define a tasks variable using the useQuery hook.
    • Iterate over the tasks and render them.

Implementing Authentication with Convex Auth and NextAuth.js

  • Installing Dependencies:
    • Run bun add convex-dev/slouch next-auth.
  • Running the Convex Auth Initialization Command:
    • Run bunx convex-dev/auth.
    • Enter the URL of your local web server (e.g., http://localhost:3000).
  • Configuring the Schema:
    • Import the auth tables from @/convex/auth.
    • Spread the auth tables into the schema definition.
  • Configuring the Front End:
    • Import ConvexAuthNextjsServerProvider into src/app/layout.tsx.
    • Wrap the app with the ConvexAuthNextjsServerProvider.
    • Modify the ConvexClientProvider to use the ConvexAuthNextjsProvider.
  • Adding a Middleware:
    • Create a middleware.ts file in the src folder.
    • Copy the code from the Convex documentation.
  • Setting up GitHub OAuth:
    • Create a new OAuth app on GitHub.
    • Set the authorization callback URL to your Convex HTTP actions URL with /api/auth/callback/github.
    • Set the homepage URL to your Convex HTTP actions URL.
    • Set the client ID and client secret as environment variables using bunx convex environment set.
  • Enabling the GitHub Provider:
    • Import the GitHub provider from @/convex/auth/providers/github.
    • Add the GitHub provider to the auth.config.ts file.
  • Implementing Sign-in via GitHub:
    • Import useAuthActions from @/convex-dev/auth/react.
    • Extract the signIn method.
    • Add an onClick handler to the "Continue with GitHub" button to call the signIn method.
  • Protecting Routes with Middleware:
    • Create a middleware.ts file in the src folder.
    • Import createRouteMatcher from @/convex-dev/auth.
    • Define a list of public pages (e.g., /sign-in).
    • Use the isAuthenticatedNextjs function to check if the user is authenticated.
    • Redirect unauthenticated users to the sign-in page.
  • Handling Reverse Redirect Logic:
    • Redirect authenticated users away from the sign-in page.
  • Adding Loading States:
    • Use the useState hook to manage a pending state.
    • Set the pending state to true before calling the signIn method.
    • Set the pending state to false in a finally block.
    • Disable the buttons while the pending state is true.
  • Implementing Google Login:
    • Create a new project in the Google Cloud Console.
    • Set up the OAuth consent screen.
    • Create OAuth client ID credentials.
    • Set the authorized JavaScript origins to http://localhost:3000.
    • Set the authorized redirect URIs to your Convex HTTP actions URL with /api/auth/callback/google.
    • Set the client ID and client secret as environment variables using bunx convex environment set.
    • Import the Google provider from @/convex/auth/providers/google.
    • Add the Google provider to the auth.config.ts file.
    • Add an onClick handler to the "Continue with Google" button to call the signIn method.
  • Implementing Credential Login:
    • Import the Password provider from @/convex-dev/auth/providers/password.
    • Add the Password provider to the auth.config.ts file.
    • Create a useState hook to control the email and password input values.
    • Create an onPasswordSignIn method to handle the form submission.
    • Call the signIn method with the email, password, and flow (either "sign-in" or "sign-up").
    • Implement error handling and loading states.
  • Customizing User Information:
    • Create a custom password model to pass additional user information (e.g., name).
    • Customize the schema to include the new user information.
  • Building the User Button:
    • Install the Avatar and DropdownMenu components from Shadcn UI.
    • Create a UserButton.tsx component.
    • Import the necessary components from Shadcn UI.
    • Use the useAuthActions hook to get the signOut method.
    • Use the useCurrentUser hook to fetch the current user's data.
    • Render the user's avatar and a dropdown menu with a sign-out option.

Building Workspaces

  • Defining the Workspace Schema:
    • Add a workspaces table to the convex/schema.ts file.
    • Define the properties of the workspace: name, userId, and joinCode.
  • Installing Yoai and Dialogue Components:
    • Run bun add yoai.
    • Run bunx shadcn-ui@latest add dialog.
  • Creating a Global State for the Create Workspace Model:
    • Create a src/features/workspaces/store/useCreateWorkspaceModel.ts file.
    • Import atom and useAtom from yoai.
    • Create an atom to manage the open state of the create workspace model.
    • Export a hook that returns the open and setOpen values from the atom.
  • Creating the Create Workspace Model Component:
    • Create a src/features/workspaces/components/CreateWorkspaceModel.tsx file.
    • Import the necessary components from Shadcn UI.
    • Import the useCreateWorkspaceModel hook.
    • Render a Dialog component with a form to create a new workspace.
  • Rendering the Create Workspace Model:
    • Create a src/components/Models.tsx file.
    • Import the CreateWorkspaceModel component.
    • Render the CreateWorkspaceModel component inside a fragment.
    • Import the Models component into src/app/layout.tsx.
    • Render the Models component inside the app layout.
  • Fixing Hydration Errors:
    • Use the useState and useEffect hooks to ensure that the models are only rendered on the client side.
  • Adding a Form to the Create Workspace Model:
    • Add an Input component for the workspace name.
    • Add a Button component to submit the form.
  • Creating the Create Workspace API Endpoint:
    • Create a convex/workspaces.ts file.
    • Export a create constant using the mutation function.
    • Implement the handler to insert a new workspace into the database.
  • Creating a Hook for the Create Workspace API Endpoint:
    • Create a src/features/workspaces/api/useCreateWorkspace.ts file.
    • Import useMutation from convex/react.
    • Import the API from @/convex/_generated/api.
    • Define a useCreateWorkspace hook that calls the create mutation.
  • Implementing the Handle Submit Method:
    • In CreateWorkspaceModel.tsx, create a handleSubmit method.
    • Call the mutate function with the workspace name.
    • Implement the onSuccess and onError callbacks.
  • Implementing the Redirect Logic:
    • Create a src/app/workspace/[workspaceId]/page.tsx file.
    • Implement the logic to redirect the user to the new workspace.
  • Adding a Toaster:
    • Install the sonner package: bun add sonner.
    • Import the Toaster component into src/app/layout.tsx.
    • Render the Toaster component.
    • Use the toast function to display success and error messages.

Designing the Workspace View

  • Accessing Workspace ID:
    • Use the useParams hook from next/navigation to access the dynamic route parameters.
    • Create a reusable useWorkspaceId hook to access the workspace ID.
  • Fetching Workspace Data:
    • Create a convex/workspaces.ts file.
    • Export a getById constant using the query function.
    • Implement the handler to fetch a workspace by its ID.
    • Create a src/features/workspaces/api/useGetWorkspace.ts file.
    • Import useQuery from convex/react.
    • Import the API from @/convex/_generated/api.
    • Define a useGetWorkspace hook that calls the getById query.
  • Creating a Layout:
    • Create a src/app/workspace/[workspaceId]/layout.tsx file.
    • Export a default function called WorkspaceLayout.
    • Accept a children prop of type ReactNode.
    • Render the children prop inside the layout.
  • Creating a Toolbar Component:
    • Create a src/components/Toolbar.tsx file.
    • Export a functional component called Toolbar.
    • Return a nav element with the text "Toolbar".
  • Integrating the Toolbar:
    • Import the Toolbar component into src/app/workspace/[workspaceId]/layout.tsx.
    • Render the Toolbar component inside the layout.
  • Styling the Toolbar:
    • Add Tailwind CSS classes to the nav element to style the toolbar.
  • Fetching Workspace Name:
    • Use the useGetWorkspace hook to fetch the workspace data.
    • Render the workspace name in the toolbar.
  • Creating a Sidebar Component:
    • Create a src/components/Sidebar.tsx file.
    • Export a functional component called Sidebar.
    • Return an aside element with the text "Sidebar".
  • Integrating the Sidebar:
    • Import the Sidebar component into src/app/workspace/[workspaceId]/layout.tsx.
    • Render the Sidebar component inside the layout.
  • Styling the Sidebar:
    • Add Tailwind CSS classes to the aside element to style the sidebar.
  • Creating a Workspace Switcher Component:
    • Create a src/components/WorkspaceSwitcher.tsx file.
    • Export a functional component called WorkspaceSwitcher.
    • Return a div with the text "Workspace Switcher".
  • Integrating the Workspace Switcher:
    • Import the WorkspaceSwitcher component into src/components/Sidebar.tsx.
    • Render the WorkspaceSwitcher component inside the sidebar.
  • Fetching Workspaces:
    • Create a convex/workspaces.ts file.
    • Export a get constant using the query function.
    • Implement the handler to fetch all workspaces from the database.
    • Create a src/features/workspaces/api/useGetWorkspaces.ts file.
    • Import useQuery from convex/react.
    • Import the API from @/convex/_generated/api.
    • Define a useGetWorkspaces hook that calls the get query.
  • Rendering Workspaces:
    • Use the useGetWorkspaces hook to fetch the workspaces data.
    • Iterate over the workspaces and render them in the workspace switcher.
  • Creating a Sidebar Button Component:
    • Create a src/components/SidebarButton.tsx file.
    • Export a functional component called SidebarButton.
    • Accept props for the icon, label, and active state.
    • Render a Button component with the icon and label.
  • Integrating the Sidebar Button:
    • Import the SidebarButton component into src/components/Sidebar.tsx.
    • Render the SidebarButton component inside the sidebar.
  • Styling the Sidebar Button:
    • Add Tailwind CSS classes to the Button component to style the sidebar button.
  • Dynamic Active State:
    • Use the usePathname hook from next/navigation to get the current path.
    • Use the includes method to check if the path includes the workspace ID.
    • Set the active prop of the SidebarButton based on the path.

Implementing Members Functionality and Channel Sidebar

  • Defining the Members Schema:
    • Add a members table to the convex/schema.ts file.
    • Define the properties of the member: userId, workspaceId, and role.
    • Add indexes for userId, workspaceId, and a combination of both.
  • Creating Members on Workspace Creation:
    • Modify the create mutation in convex/workspaces.ts to insert a new member with the role of "admin" when a workspace is created.
  • Modifying the Get Query:
    • Modify the get query in convex/workspaces.ts to only return workspaces that the user is a member of.
  • Creating a Proper Method:
    • Modify the generateCode method in convex/workspaces.ts to generate a more secure and random join code.
  • Creating a Custom Password Model:
    • Create a custom password model to pass additional user information (e.g., name).
    • Customize the schema to include the new user information.
  • Building the User Button:
    • Install the Avatar and DropdownMenu components from Shadcn UI.
    • Create a UserButton.tsx component.
    • Import the necessary components from Shadcn UI.
    • Use the useAuthActions hook to get the signOut method.
    • Use the useCurrentUser hook to fetch the current user's data.
    • Render the user's avatar and a dropdown menu with a sign-out option.
  • Creating a Sidebar Component:
    • Create a src/components/Sidebar.tsx file.
    • Export a functional component called Sidebar.
    • Return an aside element with the text "Sidebar".
  • Integrating the Sidebar:
    • Import the Sidebar component into src/app/workspace/[workspaceId]/layout.tsx.
    • Render the Sidebar component inside the layout.
  • Styling the Sidebar:
    • Add Tailwind CSS classes to the aside element to style the sidebar.
  • Creating a Workspace Switcher Component:
    • Create a src/components/WorkspaceSwitcher.tsx file.
    • Export a functional component called WorkspaceSwitcher.
    • Return a div with the text "Workspace Switcher".
  • Integrating the Workspace Switcher:
    • Import the WorkspaceSwitcher component into src/components/Sidebar.tsx.
    • Render the WorkspaceSwitcher component inside the sidebar.
  • Fetching Workspaces:
    • Create a convex/workspaces.ts file.
    • Export a get constant using the query function.
    • Implement the handler to fetch all workspaces from the database.
    • Create a src/features/workspaces/api/useGetWorkspaces.ts file.
    • Import useQuery from convex/react.
    • Import the API from @/convex/_generated/api.
    • Define a useGetWorkspaces hook that calls the get query.
  • Rendering Workspaces:
    • Use the useGetWorkspaces hook to fetch the workspaces data.
    • Iterate over the workspaces and render them in the workspace switcher.
  • Creating a Sidebar Button Component:
    • Create a src/components/SidebarButton.tsx file.
    • Export a functional component called SidebarButton.
    • Accept props for the icon, label, and active state.
    • Render a Button component with the icon and label.
  • Integrating the Sidebar Button:
    • Import the SidebarButton component into src/components/Sidebar.tsx.
    • Render the SidebarButton component inside the

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.