Logo
    Login
    Hackerspace
    • Learn
    • Colleges
    • Hackers
    Career
    • Jobs
    • Applications
    Profile
    • Login as Hacker
    Paul College

    Clash of Frameworks

    0 / 10 chapters0%
    Course Introduction
    Next.js
    Configuration
    Creating a chat list
    Creating an AI chat
    Next steps
    Tanstack Start
    Configuration
    Creating a chat list
    Creating an AI chat
    Next steps
    React SPA
    Configuration
    Comparison
    1. Clash of Frameworks
    2. Creating a chat list

    Creating a chat list

    In this chapter, we'll redesign our layout to include a sidebar with a chat list on the left and chat content as the main area. For now, the main content will remain empty as we focus on building the chat list functionality.

    Need Help?

    If you encounter any difficulties, you can always reference the complete code from this GitHub repository:

    https://github.com/pouljohn1/nextjs-chat

    Prerequisites

    First, we need to install some shadcn components. Run this command in your terminal:

    bash

    If the CLI doesn't install icons automatically, run bun add lucide-react.

    This creates new components in the components/ui folder. Feel free to explore them to see how they're built.

    Cleaning Up the Default Content

    Now we need to remove Next.js example web page content. First let's move to app/page.tsx and remove everything and leave just simple div component:

    typescript

    This will be our chat content area in the future.

    Configuring the Layout

    In Next.js, the layout file wraps all page content. Let's add the shadcn sidebar here to display our list of chats.

    Open app/layout.tsx and replace its content with:

    typescript

    You'll see an error about a missing @/components/ChatSidebar file. Let's create it!

    Create a new file ChatSidebar.tsx in the components/ folder with the following content:

    typescript

    Great! Now we have the backbone of our application. In your browser, you should see something like this:

    Basic Layout

    Creating the Chat Storage

    Next, we'll create the code for storing chats. First, we need to define a Chat type. Create a new folder and file at types/chat.ts with this content:

    types/chat.tstypescript

    Next, we'll add a simple in-memory storage with database functions. Create db/chat.ts with the following content:

    db/chat.tstypescript

    We've created two functions here:

    • getChats - Retrieves chats from the local chats array. Note that we're using use cache and cacheTag directives. This makes any component using it a cached static component.
    • createChat - Creates a new chat. Note that at the end we call updateTag, which invalidates the cached page and triggers a re-fetch.

    Also notice import 'server-only'; at the top. This prevents these functions from being used in client components, ensuring we don't accidentally call them from the browser.

    Creating Server Actions

    Now we have database-related code, let's add some actions! Create a folder actions and a file actions/chat.ts inside it with this content:

    actions/chat.tstypescript

    The first thing you'll notice is the use server directive. This tells Next.js that the code in this file contains Server Functions (also known as Server Actions). When Next.js sees this, it automatically creates API endpoints under the hood that client components can call simply by invoking createChatAction. Magic!

    Fetching Data

    Now we have all the infrastructure needed to connect it to our components. Go to app/layout.tsx and add getChats as follows:

    app/layout.tsxtypescript

    As you can see, we made several changes:

    • Made RootLayout an async server component (required to use await getChats())
    • Added getChats() call to fetch chats
    • Passed the chats data to ChatSidebar

    Is it safe to call getChats() directly in a component?

    You might be wondering whether calling getChats(), which is a database function, directly in a component is safe. What if it were called on the client side? Wouldn't that make it vulnerable by exposing private data?

    The answer is no, it's completely safe! Since RootLayout is a Server Component, getChats() will only be called on the server. The results, along with the component JSX, are then passed to the client. No database credentials or sensitive logic ever reach the browser. Pure magic! ✨

    Displaying Chats

    Now let's update components/ChatSidebar.tsx to display the chats:

    components/ChatSidebar.tsxtypescript

    What we've done here is iterate over the received chats and create an item for each one. If there are no chats, we display a "no chats" message.

    You should see something like this:

    No chats image

    Creating a New Chat

    Now let's add the ability to create new chats! Add a new section at the start of the <SidebarContent> component:

    components/ChatSidebar.tsxtypescript

    What did we do here? First, we imported createChatAction and set it as the action for the <form> component. Inside the form, there's a New Chat button that triggers form submission and calls our Server Action.

    What is .bind() and what does it do?

    Here's some JavaScript magic: .bind() creates a new function with a specified this context (first parameter) and pre-filled arguments. In this case, we're adding the 'New Chat' title argument to the action without creating an arrow function.

    Now you can test it! Click the New Chat button in your browser. After a few clicks, you should see a similar view:

    Chats image

    Better Storage

    Now we can create and view chats, but there are some problems with our current approach:

    • Chats are not persistent
    • Chats get removed when code changes (try modifying the code to see this happen)

    Why does this happen? It's because of how Next.js is built. All Next.js server functions are designed to run in a serverless environment. This means each backend call runs on a separate instance, so the chats variable gets reset to an empty array on each new instance.

    We need a better approach. Normally you would use a database or cloud storage, but for this course we'll store data locally as a file. This will persist through refreshes and across server function calls.

    Let's get started! First, we need to create utilities for file storage. Create lib/fileUtils.ts with the following content:

    lib/fileUtils.tstypescript

    This utility provides two functions:

    • readContent - Reads and parses JSON data from a file, creating the directory if it doesn't exist
    • writeContent - Writes data to a file as formatted JSON

    Next, we need to update our db/chat.ts functions to use these utilities:

    db/chat.tstypescript

    Key changes we made:

    • Removed the in-memory chats array
    • getChats() now reads from chats.json file
    • createChat() reads existing chats, generates a proper ID (using the maximum existing ID + 1), adds the new chat, and writes back to the file

    Now when you add chats and refresh the page, they should persist. Great work!

    Next Steps

    Great! We now have our first working feature. Users can create and view chat objects. Remember, this isn't just frontend code - we already have a backend with server components, server actions, and caching!

    Ready to move on?

    Mark this chapter as finished to continue

    Ready to move on?

    Mark this chapter as finished to continue

    LoginLogin to mark
    Chapter completed!
    NextGo to Next Chapter

    © 2025 Hacklab

    • Privacy
    • Terms