diff --git a/services/web/frontend/stories/chat.stories.js b/services/web/frontend/stories/chat.stories.js
new file mode 100644
index 0000000000..784fb91be4
--- /dev/null
+++ b/services/web/frontend/stories/chat.stories.js
@@ -0,0 +1,99 @@
+import React from 'react'
+import fetchMock from 'fetch-mock'
+import { ContextRoot } from '../js/shared/context/root-context'
+import ChatPane from '../js/features/chat/components/chat-pane'
+import {
+ stubChatStore,
+ stubUIConfig,
+ stubMathJax
+} from '../../test/frontend/features/chat/components/stubs'
+
+const ONE_MINUTE = 60 * 1000
+
+window.project_id = '1234'
+
+const user = {
+ id: 'fake_user',
+ first_name: 'mortimer',
+ email: 'fake@example.com'
+}
+
+const user2 = {
+ id: 'another_fake_user',
+ first_name: 'leopold',
+ email: 'another_fake@example.com'
+}
+
+function generateMessages(count) {
+ const messages = []
+ let timestamp = new Date().getTime() // newest message goes first
+ for (let i = 0; i <= count; i++) {
+ const author = Math.random() > 0.5 ? user : user2
+ // modify the timestamp so the previous message has 70% chances to be within 5 minutes from
+ // the current one, for grouping purposes
+ timestamp -= (4.3 + Math.random()) * ONE_MINUTE
+
+ messages.push({
+ content: `message #${i}`,
+ user: author,
+ timestamp
+ })
+ }
+ return messages
+}
+
+stubUIConfig()
+stubMathJax()
+stubChatStore({ user })
+
+export const Conversation = args =>
+Conversation.parameters = {
+ setupMocks: () => {
+ fetchMock.restore()
+ fetchMock.get(/messages/, generateMessages(35))
+ fetchMock.post(/messages/, {})
+ }
+}
+
+export const NoMessages = args =>
+NoMessages.parameters = {
+ setupMocks: () => {
+ fetchMock.restore()
+ fetchMock.get(/messages/, [])
+ }
+}
+
+export const Loading = args =>
+Loading.parameters = {
+ setupMocks: () => {
+ fetchMock.restore()
+ fetchMock.get(/messages/, generateMessages(6), {
+ delay: 1000 * 10
+ })
+ }
+}
+
+export default {
+ title: 'Chat',
+ component: ChatPane,
+ argTypes: {
+ resetUnreadMessages: { action: 'resetUnreadMessages' }
+ },
+ args: {
+ resetUnreadMessages: () => {}
+ },
+ decorators: [
+ (Story, { parameters: { setupMocks } }) => {
+ if (setupMocks) setupMocks()
+ return
+ },
+ Story => (
+ <>
+
+
+
+
+ >
+ )
+ ]
+}