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
21 changes: 12 additions & 9 deletions examples/tutorial/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
This folder contains the source code for [Chat React tutorial](https://github.com/GetStream/getstream.io-tutorials/blob/main/chat/tutorials/react-tutorial.mdx). It contains multiple versions of apps representing the tutorial steps.

The tutorial app is wired against the local `stream-chat-react` checkout so each step stays aligned with the current SDK implementation.

## Setup

1. Copy create a `.env` file next to the `.env.example` file.
2. Copy the contents of `.env.example` file into `.env` file and populate the credentials

## Run individual app examples
## Run the tutorial browser

```shell
yarn dev
```

`yarn dev` starts a simple tutorial browser that lets you switch between all steps from one sidebar.

## Build the tutorial browser

```shell
yarn dev:client-setup
yarn dev:client-setup
yarn dev:core-component-setup
yarn dev:channel-list
yarn dev:custom-ui-components
yarn dev:custom-attachment-type
yarn dev:emoji-picker
yarn dev:livestream
yarn build
```
53 changes: 21 additions & 32 deletions examples/tutorial/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,30 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev:client-setup": "vite --mode 1-client-setup",
"dev:core-component-setup": "vite --mode 2-core-component-setup",
"dev:channel-list": "vite --mode 3-channel-list",
"dev:custom-ui-components": "vite --mode 4-custom-ui-components",
"dev:custom-attachment-type": "vite --mode 5-custom-attachment-type",
"dev:emoji-picker": "vite --mode 6-emoji-picker",
"dev:livestream": "vite --mode 7-livestream",
"build:client-setup": "vite build --mode 1-client-setup",
"build:core-component-setup": "vite build --mode 2-core-component-setup",
"build:channel-list": "vite build --mode 3-channel-list",
"build:custom-ui-components": "vite build --mode 4-custom-ui-components",
"build:custom-attachment-type": "vite build --mode 5-custom-attachment-type",
"build:emoji-picker": "vite build --mode 6-emoji-picker",
"build:livestream": "vite build --mode 7-livestream",
"build:all": "concurrently \"npm run build:client-setup\" \"npm run build:core-component-setup\" \"npm run build:channel-list\" \"npm run build:custom-ui-components\" \"npm run build:custom-attachment-type\" \"npm run build:emoji-picker\" \"npm run build:livestream\"",
"lint": "eslint ."
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0",
"stream-chat": "^9.0.0",
"stream-chat-react": "^13.0.0"
"@emoji-mart/data": "link:../../node_modules/@emoji-mart/data",
"emoji-mart": "link:../../node_modules/emoji-mart",
"react": "link:../../node_modules/react",
"react-dom": "link:../../node_modules/react-dom",
"stream-chat": "link:../../node_modules/stream-chat",
"stream-chat-react": "link:../../"
},
"devDependencies": {
"@eslint/js": "^9.25.0",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@vitejs/plugin-react": "^4.4.1",
"concurrently": "^9.1.2",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.30.1",
"vite": "^6.4.1"
"@eslint/js": "^9.39.4",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.57.2",
"vite": "^8.0.3"
}
}
7 changes: 1 addition & 6 deletions examples/tutorial/src/1-client-setup/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { Chat, useCreateChatClient } from 'stream-chat-react';

// your Stream app information
const apiKey = 'REPLACE_WITH_API_KEY';
const userId = 'REPLACE_WITH_USER_ID';
const userName = 'REPLACE_WITH_USER_NAME';
const userToken = 'REPLACE_WITH_USER_TOKEN';
import { apiKey, userId, userName, userToken } from './credentials';

