Vite with React
Mount Docent in a Vite and React app with no router. The SDK falls back to pushState, and this page says what that means for navigation.
An app with no client-side router needs no adapter. Omit router and the SDK uses the browser's
own history.
The mount
import { Docent } from '@usedocent/react'
import { PRESENCE_LOADERS } from '@usedocent/mascot/library/loaders'
export const Guide = ({ userId, children }: { userId: string; children: React.ReactNode }) => (
<Docent
appId={import.meta.env.VITE_DOCENT_APP_ID}
publishableKey={import.meta.env.VITE_DOCENT_PUBLISHABLE_KEY}
apiUrl="https://api.usedocent.com"
user={{ id: userId }}
loadPresence={(slug) => PRESENCE_LOADERS[slug]?.() ?? Promise.reject(new Error(slug))}
loadAudio={(vendor) =>
vendor === 'openai' ? import('@usedocent/sdk-audio-live') : import('@usedocent/sdk-audio')
}
voice="tap"
>
{children}
</Docent>
)Mount it once, around your app:
createRoot(document.getElementById('root')!).render(
<StrictMode>
<Guide userId={userId}>
<App />
</Guide>
</StrictMode>,
)What the fallback does
With no router, the navigate tool calls history.pushState with the new path and then
dispatches a popstate event on the window. The current path it reports back is
location.pathname plus location.search.
That is enough for two kinds of app and not enough for a third:
| Your app | What happens |
|---|---|
| Server-rendered pages, full page loads | Works, but a pushState navigation changes the URL without loading the new page. Prefer real links and let the guide point at them. |
A client router that listens for popstate | Works. The dispatched event is what makes it re-render. |
| A client router that only re-renders on its own navigate call | The URL changes and the view does not. Pass an adapter. |
Writing one is two functions:
import type { RouterAdapter } from '@usedocent/react'
const adapter: RouterAdapter = {
navigate: (path) => myRouter.go(path),
currentPath: () => `${location.pathname}${location.search}`,
}Keep the object itself stable across renders. A new object every render is a new router prop,
and Docent would tear the session down and mint a new one each time. A useRef around it is
enough, and it is what the packaged adapters do.
Environment variables
Vite exposes only variables prefixed VITE_ to the browser, which is the same line Docent draws:
VITE_DOCENT_APP_ID=your-app-id
VITE_DOCENT_PUBLISHABLE_KEY=dk_pub_your_keyThe secret key has no place in a Vite app. It belongs to your build or your server, where the CLI runs.
Without React
@usedocent/sdk-core is the vanilla build, and createDocent(options) is the same thing without
the component. It has no runtime dependencies and takes the same options, minus voice, which
is the binding's own: with sdk-core you call docent.startVoice() yourself from a real user
gesture.
React Router and Remix
Mount Docent in a React Router or Remix app with useReactRouterAdapter, built from the useNavigate and useLocation hooks you already have.
The Docent component
Every prop of the Docent component with its type and default, the hooks it provides, the events it emits, and the handle it puts on React context.
