Docent
Guides

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 appWhat happens
Server-rendered pages, full page loadsWorks, 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 popstateWorks. The dispatched event is what makes it re-render.
A client router that only re-renders on its own navigate callThe 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_key

The 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.

On this page