Skip to Content
QMD Search SDKReact UI-Komponente

React UI-Komponente

@processcube-io/qmd-search-ui bietet einen konfigurierbaren Such-Dialog fuer React-Anwendungen.

🔍

Live-Demo — Diese Seite nutzt den SearchDialog produktiv. Druecke Cmd+K (macOS) oder Ctrl+K (Windows/Linux) um die Suche zu oeffnen und die Komponente in Aktion zu sehen.

Installation

npm install @processcube-io/qmd-search-ui

Voraussetzung: React >= 18

Verwendung

import { SearchDialog } from '@processcube-io/qmd-search-ui'; function App() { const router = useRouter(); // oder eigene Navigation return ( <SearchDialog apiEndpoint="/api/search" collections={[ { label: 'Dokumentation', collections: [ { key: 'docs', label: 'Docs' }, { key: 'api', label: 'API Reference' }, ], }, { label: 'Sonstige', collections: [ { key: 'blog', label: 'Blog' }, { key: 'changelogs', label: 'Changelogs' }, ], }, ]} onNavigate={(path) => router.push(path)} theme={{ accentColor: '#f7a823', collectionColors: { docs: '#3b82f6', api: '#8b5cf6', blog: '#10b981', changelogs: '#a855f7', }, }} /> ); }

Props

PropTypDefaultBeschreibung
apiEndpointstring'/api/search'Such-API-Endpoint
collectionsCollectionGroupConfig[][]Collection-Gruppen fuer den Filter
onNavigate(path: string) => void(Pflicht)Navigation bei Ergebnis-Klick
themeSearchTheme{}Farben, Schrift, Border-Radius
labelsSearchLabelsDeutsche DefaultsTexte / Lokalisierung
debounceMsnumber350Debounce-Zeit
limitnumber15Max. Ergebnisse pro Suche

Theme

interface SearchTheme { accentColor?: string; // Spinner, Ladeleiste (Default: '#f7a823') borderRadius?: string; // Dialog-Ecken (Default: '12px') fontFamily?: string; // Schriftart (Default: inherit) collectionColors?: Record<string, string>; // Badge-Farben pro Collection }

Labels / Lokalisierung

Alle Texte koennen ueberschrieben werden. Defaults sind Deutsch:

<SearchDialog labels={{ placeholder: 'Search documentation...', triggerText: 'Search...', shortcutLabel: '⌘K', allCollections: 'All', noResults: 'No results for', unavailable: 'Search is initializing...', navigationHint: 'Navigate', openHint: 'Open', closeHint: 'Close', }} />

Features

  • Cmd+K / Ctrl+K — Shortcut zum Oeffnen
  • Debounce — Konfigurierbare Verzoegerung
  • AbortController — Vorherige Requests werden abgebrochen
  • Keyboard-Navigation — Pfeiltasten + Enter
  • Skeleton-Platzhalter — Waehrend der ersten Suche
  • Collection-Filter — Gruppierter Dropdown
  • Animationen — Fade-In, Ladeleiste, Spinner

Auth-Integration

Die UI-Komponente kennt keinen API-Key — der Key darf nie im Browser landen. Zwei Integrationsmuster:

Muster A: Proxy (empfohlen)

Die App leitet Suchanfragen an den qmd-search-server weiter und fuegt den API-Key serverseitig hinzu. Funktioniert mit jedem Framework.

Browser (SearchDialog) → App-Backend (+ API-Key) → qmd-search-server

Next.js Beispiel:

// app/api/search/route.ts — Proxy-Route export async function GET(request: Request) { const { searchParams } = new URL(request.url); const q = searchParams.get('q'); const collection = searchParams.get('collection') || ''; const limit = searchParams.get('limit') || '20'; const params = new URLSearchParams({ q: q || '', collection, limit }); const response = await fetch(`https://search.example.com/api/search?${params}`, { headers: { 'X-API-Key': process.env.QMD_API_KEY! }, }); return new Response(response.body, { status: response.status, headers: { 'Content-Type': 'application/json' }, }); }
// Komponente — spricht die eigene Proxy-Route an <SearchDialog apiEndpoint="/api/search" onNavigate={(path) => router.push(path)} />

Express Beispiel:

import express from 'express'; const app = express(); app.get('/api/search', async (req, res) => { const params = new URLSearchParams(req.query as Record<string, string>); const response = await fetch(`https://search.example.com/api/search?${params}`, { headers: { 'X-API-Key': process.env.QMD_API_KEY! }, }); const data = await response.json(); res.json(data); });

Muster B: Server Component (Next.js)

Die Suche laeuft komplett serverseitig — der Store wird direkt importiert, kein HTTP noetig. Der API-Key ist nicht beteiligt, da der Store lokal auf die SQLite-Datenbank zugreift.

// app/search/page.tsx — Server Component import { createSearchStore } from '@processcube-io/qmd-search'; const store = await createSearchStore({ dbPath: './data/index.sqlite' }); export default async function SearchPage({ searchParams, }: { searchParams: Promise<{ q?: string; collection?: string }>; }) { const { q, collection } = await searchParams; if (!q) return <p>Bitte Suchbegriff eingeben.</p>; const result = await store.search(q, { collection, limit: 20 }); return ( <ul> {result.results.map((hit) => ( <li key={hit.path}> <a href={hit.path}><strong>{hit.title}</strong></a> <span> [{hit.collection}]</span> <p>{hit.snippet}</p> </li> ))} </ul> ); }

Dieses Muster eignet sich besonders wenn die Datenbank lokal vorliegt (z.B. im gleichen Container).