UI Components
Install search UI patterns and wire them to a Ceisium project.
Browse docs
UI components give visitors a search surface backed by either a restricted Browser/public key or your own server route.
Component patterns
| Pattern | Use it when |
|---|---|
| Command palette | Users expect keyboard search with / or a shortcut. |
| Inline search | A docs page needs a visible input and result list. |
| Search bar | A compact bar should open a modal when clicked. |
| Search modal | You want a SigNoz-style overlay with a search input and results. |
Direct browser setup
Create a Browser/public key, configure its allowed origins and result URL prefixes, and expose it as CEISIUM_PUBLIC_SEARCH_KEY in your client configuration.
const response = await fetch("https://api.ceisium.com/v1/search", { method: "POST", headers: { Authorization: `Bearer ${CEISIUM_PUBLIC_SEARCH_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ project_id: CEISIUM_PROJECT_ID, query, top_k: 8, path_prefix: "https://signoz.io/docs/", }),});Server route alternative
If you use a Server key, keep it on your backend and have the component call your route.
export async function POST(request: Request) { const { query, top_k = 8 } = await request.json(); const response = await fetch("https://api.ceisium.com/v1/search", { method: "POST", headers: { Authorization: `Bearer ${process.env.CEISIUM_SEARCH_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ project_id: process.env.CEISIUM_PROJECT_ID, query, top_k, }), }); return Response.json(await response.json());}Client config
Keep the config small:
{ placeholder: "Search docs...", shortcut: "/", limit: 8, debounceMs: 200, emptyMessage: "No results found."}For SigNoz, useful preview queries are:
OpenTelemetry Python instrumentationsend traceslogs exploreralertsResult behavior
Each result should show:
- Page title.
- Short snippet.
- Canonical URL or path.
- Click handler that navigates to the result.
Keep result lists scrollable so long docs sites do not make the modal grow past the viewport.
Latest request wins
During fast typing, an older request can finish after a newer request. Debouncing reduces request volume, but it does not guarantee response order. Cancel superseded work and only let the newest request update the UI.
let activeController: AbortController | null = null;let requestVersion = 0;async function search(query: string) { const version = ++requestVersion; activeController?.abort(); const controller = new AbortController(); activeController = controller; try { const response = await fetch("https://api.ceisium.com/v1/search", { method: "POST", signal: controller.signal, headers: { Authorization: `Bearer ${CEISIUM_PUBLIC_SEARCH_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ project_id: CEISIUM_PROJECT_ID, query, top_k: 8, }), }); const data = await response.json(); if (version !== requestVersion) return; renderResults(data.results); } catch (error) { if (error instanceof DOMException && error.name === "AbortError") return; if (version === requestVersion) renderError(); }}Ceisium’s built-in component previews already apply this guard. Add it when you call the raw Search API from a custom typeahead client.
Styling
Use your app theme tokens for borders, background, text, and focus states. Avoid hardcoded colors in the component.