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:

ModeIconBehavior
Ask?Text Q&A — returns answers without launching visual tours
GuideMapStep-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:

StateDescription
"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.