const App = () => {
const client = useCreateChatClient({
Expand Down
32 changes: 18 additions & 14 deletions examples/tutorial/src/2-core-component-setup/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,16 @@ import {
Chat,
Channel,
ChannelHeader,
MessageInput,
MessageComposer,
MessageList,
Thread,
Window,
} from 'stream-chat-react';

import 'stream-chat-react/dist/css/v2/index.css';
import 'stream-chat-react/dist/css/index.css';
// additionally
import './layout.css';

const apiKey = 'REPLACE_WITH_API_KEY';
const userId = 'REPLACE_WITH_USER_ID';
const userName = 'REPLACE_WITH_USER_NAME';
const userToken = 'REPLACE_WITH_USER_TOKEN';
import { apiKey, userId, userName, userToken } from '../1-client-setup/credentials';

const user: User = {
id: userId,
Expand All @@ -37,24 +33,32 @@ const App = () => {
useEffect(() => {
if (!client) return;

const channel = client.channel('messaging', 'custom_channel_id', {
image: 'https://getstream.io/random_png/?name=react',
name: 'Talk about React',
members: [userId],
});
const initChannel = async () => {
const nextChannel = client.channel('messaging', 'react-tutorial', {
image: 'https://getstream.io/random_png/?name=react-v14',
name: 'Talk about React',
members: [userId],
});

setChannel(channel);
await nextChannel.watch();
setChannel(nextChannel);
};

initChannel().catch((error) => {
console.error('Failed to initialize tutorial channel', error);
});
}, [client]);

if (!client) return <div>Setting up client & connection...</div>;
if (!channel) return <div>Loading tutorial channel...</div>;

return (
<Chat client={client} theme='str-chat__theme-custom'>
<Channel channel={channel}>
<Window>
<ChannelHeader />
<MessageList />
<MessageInput />
<MessageComposer />
</Window>
<Thread />
</Channel>
Expand Down
45 changes: 34 additions & 11 deletions examples/tutorial/src/3-channel-list/App.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import type { User, ChannelSort, ChannelFilters, ChannelOptions } from 'stream-chat';
import { useEffect, useState } from 'react';
import type {
User,
Channel as StreamChannel,
ChannelSort,
ChannelFilters,
ChannelOptions,
} from 'stream-chat';
import {
useCreateChatClient,
Chat,
Channel,
ChannelHeader,
ChannelList,
MessageInput,
MessageComposer,
MessageList,
Thread,
Window,
} from 'stream-chat-react';

import 'stream-chat-react/dist/css/v2/index.css';
import './layout.css';

const apiKey = 'REPLACE_WITH_API_KEY';
const userId = 'REPLACE_WITH_USER_ID';
const userName = 'REPLACE_WITH_USER_NAME';
const userToken = 'REPLACE_WITH_USER_TOKEN';
import { apiKey, userId, userName, userToken } from '../1-client-setup/credentials';

const user: User = {
id: userId,
Expand All @@ -35,22 +37,43 @@ const options: ChannelOptions = {
};

const App = () => {
const [channel, setChannel] = useState<StreamChannel>();
const client = useCreateChatClient({
apiKey,
tokenOrProvider: userToken,
userData: user,
});

useEffect(() => {
if (!client) return;

const initChannel = async () => {
const nextChannel = client.channel('messaging', 'react-tutorial', {
image: 'https://getstream.io/random_png/?name=react-v14',
name: 'Talk about React',
members: [userId],
});

await nextChannel.watch();
setChannel(nextChannel);
};

initChannel().catch((error) => {
console.error('Failed to initialize tutorial channel', error);
});
}, [client]);

if (!client) return <div>Setting up client & connection...</div>;
if (!channel) return <div>Loading tutorial channel...</div>;

return (
<Chat client={client}>
<Chat client={client} theme='str-chat__theme-custom'>
<ChannelList filters={filters} sort={sort} options={options} />
<Channel>
<Channel channel={channel}>
<Window>
<ChannelHeader />
<MessageList />
<MessageInput />
<MessageComposer />
</Window>
<Thread />
</Channel>
Expand Down
40 changes: 28 additions & 12 deletions examples/tutorial/src/3-channel-list/layout.css
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
@layer base, theme;
@import 'stream-chat-react/dist/css/v2/index.css' layer(base);
@import 'stream-chat-react/dist/css/index.css' layer(base);

@layer theme {
.str-chat__theme-custom {
--str-chat__primary-color: #009688;
--str-chat__active-primary-color: #004d40;
--str-chat__surface-color: #f5f5f5;
--str-chat__secondary-surface-color: #fafafa;
--str-chat__primary-surface-color: #e0f2f1;
--str-chat__primary-surface-color-low-emphasis: #edf7f7;
--str-chat__border-radius-circle: 6px;
--brand-50: #edf7f7;
--brand-100: #e0f2f1;
--brand-150: #b2dfdb;
--brand-200: #80cbc4;
--brand-300: #4db6ac;
--brand-400: #26a69a;
--brand-500: #009688;
--brand-600: #00897b;
--brand-700: #00796b;
--brand-800: #00695c;
--brand-900: #004d40;
--accent-primary: var(--brand-500);
--radius-full: 18px;
--str-chat__channel-list-width: min(360px, 32%);
}
}

Expand All @@ -20,17 +27,26 @@ body,
}
body {
margin: 0;
background: linear-gradient(180deg, #f4f7ff 0%, #e8f4f3 100%);
}
#root {
display: flex;
min-height: 100%;
}

.str-chat {
flex: 1;
}

.str-chat__channel-list {
width: 30%;
width: min(360px, 32%);
}
.str-chat__channel {

.str-chat__channel,
.str-chat__thread {
width: 100%;
}

.str-chat__thread {
width: 45%;
}
width: min(420px, 45%);
}
Loading
Loading