Scaffolding our frontend
Start by setting up a default Nuxt 3 repo.
Weβre going to style this app with TailwindCSS. To do this, I installed the Nuxt Tailwind module and then added it to my nuxt.config.ts
:
export default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss']
})
Letβs scaffold out a very simple UI with a form input, that we can use to start testing.
To do this, I set up a simple component called ChatWidget
, which houses our layout, and a nested component called Chain
in the parts
folder, where we can handle the UI around the Q&A chain.
// components/ChatWidget.vue
<template>
<div class="w-screen h-screen flex flex-col bg-gray-50">
<div class="w-full relative overflow-hidden flex flex-grow">
<div class="flex flex-col w-full h-full">
<header
class="flex items-center px-6 h-14 w-full bg-white border-b border-gray-200 shadow-sm fixed top-0 z-10"
>
<h1 class="font-semibold text-lg">My Q&A chat tool</h1>
</header>
<div
class="p-6 mt-14 h-full flex flex-col-reverse overflow-y-auto"
>
<PartsChain />
</div>
</div>
</div>
</div>
</template>
// components/parts/Chain.vue
<template>
<form class="flex flex-col w-full">
<label class="font-medium text-gray-600 mb-2.5"
>Ask our knowledge base a question</label
>
<input
type="text"
placeholder="Ask your question..."
class="h-14 px-3 border border-gray-200 max-w-lg"
/>
<div class="font-medium text-xs text-gray-400 mt-1.5">
<p>Press enter to ask...</p>
</div>
</form>
</template>
Perfect! Now we have this:

Letβs add some logic to Chain.vue
to simulate async messaging. This will allow us to scaffold out some basic loading state logic.
Iβll add a <script setup lang="ts">
and mock an askQuestion
method with a setTimeout Promise hack.
// in script setup
const error = ref<string | null>(null);
const isLoadingMessage = ref<boolean>(false);
async function askQuestion() {
try {
isLoadingMessage.value = true;
await new Promise((resolve) => setTimeout(resolve, 1000));
} catch (e) {
console.error(e);
error.value = 'Unfortunately, there was an issue answering your question.';
} finally {
isLoadingMessage.value = false;
}
}
And now, letβs update the template:
<template>
<form @submit.prevent="askQuestion" class="flex flex-col w-full">
<label class="font-medium text-gray-600 mb-2.5"
>Ask our knowledge base a question</label
>
<input
type="text"
placeholder="Ask your question..."
class="h-14 px-3 border border-gray-200 max-w-lg"
/>
<div class="font-medium text-xs text-gray-400 mt-1.5">
<p v-if="!isLoadingMessage">Press enter to ask...</p>
<p class="animate-pulse" v-else>Analysing the knowledge base...</p>
</div>
</form>
</template>
Voila! We have our scaffolded chat interface. When you press enter in the form, it triggers askQuestion
, which displays a loading state for 1000ms.
Updated 5 months ago