Skip to content

Commit 86ada37

Browse files
authored
feat(examples): refresh react tutorial app for v14 (#3078)
### 🎯 Goal Update the React tutorial example app so it matches the upcoming v14 SDK APIs and is easier to explore locally. ### 🛠 Implementation details - migrate the tutorial step apps from `MessageInput` to `MessageComposer` - replace old prop-based customization examples with `WithComponents` patterns - update the tutorial examples for current channel-list, attachment, emoji, and livestream APIs - point the local tutorial app at the local `stream-chat-react` checkout and current CSS entrypoint - add a root tutorial browser with a sidebar step list and a live preview pane - move the browser shell styles into `examples/tutorial/src/tutorial-main.css` - update the tutorial Vite config and README to support the new browser flow ### 🎨 UI Changes - adds a tutorial browser shell with a left sidebar for step navigation and a right preview pane - keeps the individual tutorial steps runnable while making it easier to switch between them quickly
1 parent 3f09362 commit 86ada37

File tree

18 files changed

+1626
-2152
lines changed

18 files changed

+1626
-2152
lines changed

examples/tutorial/README.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
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.
22

3+
The tutorial app is wired against the local `stream-chat-react` checkout so each step stays aligned with the current SDK implementation.
4+
35
## Setup
46

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

8-
## Run individual app examples
10+
## Run the tutorial browser
11+
12+
```shell
13+
yarn dev
14+
```
15+
16+
`yarn dev` starts a simple tutorial browser that lets you switch between all steps from one sidebar.
17+
18+
## Build the tutorial browser
919

1020
```shell
11-
yarn dev:client-setup
12-
yarn dev:client-setup
13-
yarn dev:core-component-setup
14-
yarn dev:channel-list
15-
yarn dev:custom-ui-components
16-
yarn dev:custom-attachment-type
17-
yarn dev:emoji-picker
18-
yarn dev:livestream
21+
yarn build
1922
```

examples/tutorial/package.json

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,30 @@
44
"version": "0.0.0",
55
"type": "module",
66
"scripts": {
7-
"dev:client-setup": "vite --mode 1-client-setup",
8-
"dev:core-component-setup": "vite --mode 2-core-component-setup",
9-
"dev:channel-list": "vite --mode 3-channel-list",
10-
"dev:custom-ui-components": "vite --mode 4-custom-ui-components",
11-
"dev:custom-attachment-type": "vite --mode 5-custom-attachment-type",
12-
"dev:emoji-picker": "vite --mode 6-emoji-picker",
13-
"dev:livestream": "vite --mode 7-livestream",
14-
"build:client-setup": "vite build --mode 1-client-setup",
15-
"build:core-component-setup": "vite build --mode 2-core-component-setup",
16-
"build:channel-list": "vite build --mode 3-channel-list",
17-
"build:custom-ui-components": "vite build --mode 4-custom-ui-components",
18-
"build:custom-attachment-type": "vite build --mode 5-custom-attachment-type",
19-
"build:emoji-picker": "vite build --mode 6-emoji-picker",
20-
"build:livestream": "vite build --mode 7-livestream",
21-
"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\"",
22-
"lint": "eslint ."
7+
"dev": "vite",
8+
"build": "vite build",
9+
"lint": "eslint .",
10+
"preview": "vite preview"
2311
},
2412
"dependencies": {
25-
"react": "^19.1.0",
26-
"react-dom": "^19.1.0",
27-
"stream-chat": "^9.0.0",
28-
"stream-chat-react": "^13.0.0"
13+
"@emoji-mart/data": "link:../../node_modules/@emoji-mart/data",
14+
"emoji-mart": "link:../../node_modules/emoji-mart",
15+
"react": "link:../../node_modules/react",
16+
"react-dom": "link:../../node_modules/react-dom",
17+
"stream-chat": "link:../../node_modules/stream-chat",
18+
"stream-chat-react": "link:../../"
2919
},
3020
"devDependencies": {
31-
"@eslint/js": "^9.25.0",
32-
"@types/react": "^19.1.2",
33-
"@types/react-dom": "^19.1.2",
34-
"@vitejs/plugin-react": "^4.4.1",
35-
"concurrently": "^9.1.2",
36-
"eslint": "^9.25.0",
37-
"eslint-plugin-react-hooks": "^5.2.0",
38-
"eslint-plugin-react-refresh": "^0.4.19",
39-
"globals": "^16.0.0",
40-
"typescript": "~5.8.3",
41-
"typescript-eslint": "^8.30.1",
42-
"vite": "^6.4.1"
21+
"@eslint/js": "^9.39.4",
22+
"@types/react": "^19.2.14",
23+
"@types/react-dom": "^19.2.3",
24+
"@vitejs/plugin-react": "^6.0.1",
25+
"eslint": "^9.39.4",
26+
"eslint-plugin-react-hooks": "^7.0.1",
27+
"eslint-plugin-react-refresh": "^0.5.2",
28+
"globals": "^17.4.0",
29+
"typescript": "^5.9.3",
30+
"typescript-eslint": "^8.57.2",
31+
"vite": "^8.0.3"
4332
}
4433
}

examples/tutorial/src/1-client-setup/App.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { Chat, useCreateChatClient } from 'stream-chat-react';
2-
3-
// your Stream app information
4-
const apiKey = 'REPLACE_WITH_API_KEY';
5-
const userId = 'REPLACE_WITH_USER_ID';
6-
const userName = 'REPLACE_WITH_USER_NAME';
7-
const userToken = 'REPLACE_WITH_USER_TOKEN';
2+
import { apiKey, userId, userName, userToken } from './credentials';
83

