Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20
15 changes: 6 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
> [!IMPORTANT]
> This repository uses the Ably Pub/Sub approach for building chat apps. We now offer Ably Chat—a new family of SDKs and APIs that streamline development and manage realtime chat complexity for you. For a modern, easier way to create chat experiences, visit our [Ably Chat documentation](https://ably.com/docs/chat).

# Building a Realtime Chat App with Next.js, Ably, and Vercel
# Building a realtime chat app with Next.js, Ably Chat, and Vercel

Live example at: <https://next-js-chat-app.vercel.app>

Expand All @@ -13,8 +10,8 @@ This is a demo chat application with [Next.js](https://nextjs.org/) using [Ably]

It demonstrates the use of:

- Pub/sub messaging
- Ably's React Hooks
- [Ably Chat SDK](https://github.com/ably/ably-chat-js) for messaging
- [Ably Chat React hooks](https://ably.com/docs/chat/setup?lang=react) (`useMessages`, `useChatClient`)
- Token authentication with Ably

## Tech stack
Expand All @@ -23,7 +20,7 @@ The project uses the following components:

- [Next.js](https://nextjs.org/) is a React framework from [Vercel](https://vercel.com/). It is used to build static web applications with server side rendering, serverless functions and seamless hosting. It's a framework that takes the React knowledge you already have, and puts some structure and conventions in place.

- [Ably](https://ably.com/) is realtime, pub/sub messaging platform with a suite of integrated services to deliver complete realtime functionality directly to end-users.
- [Ably Chat](https://ably.com/docs/chat) is a set of SDKs built on top of Ably's realtime platform, designed for building chat experiences with minimal setup.

- [Vercel](https://vercel.com/) is a hosting platform, built from the ground up to host Next.js apps, and Serverless Functions with them.

Expand All @@ -34,7 +31,7 @@ The project uses the following components:
![The UI of the chat app we'll build. It is a window with speech bubbles for text.](https://cdn.glitch.com/0cb30add-c9ef-4c00-983c-e12deb0d4080%2Fchatapp.png?v=1612279601157)
*The UI of the app we'll build with this walkthrough*

We'll build a realtime chat app that runs in the browser. It will be built upon the Next.js [create-next-app](https://nextjs.org/docs/api-reference/create-next-app) template, it will contain a React component which will use Ably to send and receive messages. We'll also write a Next.js serverless function which will be used to connect to Ably.
We'll build a realtime chat app that runs in the browser. It will be built upon the Next.js [create-next-app](https://nextjs.org/docs/api-reference/create-next-app) template, it will contain a React component which will use the Ably Chat SDK to send and receive messages. We'll also write a Next.js [Route Handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) which will be used to authenticate with Ably.

## Building & running locally

Expand All @@ -44,7 +41,7 @@ In order to build and deploy this app, you will need:

- **An Ably account** for sending messages: [Create an account with Ably for free](https://ably.com/signup).
- **A Vercel Account** for hosting on production: [Create an account with Vercel for free](https://vercel.com/signup).
- **Node 16** or greater: [Install Node](https://nodejs.org/en/).
- **Node 20** or greater: [Install Node](https://nodejs.org/en/).

You'll also need an API key from Ably to authenticate with the Ably Service. To get an API key, once you have [created an Ably account](https://ably.com/signup):

Expand Down
7 changes: 5 additions & 2 deletions app/layout.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import './globals.css';

export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
title: 'Realtime Chat App with Ably, NextJS and Vercel',
description: 'A demo chat application with Next.js using Ably Chat',
icons: {
icon: { url: 'https://static.ably.dev/motif-red.svg?nextjs-vercel', type: 'image/svg+xml' },
},
};

export default function RootLayout({ children }) {
Expand Down
12 changes: 1 addition & 11 deletions app/page.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
import Head from 'next/head';
import dynamic from 'next/dynamic';

const Chat = dynamic(() => import('../components/Chat'), {
ssr: false,
});
import Chat from '../components/Chat';

export default function Home() {
return (
<div className="container">
<Head>
<title>Realtime Chat App with Ably, NextJS and Vercel</title>
<link rel="icon" href="https://static.ably.dev/motif-red.svg?nextjs-vercel" type="image/svg+xml" />
</Head>

<main>
<h1 className="title">Next.js Chat Demo</h1>
<Chat />
Expand Down
21 changes: 15 additions & 6 deletions components/Chat.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
'use client';

import { useEffect, useState } from 'react';
import * as Ably from 'ably';
import { ChatClient } from '@ably/chat';
import { ChatClientProvider, ChatRoomProvider } from '@ably/chat/react';
import ChatBox from './ChatBox.jsx';

const roomOptions = {
history: { limit: 50 },
};
const roomOptions = {};

export default function Chat() {
const realtimeClient = new Ably.Realtime({ authUrl: '/api' });
const chatClient = new ChatClient(realtimeClient);
const [chatClient, setChatClient] = useState(null);

useEffect(() => {
const realtimeClient = new Ably.Realtime({ authUrl: '/api' });
const client = new ChatClient(realtimeClient);
setChatClient(client);
return () => {
realtimeClient.close();
};
}, []);

if (!chatClient) return <div>Loading...</div>;

return (
<ChatClientProvider client={chatClient}>
<ChatRoomProvider id="chat-demo" options={roomOptions}>
<ChatRoomProvider name="chat-demo" options={roomOptions}>
<ChatBox />
</ChatRoomProvider>
</ChatClientProvider>
Expand Down
15 changes: 3 additions & 12 deletions components/ChatBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,14 @@ export default function ChatBox() {
const [messages, setMessages] = useState([]);
const messageTextIsEmpty = messageText.trim().length === 0;

const { send: sendMessage } = useMessages({
const { sendMessage } = useMessages({
listener: (payload) => {
const newMessage = payload.message;
setMessages((prevMessages) => {
if (prevMessages.some((existingMessage) => existingMessage.isSameAs(newMessage))) {
if (prevMessages.some((existingMessage) => existingMessage.serial === newMessage.serial)) {
return prevMessages;
}

const index = prevMessages.findIndex((existingMessage) => existingMessage.after(newMessage));

const newMessages = [...prevMessages];
if (index === -1) {
newMessages.push(newMessage);
} else {
newMessages.splice(index, 0, newMessage);
}
return newMessages;
return [...prevMessages, newMessage].sort((a, b) => (a.serial < b.serial ? -1 : b.serial < a.serial ? 1 : 0));
});
},
});
Expand Down
Loading
Loading