Chat Widget
Navieo includes a built-in floating chat widget that appears in the bottom-right corner of your app. Users type questions and receive AI-powered guided tours or text answers depending on the selected mode.
Built-in Widget
The widget is rendered by default when you use NavieoProvider:
<NavieoProvider>
{children}
{/* Chat widget is automatically included */}
</NavieoProvider>
It includes:
- A floating action button (circle with chat icon)
- A chat panel with message history
- A mode selector for switching between Ask and Guide modes
- Suggestion chips for quick queries
- Automatic loading states during tour generation
- Auto-hide during active tours
Chat Modes
The widget includes a mode selector dropdown at the bottom-left of the input area:
| Mode | Icon | Behavior |
|---|---|---|
| Ask | ? | Text Q&A — returns answers without launching visual tours |
| Guide | Map | Step-by-step visual walkthroughs that highlight UI elements (default) |
The selected mode persists in sessionStorage so it survives page navigation within the same tab.
Disabling the Built-in Widget
If you want to build a custom chat UI, disable the built-in widget:
<NavieoProvider renderChat={false}>
{children}
<MyCustomChatWidget />
</NavieoProvider>
Hiding the Widget per Route
Use the showWidget prop to conditionally hide the widget on specific pages (e.g., login):
<NavieoProvider showWidget={!isLoginPage}>
{children}
</NavieoProvider>
Building a Custom Widget
Use the useNavieo hook to access all the state and controls you need:
"use client";
import { useNavieo } from "@navieo/react";
function MyCustomChatWidget() {
const {
isChatOpen,
toggleChat,
tourState,
startTour,
chatHistory,
suggestions,
chatMode,
setChatMode,
} = useNavieo();
const handleSubmit = (query: string) => {
startTour(query);
};
return (
<div>
<button onClick={toggleChat}>
{isChatOpen ? "Close" : "Ask for help"}
</button>
{isChatOpen && (
<div>
{/* Mode selector */}
<select
value={chatMode}
onChange={(e) => setChatMode(e.target.value as "ask" | "guide")}
>
<option value="ask">Ask</option>
<option value="guide">Guide</option>
</select>
{/* Render chat messages */}
{chatHistory.map((msg, i) => (
<div key={i} className={msg.role}>
{msg.content}
</div>
))}
{/* Loading state */}
{tourState === "loading" && <p>Generating response...</p>}
{/* Suggestion chips */}
{chatHistory.length === 0 && suggestions?.map((s) => (
<button key={s.label} onClick={() => handleSubmit(s.label)}>
{s.label}
</button>
))}
{/* Input */}
<form onSubmit={(e) => {
e.preventDefault();
const input = e.currentTarget.querySelector("input");
if (input?.value) {
handleSubmit(input.value);
input.value = "";
}
}}>
<input placeholder="How do I..." />
<button type="submit">Send</button>
</form>
</div>
)}
</div>
);
}
Tour State
The tourState value tracks the current lifecycle:
| State | Description |
|---|---|
"idle" | No tour active, ready for queries |
"loading" | Query sent to API, waiting for response |
"active" | Tour is running, user is stepping through highlights |
"error" | Something went wrong (resets to idle after 2 seconds) |
Chat History
chatHistory is an array of { role: "user" | "assistant"; content: string } messages. It persists in localStorage across chat open/close and page refreshes within the same browser.