94
const App = () => {
105
const client = useCreateChatClient({

examples/tutorial/src/2-core-component-setup/App.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,16 @@ import {
55
Chat,
66
Channel,
77
ChannelHeader,
8-
MessageInput,
8+
MessageComposer,
99
MessageList,
1010
Thread,
1111
Window,
1212
} from 'stream-chat-react';
1313

14-
import 'stream-chat-react/dist/css/v2/index.css';
14+
import 'stream-chat-react/dist/css/index.css';
1515
// additionally
1616
import './layout.css';
17-
18-
const apiKey = 'REPLACE_WITH_API_KEY';
19-
const userId = 'REPLACE_WITH_USER_ID';
20-
const userName = 'REPLACE_WITH_USER_NAME';
21-
const userToken = 'REPLACE_WITH_USER_TOKEN';
17+
import { apiKey, userId, userName, userToken } from '../1-client-setup/credentials';
2218

2319
const user: User = {
2420
id: userId,
@@ -37,24 +33,32 @@ const App = () => {
3733
useEffect(() => {
3834
if (!client) return;
3935

40-
const channel = client.channel('messaging', 'custom_channel_id', {
41-
image: 'https://getstream.io/random_png/?name=react',
42-
name: 'Talk about React',
43-
members: [userId],
44-
});
36+
const initChannel = async () => {
37+
const nextChannel = client.channel('messaging', 'react-tutorial', {
38+
image: 'https://getstream.io/random_png/?name=react-v14',
39+
name: 'Talk about React',
40+
members: [userId],
41+
});
4542

46-
setChannel(channel);
43+
await nextChannel.watch();
44+
setChannel(nextChannel);
45+
};
46+
47+
initChannel().catch((error) => {
48+
console.error('Failed to initialize tutorial channel', error);
49+
});
4750
}, [client]);
4851

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

5155
return (
5256
<Chat client={client} theme='str-chat__theme-custom'>
5357
<Channel channel={channel}>
5458
<Window>
5559
<ChannelHeader />
5660
<MessageList />
57-
<MessageInput />
61+
<MessageComposer />
5862
</Window>
5963
<Thread />
6064
</Channel>

examples/tutorial/src/3-channel-list/App.tsx

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1-
import type { User, ChannelSort, ChannelFilters, ChannelOptions } from 'stream-chat';
1+
import { useEffect, useState } from 'react';
2+
import type {
3+
User,
4+
Channel as StreamChannel,
5+
ChannelSort,
6+
ChannelFilters,
7+
ChannelOptions,
8+
} from 'stream-chat';
29
import {
310
useCreateChatClient,
411
Chat,
512
Channel,
613
ChannelHeader,
714
ChannelList,
8-
MessageInput,
15+
MessageComposer,
916
MessageList,
1017
Thread,
1118
Window,
1219
} from 'stream-chat-react';
1320

14-
import 'stream-chat-react/dist/css/v2/index.css';
1521
import './layout.css';
16-
17-
const apiKey = 'REPLACE_WITH_API_KEY';
18-
const userId = 'REPLACE_WITH_USER_ID';
19-
const userName = 'REPLACE_WITH_USER_NAME';
20-
const userToken = 'REPLACE_WITH_USER_TOKEN';
22+
import { apiKey, userId, userName, userToken } from '../1-client-setup/credentials';
2123

2224
const user: User = {
2325
id: userId,
@@ -35,22 +37,43 @@ const options: ChannelOptions = {
3537
};
3638

3739
const App = () => {
40+
const [channel, setChannel] = useState<StreamChannel>();
3841
const client = useCreateChatClient({
3942
apiKey,
4043
tokenOrProvider: userToken,
4144
userData: user,
4245
});
4346

47+
useEffect(() => {
48+
if (!client) return;
49+
50+
const initChannel = async () => {
51+
const nextChannel = client.channel('messaging', 'react-tutorial', {
52+
image: 'https://getstream.io/random_png/?name=react-v14',
53+
name: 'Talk about React',
54+
members: [userId],
55+
});
56+
57+
await nextChannel.watch();
58+
setChannel(nextChannel);
59+
};
60+
61+
initChannel().catch((error) => {
62+
console.error('Failed to initialize tutorial channel', error);
63+
});
64+
}, [client]);
65+
4466
if (!client) return <div>Setting up client & connection...</div>;
67+
if (!channel) return <div>Loading tutorial channel...</div>;
4568

4669
return (
47-
<Chat client={client}>
70+
<Chat client={client} theme='str-chat__theme-custom'>
4871
<ChannelList filters={filters} sort={sort} options={options} />
49-
<Channel>
72+
<Channel channel={channel}>
5073
<Window>
5174
<ChannelHeader />
5275
<MessageList />
53-
<MessageInput />
76+
<MessageComposer />
5477
</Window>
5578
<Thread />
5679
</Channel>
Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
@layer base, theme;
2-
@import 'stream-chat-react/dist/css/v2/index.css' layer(base);
2+
@import 'stream-chat-react/dist/css/index.css' layer(base);
33

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

@@ -20,17 +27,26 @@ body,
2027
}
2128
body {
2229
margin: 0;
30+
background: linear-gradient(180deg, #f4f7ff 0%, #e8f4f3 100%);
2331
}
2432
#root {
2533
display: flex;
34+
min-height: 100%;
35+
}
36+
37+
.str-chat {
38+
flex: 1;
2639
}
2740

2841
.str-chat__channel-list {
29-
width: 30%;
42+
width: min(360px, 32%);
3043
}
31-
.str-chat__channel {
44+
45+
.str-chat__channel,
46+
.str-chat__thread {
3247
width: 100%;
3348
}
49+
3450
.str-chat__thread {
35-
width: 45%;
36-
}
51+
width: min(420px, 45%);
52+
}

0 commit comments

Comments
 (0)