Skip to content

Commit 84925de

Browse files
feat(example): React AI tutorial
1 parent c540aa5 commit 84925de

File tree

17 files changed

+1949
-295
lines changed

17 files changed

+1949
-295
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
VITE_STREAM_API_KEY=""
2+
VITE_STREAM_USER_TOKEN=""
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# React + TypeScript + Vite
2+
3+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4+
5+
Currently, two official plugins are available:
6+
7+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
8+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9+
10+
## React Compiler
11+
12+
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
13+
14+
## Expanding the ESLint configuration
15+
16+
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
17+
18+
```js
19+
export default defineConfig([
20+
globalIgnores(['dist']),
21+
{
22+
files: ['**/*.{ts,tsx}'],
23+
extends: [
24+
// Other configs...
25+
26+
// Remove tseslint.configs.recommended and replace with this
27+
tseslint.configs.recommendedTypeChecked,
28+
// Alternatively, use this for stricter rules
29+
tseslint.configs.strictTypeChecked,
30+
// Optionally, add this for stylistic rules
31+
tseslint.configs.stylisticTypeChecked,
32+
33+
// Other configs...
34+
],
35+
languageOptions: {
36+
parserOptions: {
37+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
38+
tsconfigRootDir: import.meta.dirname,
39+
},
40+
// other options...
41+
},
42+
},
43+
])
44+
```
45+
46+
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
47+
48+
```js
49+
// eslint.config.js
50+
import reactX from 'eslint-plugin-react-x'
51+
import reactDom from 'eslint-plugin-react-dom'
52+
53+
export default defineConfig([
54+
globalIgnores(['dist']),
55+
{
56+
files: ['**/*.{ts,tsx}'],
57+
extends: [
58+
// Other configs...
59+
// Enable lint rules for React
60+
reactX.configs['recommended-typescript'],
61+
// Enable lint rules for React DOM
62+
reactDom.configs.recommended,
63+
],
64+
languageOptions: {
65+
parserOptions: {
66+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
67+
tsconfigRootDir: import.meta.dirname,
68+
},
69+
// other options...
70+
},
71+
},
72+
])
73+
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import js from '@eslint/js'
2+
import globals from 'globals'
3+
import reactHooks from 'eslint-plugin-react-hooks'
4+
import reactRefresh from 'eslint-plugin-react-refresh'
5+
import tseslint from 'typescript-eslint'
6+
import { defineConfig, globalIgnores } from 'eslint/config'
7+
8+
export default defineConfig([
9+
globalIgnores(['dist']),
10+
{
11+
files: ['**/*.{ts,tsx}'],
12+
extends: [
13+
js.configs.recommended,
14+
tseslint.configs.recommended,
15+
reactHooks.configs.flat.recommended,
16+
reactRefresh.configs.vite,
17+
],
18+
languageOptions: {
19+
ecmaVersion: 2020,
20+
globals: globals.browser,
21+
},
22+
},
23+
])
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>react-chat-ai-tutorial</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "react-chat-ai-tutorial",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc -b && vite build",
9+
"lint": "eslint .",
10+
"preview": "vite preview"
11+
},
12+
"dependencies": {
13+
"@stream-io/chat-react-ai": "workspace:^",
14+
"react": "^19.2.0",
15+
"react-dom": "^19.2.0",
16+
"stream-chat": "^9.26.1",
17+
"stream-chat-react": "^13.13.0"
18+
},
19+
"devDependencies": {
20+
"@eslint/js": "^9.39.1",
21+
"@types/node": "^24.10.1",
22+
"@types/react": "^19.2.5",
23+
"@types/react-dom": "^19.2.3",
24+
"@vitejs/plugin-react": "^5.1.1",
25+
"eslint": "^9.39.1",
26+
"eslint-plugin-react-hooks": "^7.0.1",
27+
"eslint-plugin-react-refresh": "^0.4.24",
28+
"globals": "^16.5.0",
29+
"typescript": "~5.9.3",
30+
"typescript-eslint": "^8.46.4",
31+
"vite": "^7.2.4"
32+
}
33+
}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import type { ChannelFilters, ChannelOptions, ChannelSort } from "stream-chat";
2+
import {
3+
Chat,
4+
Channel,
5+
MessageList,
6+
useCreateChatClient,
7+
ChannelList,
8+
Window,
9+
} from "stream-chat-react";
10+
11+
const userToken = import.meta.env.VITE_STREAM_USER_TOKEN;
12+
const apiKey = import.meta.env.VITE_STREAM_API_KEY;
13+
14+
console.log(import.meta, userToken, apiKey);
15+
16+
if (typeof apiKey !== "string" || !apiKey.length) {
17+
throw new Error("Missing VITE_STREAM_API_KEY");
18+
}
19+
20+
if (typeof userToken !== "string" || !userToken.length) {
21+
throw new Error("Missing VITE_STREAM_USER_TOKEN");
22+
}
23+
24+
const userIdFromToken = (token: string) => {
25+
const [, payload] = token.split(".");
26+
const parsedPayload = JSON.parse(atob(payload));
27+
return parsedPayload.user_id as string;
28+
};
29+
30+
const userId = userIdFromToken(userToken!);
31+
32+
const filters: ChannelFilters = {
33+
members: { $in: [userId] },
34+
type: "messaging",
35+
archived: false,
36+
};
37+
const options: ChannelOptions = { limit: 5 };
38+
const sort: ChannelSort = { pinned_at: 1, last_message_at: -1, updated_at: -1 };
39+
40+
function App() {
41+
const chatClient = useCreateChatClient({
42+
apiKey: apiKey!,
43+
tokenOrProvider: userToken!,
44+
userData: {
45+
id: userId,
46+
},
47+
});
48+
49+
if (!chatClient) {
50+
return <div>Loading chat...</div>;
51+
}
52+
53+
return (
54+
<Chat client={chatClient}>
55+
<ChannelList filters={filters} sort={sort} options={options} />
56+
<Channel>
57+
<Window>
58+
<MessageList />
59+
</Window>
60+
</Channel>
61+
</Chat>
62+
);
63+
}
64+
65+
export default App;
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@layer stream, stream-overrides;
2+
3+
@import url("stream-chat-react/dist/css/v2/index.css") layer(stream);
4+
5+
:root {
6+
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
7+
line-height: 1.5;
8+
font-weight: 400;
9+
10+
color-scheme: light dark;
11+
color: rgba(255, 255, 255, 0.87);
12+
background-color: #242424;
13+
14+
font-synthesis: none;
15+
text-rendering: optimizeLegibility;
16+
-webkit-font-smoothing: antialiased;
17+
-moz-osx-font-smoothing: grayscale;
18+
}
19+
20+
body {
21+
margin: 0;
22+
}
23+
24+
#root {
25+
display: flex;
26+
justify-content: stretch;
27+
}
28+
29+
@layer stream-overrides {
30+
31+
.str-chat__channel-list {
32+
display: flex;
33+
flex-shrink: 1;
34+
flex-basis: 400px;
35+
height: 100%;
36+
}
37+
38+
.str-chat__channel {
39+
flex-grow: 1;
40+
flex-shrink: 0;
41+
}
42+
43+
}

0 commit comments

Comments
 (0)