Customization
Navieo is designed to be flexible. Here are the main ways to customize its behavior.
Suggestion Chips
Configure the suggestion chips shown when the chat is empty:
<NavieoProvider
suggestions={[
{ label: "How do I create a project?" },
{ label: "Where are billing settings?", tag: "Popular" },
{ label: "How do I invite my team?", tag: "New" },
{ label: "How do I export data?" },
]}
>
{children}
</NavieoProvider>
Each suggestion can have an optional tag that renders as a small colored badge next to the label.
Chat Modes
The widget includes a mode selector at the bottom-left of the chat input. Users can switch between:
- Ask — Text Q&A. Returns answers without launching visual tours.
- Guide — Step-by-step visual walkthroughs (default mode).
You can set the mode programmatically:
const { chatMode, setChatMode } = useNavieo();
// Switch to Ask mode for text-only answers
setChatMode("ask");
The selected mode persists in sessionStorage across page navigation.
Hiding the Widget per Route
Use the showWidget prop to hide the chat widget on specific pages:
"use client";
import { NavieoProvider } from "@navieo/react";
import { usePathname } from "next/navigation";
const HIDDEN_ROUTES = ["/login", "/signup", "/onboarding"];
export function Providers({ children }) {
const pathname = usePathname();
const showWidget = !HIDDEN_ROUTES.includes(pathname);
return (
<NavieoProvider showWidget={showWidget}>
{children}
</NavieoProvider>
);
}
SPA Navigation
For single-page apps, pass your router's push function to enable smooth client-side navigation during tours:
// Next.js
<NavieoProvider onNavigate={(route) => router.push(route)}>
// React Router
<NavieoProvider onNavigate={(route) => navigate(route)}>
If omitted, Navieo falls back to window.location.href which works but causes a full page reload.
Cross-Page Tours
Navieo handles multi-page tours automatically. When a tour step requires navigating to a different route:
- The provider calls your
onNavigatecallback (or falls back towindow.location.href) - It waits for the route change to complete
- After a brief delay for the page to render, it continues the tour on the new page
This works seamlessly with Next.js client-side navigation.
Tour Controls
Use the hook to programmatically control tours:
const { startTour, endTour, nextStep, prevStep, goToStep } = useNavieo();
// Start a tour programmatically
await startTour("How do I set up notifications?");
// Navigate through steps
nextStep();
prevStep();
goToStep(3);
// End the tour early
endTour();
Triggering Tours from UI Elements
You can create custom trigger buttons anywhere in your app:
function HelpButton() {
const { startTour } = useNavieo();
return (
<button onClick={() => startTour("How do I use this feature?")}>
Need help?
</button>
);
}
Triggering Chat Open/Close
function NavBar() {
const { openChat, closeChat, toggleChat, isChatOpen } = useNavieo();
return (
<nav>
<button onClick={toggleChat}>
{isChatOpen ? "Close Help" : "Get Help"}
</button>
</nav>
);
}
Theming
The chat widget supports full visual customization via the theme prop. Use "dark" or "light" presets, or pass custom color variables. See the Themes page for the full reference.
<NavieoProvider theme="dark">
{children}
</NavieoProvider>
Element Selectors Best Practices
Navieo finds UI elements using standard CSS selectors. No special attributes are required — but the right selector strategy makes tours significantly more reliable.
Add IDs to key elements
The simplest and most reliable approach. Add id attributes to the elements you want Navieo to highlight:
<button id="create-project" onClick={handleCreate}>Create Project</button>
<div id="billing-section">
<h2>Billing</h2>
...
</div>
Then reference them in your sitemap:
{
"id": "create-project",
"type": "button",
"selector": { "cssSelector": "#create-project" },
"description": "Button to create a new project"
}
IDs are unique per page, fast to look up, and survive layout changes. You only need them on the 5-15 elements per page that tours will reference.
Use semantic selectors for links
Navigation links already have unique href attributes — no changes needed:
{ "cssSelector": "aside a[href='/settings']" }
{ "cssSelector": "nav a[href='/dashboard']" }
{ "cssSelector": "header a[href='/docs']" }
Avoid fragile selectors
These work but will break easily:
// Bad -- breaks if you add a section above it
{ "cssSelector": "main > div:nth-child(3)" }
// Bad -- breaks with any styling change
{ "cssSelector": ".bg-blue-600.text-white.rounded-lg" }
// Bad -- breaks if text content changes
{ "cssSelector": "button:first-of-type" }
Summary
| Do | Don't |
|---|---|
#create-project | div > div:nth-child(3) > button |
a[href='/settings'] | .bg-blue-600.text-white |
#installation | main > section:nth-of-type(2) |
[aria-label='Close'] | button:first-of-type |