diff --git a/bundlesize.config.json b/bundlesize.config.json
index 947ee1f6..f91ad60e 100644
--- a/bundlesize.config.json
+++ b/bundlesize.config.json
@@ -6,11 +6,11 @@
},
{
"path": "packages/docsearch-react/dist/umd/index.js",
- "maxSize": "70 kB"
+ "maxSize": "75 kB"
},
{
"path": "packages/docsearch-js/dist/umd/index.js",
- "maxSize": "85 kB"
+ "maxSize": "90 kB"
}
]
}
diff --git a/examples/demo-askai/package.json b/examples/demo-askai/package.json
deleted file mode 100644
index 11e4ed37..00000000
--- a/examples/demo-askai/package.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "name": "@docsearch/askai-example",
- "description": "DocSearch v4 AskAI example",
- "version": "4.0.0-beta.5",
- "private": true,
- "license": "MIT",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "build": "tsc -b && vite build",
- "preview": "vite preview"
- },
- "dependencies": {
- "@docsearch/css": "workspace:*",
- "@docsearch/react": "workspace:*",
- "react": "^19.0.0",
- "react-dom": "^19.0.0"
- },
- "devDependencies": {
- "@vitejs/plugin-react": "^4.3.4",
- "vite": "^6.0.7"
- }
-}
diff --git a/examples/demo-askai/src/App.css b/examples/demo-askai/src/App.css
deleted file mode 100644
index d1a94a91..00000000
--- a/examples/demo-askai/src/App.css
+++ /dev/null
@@ -1,4 +0,0 @@
-body {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
- Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
-}
diff --git a/examples/demo-askai/src/App.tsx b/examples/demo-askai/src/App.tsx
deleted file mode 100644
index a6f317e1..00000000
--- a/examples/demo-askai/src/App.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-/* eslint-disable react/react-in-jsx-scope */
-import { DocSearch } from '@docsearch/react';
-import type { JSX } from 'react';
-import './App.css';
-import '@docsearch/css/dist/style.css';
-
-function App(): JSX.Element {
- return (
-
-
DocSearch v4 - AskAI
-
-
- );
-}
-
-export default App;
diff --git a/examples/js-demo/index.html b/examples/demo-js/index.html
similarity index 100%
rename from examples/js-demo/index.html
rename to examples/demo-js/index.html
diff --git a/examples/js-demo/package.json b/examples/demo-js/package.json
similarity index 100%
rename from examples/js-demo/package.json
rename to examples/demo-js/package.json
diff --git a/examples/demo-askai/public/favicon.ico b/examples/demo-js/public/favicon.ico
similarity index 100%
rename from examples/demo-askai/public/favicon.ico
rename to examples/demo-js/public/favicon.ico
diff --git a/examples/js-demo/src/app.css b/examples/demo-js/src/app.css
similarity index 100%
rename from examples/js-demo/src/app.css
rename to examples/demo-js/src/app.css
diff --git a/examples/js-demo/src/main.ts b/examples/demo-js/src/main.ts
similarity index 100%
rename from examples/js-demo/src/main.ts
rename to examples/demo-js/src/main.ts
diff --git a/examples/demo-askai/src/vite-env.d.ts b/examples/demo-js/src/vite-env.d.ts
similarity index 100%
rename from examples/demo-askai/src/vite-env.d.ts
rename to examples/demo-js/src/vite-env.d.ts
diff --git a/examples/js-demo/tsconfig.json b/examples/demo-js/tsconfig.json
similarity index 100%
rename from examples/js-demo/tsconfig.json
rename to examples/demo-js/tsconfig.json
diff --git a/examples/demo-react-18/index.html b/examples/demo-react-18/index.html
deleted file mode 100644
index ad3c153e..00000000
--- a/examples/demo-react-18/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
- DocSearch v4 - React
-
-
-
-
-
-
-
diff --git a/examples/demo-react-18/package.json b/examples/demo-react-18/package.json
deleted file mode 100644
index 262b59dd..00000000
--- a/examples/demo-react-18/package.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "name": "@docsearch/react-18-example",
- "description": "DocSearch v3 React 18 example",
- "version": "4.0.0-beta.5",
- "private": true,
- "license": "MIT",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "build": "tsc -b && vite build",
- "preview": "vite preview"
- },
- "dependencies": {
- "@docsearch/css": "workspace:*",
- "@docsearch/react": "workspace:*",
- "react": "^18.0.0",
- "react-dom": "^18.0.0"
- },
- "devDependencies": {
- "@vitejs/plugin-react": "^4.3.4",
- "vite": "^6.0.7"
- }
-}
diff --git a/examples/demo-react-18/src/App.css b/examples/demo-react-18/src/App.css
deleted file mode 100644
index d1a94a91..00000000
--- a/examples/demo-react-18/src/App.css
+++ /dev/null
@@ -1,4 +0,0 @@
-body {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
- Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
-}
diff --git a/examples/demo-react-18/src/App.tsx b/examples/demo-react-18/src/App.tsx
deleted file mode 100644
index b2026742..00000000
--- a/examples/demo-react-18/src/App.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { DocSearch } from '@docsearch/react';
-import React from 'react';
-import './App.css';
-import '@docsearch/css/dist/style.css';
-
-function App(): React.JSX.Element {
- return (
-
-
DocSearch v4 - React - 18
-
-
- );
-}
-
-export default App;
diff --git a/examples/demo-react-18/src/main.tsx b/examples/demo-react-18/src/main.tsx
deleted file mode 100644
index 1744314d..00000000
--- a/examples/demo-react-18/src/main.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-/* eslint-disable react/react-in-jsx-scope */
-import { StrictMode } from 'react';
-import { createRoot } from 'react-dom/client';
-
-import App from './App.tsx';
-
-createRoot(document.getElementById('root')!).render(
-
-
- ,
-);
diff --git a/examples/demo-react-18/tsconfig.json b/examples/demo-react-18/tsconfig.json
deleted file mode 100644
index 697e14a9..00000000
--- a/examples/demo-react-18/tsconfig.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
- "target": "ES2020",
- "useDefineForClassFields": true,
- "lib": [
- "ES2020",
- "DOM",
- "DOM.Iterable"
- ],
- "module": "ESNext",
- "skipLibCheck": true,
- /* Bundler mode */
- "moduleResolution": "Bundler",
- "allowImportingTsExtensions": true,
- "isolatedModules": true,
- "moduleDetection": "force",
- "noEmit": true,
- "jsx": "react-jsx",
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
- },
- "include": [
- "src",
- "vite.config.ts"
- ],
-}
diff --git a/examples/demo-react-18/vite.config.ts b/examples/demo-react-18/vite.config.ts
deleted file mode 100644
index a74ec848..00000000
--- a/examples/demo-react-18/vite.config.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import react from '@vitejs/plugin-react';
-import { defineConfig } from 'vite';
-
-// https://vite.dev/config/
-export default defineConfig({
- plugins: [react()],
-});
diff --git a/examples/demo-askai/index.html b/examples/demo-react/index.html
similarity index 100%
rename from examples/demo-askai/index.html
rename to examples/demo-react/index.html
diff --git a/examples/demo/package.json b/examples/demo-react/package.json
similarity index 85%
rename from examples/demo/package.json
rename to examples/demo-react/package.json
index 736847b9..b0527725 100644
--- a/examples/demo/package.json
+++ b/examples/demo-react/package.json
@@ -11,8 +11,8 @@
"preview": "vite preview"
},
"dependencies": {
- "@docsearch/css": "4.0.0-beta.5",
- "@docsearch/react": "4.0.0-beta.5",
+ "@docsearch/css": "workspace:*",
+ "@docsearch/react": "workspace:*",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
diff --git a/examples/demo-react-18/public/favicon.ico b/examples/demo-react/public/favicon.ico
similarity index 100%
rename from examples/demo-react-18/public/favicon.ico
rename to examples/demo-react/public/favicon.ico
diff --git a/examples/demo-react/src/App.css b/examples/demo-react/src/App.css
new file mode 100644
index 00000000..5e9f2e1d
--- /dev/null
+++ b/examples/demo-react/src/App.css
@@ -0,0 +1,160 @@
+@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&family=TikTok+Sans:opsz,wght@12..36,300..900&display=swap');
+
+/* reset and base styles */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+body {
+ font-family: 'TikTok Sans', 'sans-serif';
+ min-height: 100vh;
+ background: radial-gradient(125% 125% at 50% 90%, #fff 40%, #7c3aed 100%);
+ color: #333;
+ line-height: 1.6;
+}
+
+/* container styles */
+.app-container {
+ max-width: 1024px;
+ margin: 0 auto;
+ padding: 2rem 1rem;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+/* header styles */
+.app-header {
+ text-align: center;
+ margin-bottom: 3rem;
+}
+
+.app-title {
+ font-size: 2rem;
+ font-weight: 700;
+ color: black;
+ margin-bottom: 0;
+}
+
+.app-subtitle {
+ font-size: 1rem;
+ color: black;
+ color: #666;
+ font-weight: 400;
+}
+
+main {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-template-rows: repeat(3, 1fr);
+ gap: 8px;
+}
+
+/* demo section styles */
+.demo-section {
+ background: white;
+ border-radius: 16px;
+ padding: 2rem;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.section-description {
+ text-align: center;
+ color: #666;
+ font-family: 'Source Code Pro', monospace;
+ font-size: 0.9rem;
+ margin-bottom: 1rem;
+ margin-top: -1rem;
+ opacity: 0.8;
+}
+
+.ai-powered {
+ text-decoration: underline;
+ text-decoration-color: #f2e8ff;
+ text-underline-offset: 4px;
+ text-decoration-thickness: 2px;
+}
+
+/* search component wrapper */
+.search-wrapper {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 60px;
+}
+
+/* divider styles */
+.divider {
+ border: none;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, #e2e8f0, transparent);
+ margin: 0;
+}
+
+/* responsive design */
+@media (max-width: 768px) {
+ .app-container {
+ padding: 1rem;
+ }
+
+ .app-title {
+ font-size: 2.5rem;
+ }
+
+ .demo-section {
+ padding: 1.5rem;
+ }
+ .section-description {
+ font-size: 0.8rem;
+ }
+}
+
+/* docsearch button customization */
+.DocSearch-Button {
+ margin: 0 !important;
+ background: #f7fafc !important;
+ border: 2px solid #e2e8f0 !important;
+ border-radius: 12px !important;
+ padding: 12px 16px !important;
+ font-size: 16px !important;
+ transition: all 0.2s ease !important;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05) !important;
+}
+
+.DocSearch-Button:hover {
+ border-color: #667eea !important;
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15) !important;
+ transform: translateY(-1px) !important;
+}
+
+.DocSearch-Button-Placeholder {
+ color: #718096 !important;
+}
+
+/* add subtle animations */
+.demo-section {
+ animation: fadeInUp 0.6s ease forwards;
+}
+
+.demo-section:nth-child(2) {
+ animation-delay: 0.1s;
+}
+
+.demo-section:nth-child(3) {
+ animation-delay: 0.2s;
+}
+
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
diff --git a/examples/demo-react/src/App.tsx b/examples/demo-react/src/App.tsx
new file mode 100644
index 00000000..5fc67312
--- /dev/null
+++ b/examples/demo-react/src/App.tsx
@@ -0,0 +1,56 @@
+/* eslint-disable react/react-in-jsx-scope */
+import { version } from '@docsearch/react';
+import type { JSX } from 'react';
+
+import './App.css';
+import '@docsearch/css/dist/style.css';
+
+import Basic from './examples/basic';
+import BasicAskAI from './examples/basic-askai';
+import WHitComponent from './examples/w-hit-component';
+import WTransformItems from './examples/w-hit-transformItems';
+
+function App(): JSX.Element {
+ return (
+
+
+
+
+
+
+ basic search functionality
+
+
+
+
+
+
+ search with askai integration
+
+
+
+
+
+
+ custom hit rendering
+
+
+
+
+
+
+ transform items before rendering
+
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/examples/demo-react/src/examples/basic-askai.tsx b/examples/demo-react/src/examples/basic-askai.tsx
new file mode 100644
index 00000000..ab7854e6
--- /dev/null
+++ b/examples/demo-react/src/examples/basic-askai.tsx
@@ -0,0 +1,21 @@
+/* eslint-disable react/react-in-jsx-scope */
+import { DocSearch } from '@docsearch/react';
+import type { JSX } from 'react';
+
+export default function BasicAskAI(): JSX.Element {
+ return (
+
+ );
+}
diff --git a/examples/demo-react/src/examples/basic.tsx b/examples/demo-react/src/examples/basic.tsx
new file mode 100644
index 00000000..221dd6bf
--- /dev/null
+++ b/examples/demo-react/src/examples/basic.tsx
@@ -0,0 +1,15 @@
+/* eslint-disable react/react-in-jsx-scope */
+import { DocSearch } from '@docsearch/react';
+import type { JSX } from 'react';
+
+export default function Basic(): JSX.Element {
+ return (
+
+ );
+}
diff --git a/examples/demo-react/src/examples/w-hit-component.tsx b/examples/demo-react/src/examples/w-hit-component.tsx
new file mode 100644
index 00000000..5bb42159
--- /dev/null
+++ b/examples/demo-react/src/examples/w-hit-component.tsx
@@ -0,0 +1,125 @@
+/* eslint-disable react/react-in-jsx-scope */
+import { DocSearch } from '@docsearch/react';
+import type { JSX } from 'react';
+
+function CustomHit({ hit }: { hit: any }): JSX.Element {
+ return (
+ {
+ e.currentTarget.style.backgroundColor = '#f8f9fa';
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.backgroundColor = 'transparent';
+ }}
+ >
+
+
+ {/* eslint-disable-next-line no-nested-ternary */}
+ {hit.type === 'lvl1' ? 'H1' : hit.type === 'lvl2' ? 'H2' : hit.type === 'lvl3' ? 'H3' : 'DOC'}
+
+
+
+
+ {hit.hierarchy.lvl1 || 'Untitled'}
+
+
+ {hit.hierarchy.lvl2 && (
+
+ {hit.hierarchy.lvl2}
+
+ )}
+
+ {hit.content && (
+
+ {hit.content}
+
+ )}
+
+
+ {hit.url}
+ {hit.type && {hit.type}}
+
+
+
+
+ );
+}
+
+export default function WHitComponent(): JSX.Element {
+ return (
+
+ );
+}
diff --git a/examples/demo-react/src/examples/w-hit-transformItems.tsx b/examples/demo-react/src/examples/w-hit-transformItems.tsx
new file mode 100644
index 00000000..81d28b86
--- /dev/null
+++ b/examples/demo-react/src/examples/w-hit-transformItems.tsx
@@ -0,0 +1,61 @@
+/* eslint-disable react/react-in-jsx-scope */
+import { DocSearch } from '@docsearch/react';
+import type { JSX } from 'react';
+
+// this type matches the structure of the provided example hit
+/* type _DocSearchCustomHit = {
+ path: string;
+ metaDescription: string;
+ title: string;
+ h1: string;
+ h2: string;
+ content: string;
+ breadcrumb: string[];
+ variation: Record;
+ pageDepth: number;
+ domain: string;
+ objectID: string;
+ _snippetResult: any;
+ _highlightResult: any;
+}; */
+
+export default function WTransformItems(): JSX.Element {
+ return (
+ {
+ return items.map((item: any) => ({
+ objectID: item.objectID,
+ content: item.content ?? '',
+ url: new URL(item.domain + item.path).toString(),
+ hierarchy: {
+ lvl0: item.breadcrumb.join(' > ') ?? '',
+ lvl1: item.h1 ?? '',
+ lvl2: item.h2 ?? '',
+ lvl3: null,
+ lvl4: null,
+ lvl5: null,
+ lvl6: null,
+ },
+ url_without_anchor: new URL(item.domain + item.path).toString(),
+ type: 'content' as const,
+ anchor: null,
+ _highlightResult: item._highlightResult,
+ _snippetResult: item._snippetResult,
+ }));
+ }}
+ translations={{ button: { buttonText: 'Search with transformItems' } }}
+ />
+ );
+}
diff --git a/examples/demo-askai/src/main.tsx b/examples/demo-react/src/main.tsx
similarity index 100%
rename from examples/demo-askai/src/main.tsx
rename to examples/demo-react/src/main.tsx
diff --git a/examples/demo-react-18/src/vite-env.d.ts b/examples/demo-react/src/vite-env.d.ts
similarity index 100%
rename from examples/demo-react-18/src/vite-env.d.ts
rename to examples/demo-react/src/vite-env.d.ts
diff --git a/examples/demo-askai/tsconfig.json b/examples/demo-react/tsconfig.json
similarity index 100%
rename from examples/demo-askai/tsconfig.json
rename to examples/demo-react/tsconfig.json
diff --git a/examples/demo-askai/vite.config.ts b/examples/demo-react/vite.config.ts
similarity index 100%
rename from examples/demo-askai/vite.config.ts
rename to examples/demo-react/vite.config.ts
diff --git a/examples/demo/index.html b/examples/demo/index.html
deleted file mode 100644
index ad3c153e..00000000
--- a/examples/demo/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
- DocSearch v4 - React
-
-
-
-
-
-
-
diff --git a/examples/demo/public/favicon.ico b/examples/demo/public/favicon.ico
deleted file mode 100644
index c481de75..00000000
Binary files a/examples/demo/public/favicon.ico and /dev/null differ
diff --git a/examples/demo/src/App.css b/examples/demo/src/App.css
deleted file mode 100644
index d1a94a91..00000000
--- a/examples/demo/src/App.css
+++ /dev/null
@@ -1,4 +0,0 @@
-body {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
- Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
-}
diff --git a/examples/demo/src/App.tsx b/examples/demo/src/App.tsx
deleted file mode 100644
index b062ade1..00000000
--- a/examples/demo/src/App.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { DocSearch } from '@docsearch/react';
-import type { JSX } from 'react';
-import './App.css';
-import '@docsearch/css/dist/style.css';
-
-function App(): JSX.Element {
- return (
-
-
DocSearch v4 - React
-
-
- );
-}
-
-export default App;
diff --git a/examples/demo/src/main.tsx b/examples/demo/src/main.tsx
deleted file mode 100644
index 9ac8961f..00000000
--- a/examples/demo/src/main.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { StrictMode } from 'react';
-import { createRoot } from 'react-dom/client';
-
-import App from './App.tsx';
-
-createRoot(document.getElementById('root')!).render(
-
-
- ,
-);
diff --git a/examples/demo/src/vite-env.d.ts b/examples/demo/src/vite-env.d.ts
deleted file mode 100644
index 11f02fe2..00000000
--- a/examples/demo/src/vite-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/examples/demo/tsconfig.json b/examples/demo/tsconfig.json
deleted file mode 100644
index 697e14a9..00000000
--- a/examples/demo/tsconfig.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
- "target": "ES2020",
- "useDefineForClassFields": true,
- "lib": [
- "ES2020",
- "DOM",
- "DOM.Iterable"
- ],
- "module": "ESNext",
- "skipLibCheck": true,
- /* Bundler mode */
- "moduleResolution": "Bundler",
- "allowImportingTsExtensions": true,
- "isolatedModules": true,
- "moduleDetection": "force",
- "noEmit": true,
- "jsx": "react-jsx",
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
- },
- "include": [
- "src",
- "vite.config.ts"
- ],
-}
diff --git a/examples/demo/vite.config.ts b/examples/demo/vite.config.ts
deleted file mode 100644
index a74ec848..00000000
--- a/examples/demo/vite.config.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import react from '@vitejs/plugin-react';
-import { defineConfig } from 'vite';
-
-// https://vite.dev/config/
-export default defineConfig({
- plugins: [react()],
-});
diff --git a/examples/js-demo/public/favicon.ico b/examples/js-demo/public/favicon.ico
deleted file mode 100644
index c481de75..00000000
Binary files a/examples/js-demo/public/favicon.ico and /dev/null differ
diff --git a/examples/js-demo/src/vite-env.d.ts b/examples/js-demo/src/vite-env.d.ts
deleted file mode 100644
index 11f02fe2..00000000
--- a/examples/js-demo/src/vite-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/package.json b/package.json
index a69f01e4..1202e07e 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,6 @@
"lint": "eslint .",
"playground:build": "yarn workspace @docsearch/react-example build",
"playground:start": "yarn workspace @docsearch/react-example dev --host",
- "playground-askai:start": "yarn workspace @docsearch/askai-example dev --host",
"playground-js:start": "yarn workspace @docsearch/js-example dev --host",
"release": "shipjs prepare",
"start": "yarn run watch",
diff --git a/packages/docsearch-css/src/modal.css b/packages/docsearch-css/src/modal.css
index 715c7265..fbea8c97 100644
--- a/packages/docsearch-css/src/modal.css
+++ b/packages/docsearch-css/src/modal.css
@@ -1211,7 +1211,7 @@ assistive tech users */
color: var(--docsearch-muted-color);
}
-.DocSearch-AskAiScreen-MessageContent-Tool.Tool--Result:first-child {
+.DocSearch-AskAiScreen-MessageContent-Tool.Tool--Result {
padding-top: 0;
}
diff --git a/packages/docsearch-react/src/DocSearchModal.tsx b/packages/docsearch-react/src/DocSearchModal.tsx
index b7ac120d..ba11f83b 100644
--- a/packages/docsearch-react/src/DocSearchModal.tsx
+++ b/packages/docsearch-react/src/DocSearchModal.tsx
@@ -150,7 +150,7 @@ const buildQuerySources = async ({
{
query,
indexName,
- attributesToRetrieve: [
+ attributesToRetrieve: searchParameters?.attributesToRetrieve ?? [
'hierarchy.lvl0',
'hierarchy.lvl1',
'hierarchy.lvl2',
@@ -162,7 +162,7 @@ const buildQuerySources = async ({
'type',
'url',
],
- attributesToSnippet: [
+ attributesToSnippet: searchParameters?.attributesToSnippet ?? [
`hierarchy.lvl1:${snippetLength.current}`,
`hierarchy.lvl2:${snippetLength.current}`,
`hierarchy.lvl3:${snippetLength.current}`,
@@ -171,11 +171,11 @@ const buildQuerySources = async ({
`hierarchy.lvl6:${snippetLength.current}`,
`content:${snippetLength.current}`,
],
- snippetEllipsisText: '…',
- highlightPreTag: '',
- highlightPostTag: '',
- hitsPerPage: 20,
- clickAnalytics: insightsActive,
+ snippetEllipsisText: searchParameters?.snippetEllipsisText ?? '…',
+ highlightPreTag: searchParameters?.highlightPreTag ?? '',
+ highlightPostTag: searchParameters?.highlightPostTag ?? '',
+ hitsPerPage: searchParameters?.hitsPerPage ?? 20,
+ clickAnalytics: searchParameters?.clickAnalytics ?? insightsActive,
...searchParameters,
},
],
@@ -183,7 +183,8 @@ const buildQuerySources = async ({
const firstResult = results[0] as SearchResponse;
const { hits, nbHits } = firstResult;
- const sources = groupBy(hits, (hit) => removeHighlightTags(hit), maxResultsPerGroup);
+ const transformedHits = transformItems(hits);
+ const sources = groupBy(transformedHits, (hit) => removeHighlightTags(hit), maxResultsPerGroup);
// We store the `lvl0`s to display them as search suggestions
// in the "no results" screen.
@@ -222,7 +223,6 @@ const buildQuerySources = async ({
},
getItems(): InternalDocSearchHit[] {
return Object.values(groupBy(items, (item) => item.hierarchy.lvl1, maxResultsPerGroup))
- .map(transformItems)
.map((groupedHits) =>
groupedHits.map((item) => {
let parent: InternalDocSearchHit | null = null;
diff --git a/packages/website/docs/examples.mdx b/packages/website/docs/examples.mdx
new file mode 100644
index 00000000..fbfde2eb
--- /dev/null
+++ b/packages/website/docs/examples.mdx
@@ -0,0 +1,212 @@
+---
+id: examples
+title: Examples and extensions
+description: live demos showing how to use and extend docsearch beyond documentation-only use cases.
+---
+
+import { DocSearch } from '@docsearch/react';
+import '@docsearch/css/dist/style.css';
+
+> These examples are interactive. click a button to open the modal and try a query.
+
+## Basic keyword search
+
+Use the default experience with your index credentials. this works great for typical docs, blogs, and any site with a docsearch-compliant index.
+
+```jsx
+
+```
+
+
+
+---
+
+## Ask AI: ai-assisted answers
+
+Add algolia askai to get synthesized answers grounded in your indexed content. you can scope the llm context using `searchParameters`.
+
+```jsx
+
+```
+
+
+
+---
+
+## Custom hit rendering (`hitComponent`)
+
+Replace the default hit markup to match your brand and layout. below is a minimal example of a custom component.
+
+```jsx
+function CustomHit({ hit }) {
+ // render a compact, branded hit card
+ return (
+
+
+
+ {hit.type?.toUpperCase?.() || 'DOC'}
+
+
+
{hit.hierarchy?.lvl1 || 'untitled'}
+ {hit.hierarchy?.lvl2 &&
{hit.hierarchy.lvl2}
}
+ {hit.content &&
{hit.content}
}
+
+
+
+ );
+}
+
+
+```
+
+ {
+ // render a compact, branded hit card
+ return (
+
+
+
+ {hit.type?.toUpperCase?.() || 'DOC'}
+
+
+
{hit.hierarchy?.lvl1 || 'untitled'}
+ {hit.hierarchy?.lvl2 &&
{hit.hierarchy.lvl2}
}
+ {hit.content &&
{hit.content}
}
+
+
+
+ );
+}}
+ insights={true}
+ translations={{ button: { buttonText: 'custom hits (demo)' } }}
+/>
+
+---
+
+## Bring-your-own-data shape with `transformItems`
+
+Docsearch is not limited to docsearch-like records. use `transformItems` to adapt any record shape into the internal structure docsearch expects. this lets you build search for apps, help centers, changelogs, or any custom content.
+
+the snippet below maps a non-standard record to the internal format. try it live:
+
+```jsx
+
+ items.map((item) => ({
+ objectID: item.objectID,
+ content: item.content ?? '',
+ url: item.domain + item.path,
+ hierarchy: {
+ lvl0: (item.breadcrumb || []).join(' > ') ?? '',
+ lvl1: item.h1 ?? '',
+ lvl2: item.h2 ?? '',
+ lvl3: null,
+ lvl4: null,
+ lvl5: null,
+ lvl6: null,
+ },
+ url_without_anchor: item.domain + item.path,
+ type: 'content',
+ anchor: null,
+ _highlightResult: item._highlightResult,
+ _snippetResult: item._snippetResult,
+ }))
+ }
+ insights={true}
+ translations={{ button: { buttonText: 'transform items (demo)' } }}
+/>
+```
+
+
+ items.map((item) => ({
+ objectID: item.objectID,
+ content: item.content ?? '',
+ url: item.domain + item.path,
+ hierarchy: {
+ lvl0: (item.breadcrumb || []).join(' > ') ?? '',
+ lvl1: item.h1 ?? '',
+ lvl2: item.h2 ?? '',
+ lvl3: null,
+ lvl4: null,
+ lvl5: null,
+ lvl6: null,
+ },
+ url_without_anchor: item.domain + item.path,
+ type: 'content',
+ anchor: null,
+ _highlightResult: item._highlightResult,
+ _snippetResult: item._snippetResult,
+ }))
+ }
+ insights={true}
+ translations={{ button: { buttonText: 'transform items (demo)' } }}
+/>
+
+---
+
+## Tips
+
+- **Instrumentation**: enable `insights` to send usage analytics and iterate on relevance.
+- **AskAI scoping**: use `facetFilters` to keep ai answers strictly within your desired corpus (language, product, version, etc.).
+- **Customization**: use `hitComponent`, `transformItems`, and `translations` to make docsearch feel native to any product surface.
diff --git a/packages/website/sidebars.js b/packages/website/sidebars.js
index b4103dd2..6f5079b7 100644
--- a/packages/website/sidebars.js
+++ b/packages/website/sidebars.js
@@ -19,7 +19,7 @@ export default {
{
type: 'category',
label: 'DocSearch v4',
- items: ['docsearch', 'api', 'migrating-from-v3'],
+ items: ['docsearch', 'api', 'examples', 'migrating-from-v3'],
},
{
type: 'category',
diff --git a/yarn.lock b/yarn.lock
index 320ea421..1ce71d01 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2310,19 +2310,6 @@ __metadata:
languageName: node
linkType: hard
-"@docsearch/askai-example@workspace:examples/demo-askai":
- version: 0.0.0-use.local
- resolution: "@docsearch/askai-example@workspace:examples/demo-askai"
- dependencies:
- "@docsearch/css": "workspace:*"
- "@docsearch/react": "workspace:*"
- "@vitejs/plugin-react": "npm:^4.3.4"
- react: "npm:^19.0.0"
- react-dom: "npm:^19.0.0"
- vite: "npm:^6.0.7"
- languageName: unknown
- linkType: soft
-
"@docsearch/css@npm:4.0.0-beta.4":
version: 4.0.0-beta.4
resolution: "@docsearch/css@npm:4.0.0-beta.4"
@@ -2338,9 +2325,9 @@ __metadata:
languageName: unknown
linkType: soft
-"@docsearch/js-example@workspace:examples/js-demo":
+"@docsearch/js-example@workspace:examples/demo-js":
version: 0.0.0-use.local
- resolution: "@docsearch/js-example@workspace:examples/js-demo"
+ resolution: "@docsearch/js-example@workspace:examples/demo-js"
dependencies:
"@docsearch/css": "npm:4.0.0-beta.5"
"@docsearch/js": "npm:4.0.0-beta.5"
@@ -2418,26 +2405,13 @@ __metadata:
languageName: unknown
linkType: soft
-"@docsearch/react-18-example@workspace:examples/demo-react-18":
+"@docsearch/react-example@workspace:examples/demo-react":
version: 0.0.0-use.local
- resolution: "@docsearch/react-18-example@workspace:examples/demo-react-18"
+ resolution: "@docsearch/react-example@workspace:examples/demo-react"
dependencies:
"@docsearch/css": "workspace:*"
"@docsearch/react": "workspace:*"
"@vitejs/plugin-react": "npm:^4.3.4"
- react: "npm:^18.0.0"
- react-dom: "npm:^18.0.0"
- vite: "npm:^6.0.7"
- languageName: unknown
- linkType: soft
-
-"@docsearch/react-example@workspace:examples/demo":
- version: 0.0.0-use.local
- resolution: "@docsearch/react-example@workspace:examples/demo"
- dependencies:
- "@docsearch/css": "npm:4.0.0-beta.5"
- "@docsearch/react": "npm:4.0.0-beta.5"
- "@vitejs/plugin-react": "npm:^4.3.4"
react: "npm:^19.0.0"
react-dom: "npm:^19.0.0"
vite: "npm:^6.0.7"