diff --git a/.env b/.env
index 64c8d4b9a6eb8967d150f2f0b378984c809fce91..c6677efa614503dfed6fcc5dc5d3cb4b6a4c38dd 100755
--- a/.env
+++ b/.env
@@ -1,2 +1,3 @@
 VITE_APP_NAME=ZBH Portal
 VITE_PAGE_AFTER_LOGIN=/
+VITE_LOCALE=en-US
diff --git a/src/components/chat/Chat.jsx b/src/components/chat/Chat.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..80e82ab6f77becd359647b36ded1cc190649ddae
--- /dev/null
+++ b/src/components/chat/Chat.jsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import { useChat } from '../../contexts/Chat/ChatState';
+
+
+const Chat = ({ id, title, time }) => {
+  // #################################
+  // HOOKS
+  // #################################
+  // ### CONNECT CONTEXT
+  const { currentChatId, selectChat } = useChat();
+
+
+  // #################################
+  // FUNCTIONS
+  // #################################
+  // ### mark active chat
+  const active = (currentChatId === id ? 'bg-gray-200' : 'bg-white');
+
+  // #################################
+  // OUTPUT
+  // #################################
+  return (
+    <button onClick={() => { selectChat(id); }} className={`block p-1 hover:bg-gray-200 m-1 rounded-md ${active}`} title={title}>
+      <div className={'flex items-center p-2  cursor-pointer  '}>
+        <div className="flex-grow p-2">
+          <div className="flex justify-between text-md">
+
+            <div className="text-xs text-gray-400 dark:text-gray-300">{time}</div>
+          </div>
+          <div className="text-sm text-gray-500 dark:text-gray-400  w-40 truncate">
+            {title}
+          </div>
+        </div>
+      </div>
+    </button>
+  );
+};
+
+export default Chat;
\ No newline at end of file
diff --git a/src/components/chat/Chats.jsx b/src/components/chat/Chats.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..be28c0202ae974c3b14dcc6941e72836166d56f3
--- /dev/null
+++ b/src/components/chat/Chats.jsx
@@ -0,0 +1,77 @@
+import React, { useEffect, useState } from 'react';
+import Chat from './Chat';
+import Heading from '../font/Heading';
+import { RiArrowLeftCircleLine, RiArrowRightCircleLine } from 'react-icons/ri';
+import { useChat } from '../../contexts/Chat/ChatState';
+import { useParams } from 'react-router-dom';
+
+const Chats = () => {
+  // #################################
+  // HOOKS
+  // #################################
+  // ### showSidebar 
+  const initialShowSidebar = true;
+  const [showSidebar, setShowSidebar] = useState(initialShowSidebar);
+
+  // FETCH CHAT ID FROM URL
+  const { id } = useParams();
+
+  // ### CONNECT CONTEXT
+  const { fetchAllChats, chatHeadings } = useChat();
+
+  // ### FETCH CHATS;
+  useEffect(() => {
+    // ### on run exec this code
+    const controller = new AbortController();
+
+    const getChats = async () => {
+      try {
+        // fetch all chats (and directly show id if provided)
+        await fetchAllChats(id);
+      } catch (error) {
+        console.error(error);
+        mergeBackendValidation(error.response.status, error.response.data);
+      }
+    };
+    getChats();
+
+    // ### return will be executed on unmounting this component
+    return () => {
+      // on unmount abort request
+      controller.abort();
+    };
+  }, []);
+
+  // #################################
+  // FUNCTIONS
+  // #################################
+
+  // #################################
+  // OUTPUT
+  // #################################
+  return (
+    <div className="row-start-2 row-span-2 border-r-2 border-UhhGrey flex flex-col">
+      <Heading level="6" className="text-center">Recent</Heading>
+      <div className="p-1">
+        {showSidebar &&
+          chatHeadings.map((chat, index) => (
+            <Chat
+              key={chat.id}
+              id={chat.id}
+              title={chat.title}
+              time={new Intl.DateTimeFormat(import.meta.env.VITE_LOCALE).format(new Date(chat.created))}
+            />
+          ))
+        }
+      </div>
+      <div className="mt-auto mb-8 flex justify-end text text-4xl" title='show / hide Chat'>
+        <button onClick={() => setShowSidebar(!showSidebar)}>
+          {showSidebar ? <RiArrowLeftCircleLine /> : <RiArrowRightCircleLine />}
+
+        </button>
+      </div>
+    </div>
+  );
+};
+
+export default Chats;
\ No newline at end of file
diff --git a/src/components/chat/Message.jsx b/src/components/chat/Message.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..5b83f88472035bc4d1425d2ae8d0f0894cc48221
--- /dev/null
+++ b/src/components/chat/Message.jsx
@@ -0,0 +1,49 @@
+import React from 'react';
+
+function Message({ sender, message }) {
+  // #################################
+  // HOOKS
+  // #################################
+
+
+  // #################################
+  // FUNCTIONS
+  // #################################
+  // TODO: use tailwind merge
+  // AI css
+  let tilePosition = '';
+  let tileColor = 'bg-UhhGrey';
+  let tileBorder = 'rounded-bl-none';
+  let tileMargin = 'mr-8';
+  let senderClasses = 'text-UhhLightGrey';
+  let messageClasses = 'text-UhhWhite';
+
+  // user css
+  if (sender === 'human') {
+    tilePosition = 'justify-end';
+    tileColor = 'bg-UhhLightGrey';
+    tileBorder = 'rounded-br-none';
+    tileMargin = 'ml-8';
+    senderClasses = 'text-UhhGrey';
+    messageClasses = 'text-UhhGrey';
+  }
+
+  // #################################
+  // OUTPUT
+  // #################################
+  return (
+    <div className={`flex ${tilePosition}`} >
+      <div className={`p-3 mx-3 my-1 rounded-2xl ${tileBorder} ${tileColor} ${tileMargin}`}>
+        <div className={`text-xs flex justify-between ${senderClasses}`} >
+          <div>{sender}</div>
+        </div>
+        <div className={`${messageClasses} whitespace-pre-line`}>
+          {message}
+        </div>
+
+      </div>
+    </div>
+  );
+}
+
+export default React.memo(Message);
\ No newline at end of file
diff --git a/src/components/chat/Messages.jsx b/src/components/chat/Messages.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..8e4fdcc1be29c22bdb6b3fbe502b34af539a651c
--- /dev/null
+++ b/src/components/chat/Messages.jsx
@@ -0,0 +1,51 @@
+import React, { useEffect } from 'react';
+import Message from './Message';
+import { useChat } from '../../contexts/Chat/ChatState';
+import { Link } from 'react-router-dom';
+import { RxBookmark } from 'react-icons/rx';
+
+
+const Messages = () => {
+  // #################################
+  // HOOKS
+  // #################################
+  // ### CONNECT CONTEXT
+  const { currentChatId, fetchChatHistory, chatHistory } = useChat();
+
+  useEffect(() => {
+    // ### on run exec this code
+    const controller = new AbortController();
+    // ### fetch chat history based on current chat id
+    fetchChatHistory(currentChatId);
+    // ### return will be executed on unmounting this component
+    return () => {
+      // on unmount abort request
+      controller.abort();
+    };
+  }, [currentChatId]);
+
+
+  // #################################
+  // FUNCTIONS
+  // #################################
+
+  // #################################
+  // OUTPUT
+  // #################################
+  return (
+    <div className="row-start-2 overflow-auto">
+      {<Link to={`/onboarding/${currentChatId}`} className='text-UhhBlue' target='_blank' rel='noopener noreferrer'><RxBookmark /></Link>}
+      {chatHistory.map((prompt, index) => (
+        <Message
+          key={index}
+          sender={prompt.type}
+          message={prompt.data.content}
+        />
+      ))
+      }
+      {!chatHistory.length && <div className="text-center text-gray-500">No messages yet</div>}
+    </div>
+  );
+};
+
+export default Messages;
\ No newline at end of file
diff --git a/src/components/chat/PromptInput.jsx b/src/components/chat/PromptInput.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..cb5a328e30df151b670e91cf82fe23ffe57a365c
--- /dev/null
+++ b/src/components/chat/PromptInput.jsx
@@ -0,0 +1,81 @@
+import { zodResolver } from '@hookform/resolvers/zod';
+import React from 'react';
+import { FormProvider, useForm } from 'react-hook-form';
+import { RiSendPlane2Line } from 'react-icons/ri';
+import { z } from 'zod';
+
+import Input from '../form/Input';
+import { mergeBackendValidation } from '../../utils/ErrorHandling';
+import api from '../../utils/AxiosConfig';
+import { useChat } from '../../contexts/Chat/ChatState';
+
+function PromptInput() {
+  // #################################
+  // VALIDATION SCHEMA
+  // #################################
+  const schema = z.object({
+    input: z.string().min(1),
+    model: z.string().min(1)
+  });
+
+  // #################################
+  // HOOKS
+  // #################################
+  // ### CONNECT CONTEXT
+  const { currentChatId } = useChat();
+
+  // ### PREPARE FORM
+  const methods = useForm({
+    resolver: zodResolver(schema),
+    mode: 'onSubmit',
+    defaultValues: {
+      input: '',
+      model: 'llama2'
+    }
+  });
+
+  // #################################
+  // FUNCTIONS
+  // #################################
+  // ### HANDLE SUBMITTING FORM
+  async function handleSendForm(inputs) {
+    // invoke chatID if available
+    if (currentChatId) { inputs.chatId = currentChatId; };
+    // send data to api
+    try {
+      const result = await api.post('/ai/chat', inputs);
+      // TODO: update chat context
+      console.log("🚀 ~ handleSendForm ~ result:", result);
+
+
+    } catch (error) {
+      // merge front & backend validation errors
+      mergeBackendValidation(error.response.status, error.response.data, methods.setError);
+    }
+  }
+
+  // TODO fetch available model names from backend
+  // TODO make model a dropdown
+  // #################################
+  // OUTPUT
+  // #################################
+  return (
+    <div className="row-start-3 p-3 border-t-2 border-UhhGrey">
+      <FormProvider {...methods} >
+        <form onSubmit={methods.handleSubmit(handleSendForm)}>
+          <div className="flex content-center h-14">
+            <Input name="model" />
+            <Input name="input" type="text" placeholder="Type a message" className="block w-full h-8" />
+
+            <button type="submit" className="h-8 justify-center items-center bg-UhhBlue text-UhhWhite p-1 text-xs">
+              <RiSendPlane2Line />
+            </button>
+
+          </div>
+        </form>
+      </FormProvider>
+    </div>
+  );
+}
+
+export default React.memo(PromptInput);
\ No newline at end of file
diff --git a/src/components/form/Input.jsx b/src/components/form/Input.jsx
index 8d9f8c655a134e1b174605e30e8954d04be79dac..7ecb868c48c428db11e624bd462e24f38b6ae72e 100755
--- a/src/components/form/Input.jsx
+++ b/src/components/form/Input.jsx
@@ -42,7 +42,7 @@ function Input({ title, name, type, className, tooltip, ...props }) {
     <>
       <label htmlFor={id}>
         {props.required && <RequiredBadge />}
-        {capitalizeFirstLetter(title)}
+        {title && capitalizeFirstLetter(title)}
         {tooltip && <Tooltip>{tooltip}</Tooltip>}
 
         <input {...register(name)}
diff --git a/src/contexts/Chat/ChatContext.jsx b/src/contexts/Chat/ChatContext.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..fe0db341b20e7c64e9c40f9038b1fa7a2e565e4b
--- /dev/null
+++ b/src/contexts/Chat/ChatContext.jsx
@@ -0,0 +1,5 @@
+import { createContext } from "react";
+
+const ChatContext = createContext();
+
+export default ChatContext;
\ No newline at end of file
diff --git a/src/contexts/Chat/ChatReducer.jsx b/src/contexts/Chat/ChatReducer.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..d81d48e3fa9c8774df90e17a0bf18efa443a72c2
--- /dev/null
+++ b/src/contexts/Chat/ChatReducer.jsx
@@ -0,0 +1,15 @@
+import { CHAT_ACTIONS } from './ChatTypes';
+
+const chatReducer = (state, action) => {
+  switch (action.type) {
+    case CHAT_ACTIONS.SET_CHATS:
+    case CHAT_ACTIONS.SET_HEADINGS:
+    case CHAT_ACTIONS.UPDATE_CHATID:
+    case CHAT_ACTIONS.SET_HISTORY:
+      return action.payload;
+    default:
+      return state;
+  }
+};
+
+export default chatReducer;
\ No newline at end of file
diff --git a/src/contexts/Chat/ChatState.jsx b/src/contexts/Chat/ChatState.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..bb3cf8ddbbfdc45c1f651132c15131ddc74561da
--- /dev/null
+++ b/src/contexts/Chat/ChatState.jsx
@@ -0,0 +1,101 @@
+import React, { useContext, useReducer, useState } from 'react';
+import ChatContext from './ChatContext';
+import chatReducer from './ChatReducer';
+import { CHAT_ACTIONS } from './ChatTypes';
+import api from '../../utils/AxiosConfig';
+import { mergeBackendValidation } from '../../utils/ErrorHandling';
+
+// ### EXPORT useContext TO REDUCE NEEDED CODE IN CLIENT FILES
+export function useChat() {
+  return useContext(ChatContext);
+}
+
+function ChatState({ children }) {
+  // #################################
+  // HOOKS
+  // #################################
+
+  const [chats, dispatchChats] = useReducer(chatReducer, []);
+  const [chatHeadings, dispatchChatHeadings] = useReducer(chatReducer, []);
+  const [currentChatId, dispatchCurrentChatId] = useReducer(chatReducer, null);
+  const [chatHistory, dispatchChatHistory] = useReducer(chatReducer, []);
+
+
+
+  // #################################
+  // FUNCTIONS
+  // #################################
+  // ### FETCH CHATS
+  async function fetchAllChats(id = null) {
+    try {
+      // load all chats and save them
+      const items = await api.get('/ai/chats');
+      dispatchChats({ type: CHAT_ACTIONS.SET_CHATS, payload: items.data.chats });
+      // fetch headings from chats
+      fetchChatHeadings(items.data.chats);
+      // select chat if id is provided
+      if (id) selectChat(id);
+    } catch (error) {
+      // display errors
+      mergeBackendValidation(error.response.status, error.response.data);
+    }
+  }
+
+
+  // ### FETCH HEADINGS FROM CHATS
+  function fetchChatHeadings(chats) {
+    // create new array
+    const headings = [];
+    // loop through chats
+    chats.forEach(chat => {
+      // split history from chat object
+      const removedKey = 'chatHistory';
+      const { [removedKey]: removed, ...heading } = chat;
+      // save in array
+      headings.push(heading);
+    });
+    // save array in state
+    dispatchChatHeadings({ type: CHAT_ACTIONS.SET_HEADINGS, payload: headings });
+  }
+
+
+  // ### FETCH CHAT HISTORY
+  function fetchChatHistory(id) {
+    // return empty if no chat was chosen
+    if (!id) return [];
+    // define a function to find the chat
+    const isSelectedChatId = element => element.id === id;
+    // get index of matching item
+    const index = chats.findIndex(isSelectedChatId);
+    // return history of chat
+    const history = chats[index].chatHistory;
+    // save history in state
+    dispatchChatHistory({ type: CHAT_ACTIONS.SET_HISTORY, payload: history });
+  }
+
+
+  // ### SELECT CHAT
+  function selectChat(id) {
+    // return null if no id is provided (just to prevent switching between null and undefined)
+    if (!id) id = null;
+    // save chat id in state
+    dispatchCurrentChatId({ type: CHAT_ACTIONS.UPDATE_CHATID, payload: id });
+  }
+  // #################################
+  // OUTPUT
+  // #################################
+
+  return (
+    <ChatContext.Provider value={{
+      fetchAllChats,
+      chatHeadings,
+      currentChatId,
+      selectChat,
+      fetchChatHistory,
+      chatHistory
+    }}>
+      {children}
+    </ChatContext.Provider>
+  );
+}
+export default React.memo(ChatState);
diff --git a/src/contexts/Chat/ChatTypes.js b/src/contexts/Chat/ChatTypes.js
new file mode 100644
index 0000000000000000000000000000000000000000..7b4fd86be1be13e03d8e09623cd15ef9c6186541
--- /dev/null
+++ b/src/contexts/Chat/ChatTypes.js
@@ -0,0 +1,6 @@
+export const CHAT_ACTIONS = {
+  SET_CHATS: 'set_chats',
+  SET_HEADINGS: 'set_headings',
+  SET_HISTORY: 'set_history',
+  UPDATE_CHATID: 'update_chatid'
+};
\ No newline at end of file
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
index 87e754dadcad50514fc91cc2a23430be7935bb7b..790eb305948417f0d4e64e1b8e1fb28c60385b18 100644
--- a/src/pages/Home.jsx
+++ b/src/pages/Home.jsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import Heading from '../components/font/Heading';
 
 function Home() {
   // #################################
@@ -13,9 +14,7 @@ function Home() {
   // OUTPUT
   // #################################
   return (
-    <h1 className="text-3xl font-bold underline">
-      Hello world!
-    </h1>
+    <Heading level="1">Home</Heading>
   );
 }
 
diff --git a/src/pages/Onboarding/Onboarding.jsx b/src/pages/Onboarding/Onboarding.jsx
index d9dd41470e023d38c843394df8fd6b94b304925a..2a2640b37639658a5600876d16e3b5a3f22e07b9 100644
--- a/src/pages/Onboarding/Onboarding.jsx
+++ b/src/pages/Onboarding/Onboarding.jsx
@@ -1,10 +1,18 @@
 import React from 'react';
+import { Helmet } from 'react-helmet-async';
+import Heading from '../../components/font/Heading';
+import Chats from '../../components/chat/Chats';
+import Messages from '../../components/chat/Messages';
+import PromptInput from '../../components/chat/PromptInput';
+import ChatState from '../../contexts/Chat/ChatState';
+
 
 function Onboarding() {
   // #################################
   // HOOKS
   // #################################
 
+
   // #################################
   // FUNCTIONS
   // #################################
@@ -13,8 +21,18 @@ function Onboarding() {
   // OUTPUT
   // #################################
   return (
-    <div>TEST Onboarding</div>
+    <div className="h-full grid grid-rows-[auto_1fr_auto] grid-cols-[auto_1fr]">
+      {/* render page title */}
+      <Helmet><title>[{import.meta.env.VITE_APP_NAME}] Onboarding</title></Helmet>
+
+      <Heading level="1" className="col-span-2">Onboarding</Heading>
+      <ChatState>
+        <Chats />
+        <Messages />
+        <PromptInput />
+      </ChatState>
+    </div>
   );
 }
 
-export default React.memo(Onboarding);
\ No newline at end of file
+export default React.memo(Onboarding);;
\ No newline at end of file
diff --git a/src/routes/Sitemap.jsx b/src/routes/Sitemap.jsx
index 0b7664b104e5268d7e4bbaf0f5433e408912d7b3..ddb8a2262b6213cf53a0aeec0430c33f9b6ebbcd 100644
--- a/src/routes/Sitemap.jsx
+++ b/src/routes/Sitemap.jsx
@@ -20,8 +20,12 @@ export const sitemap = [{
       {
         title: 'Onboarding',
         path: '/onboarding',
-        element: loadComponent('Onboarding/Onboarding', true, true),
-        handle: { crumb: () => <Link to="/onboarding">Onboarding</Link> }
+        handle: { crumb: () => <Link to="/onboarding">Onboarding</Link> },
+        children: [
+          { index: true, element: loadComponent('Onboarding/Onboarding') },
+          { title: 'Chat', path: ':id', element: loadComponent('Onboarding/Onboarding') }
+
+        ]
       },
       // REQUEST CHANGE EMAIL ADDRESS
       {