Switch search from Pagefind to Algolia#835
Conversation
Replaces Pagefind's build-time static search index with Algolia's crawler-based search. The index is populated automatically by Algolia's crawler — no build step needed. Adds a custom InstantSearch modal component that is theme-aware (light/dark) and uses the brand red for hit highlights. Also fixes a pre-existing bug where site.webmanifest 404'd in dev because the middleware matcher didn't exclude .webmanifest files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
`React.ComponentType<{ components?: ... }>` was incompatible with
`MDXContent` returned by @mdx-js/mdx's evaluate(), due to React 19
components returning ReactNode (including undefined) while @types/mdx
expects Element | null. Using React.ElementType resolves the boundary
without type suppressions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
not related but docs weren't building right on my branch until i made this change.
- Only initialize algoliasearch when all three env vars are present; show a setup instructions message in the modal otherwise - Add safeHref() to reject non-relative/non-https hit URLs (XSS defense) - Remove unused inputRef, FOCUS_DELAY_MS, and the focus useEffect — SearchBox autoFocus already handles this correctly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@uidotdev/usehooks is already a dependency. useEventListener handles add/remove lifecycle internally, removing the boilerplate. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces hardcoded hsl(347 ...) values with var(--primary) from @arcadeai/design-system tokens and color-mix() for transparent tints. Dark mode text override removed since --primary resolves identically in both modes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded neutral/dark: pairs with semantic tokens where the mapping is clean and visually equivalent: - text-neutral-900 dark:text-white → text-foreground - text-neutral-400/500 dark:text-neutral-500/400 → text-muted-foreground - border-neutral-200/300 dark:border-white/10 → border-border - bg-white dark:bg-neutral-900 (modal panel) → bg-popover Hover states on hits and the trigger button bg retain explicit neutral/white values since bg-muted dark resolves too dark for those. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…idotdev/usehooks@2.4.1 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| useEffect(() => { | ||
| const handler = (e: KeyboardEvent) => { | ||
| if ((e.metaKey || e.ctrlKey) && e.key === "k") { | ||
| e.preventDefault(); | ||
| setIsOpen((prev) => !prev); | ||
| } | ||
| if (e.key === "Escape") { | ||
| setIsOpen(false); | ||
| } | ||
| }; | ||
| window.addEventListener("keydown", handler); | ||
| return () => window.removeEventListener("keydown", handler); | ||
| }, []); |
There was a problem hiding this comment.
it would be neat to either upgrade @uidotdev/usehooks to a verstion that has useEventListener or use https://tanstack.com/hotkeys/latest (never tried it but tanstack is usually legit)
but for now, a useEffect is the best i've got
| "react": "19.2.3", | ||
| "react-dom": "19.2.3", | ||
| "react-hook-form": "7.65.0", | ||
| "react-instantsearch": "^7.26.0", |
There was a problem hiding this comment.
algolia's react library https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react
| hitComponent={({ hit }) => ( | ||
| <SearchHit hit={hit as unknown as HitRecord} /> | ||
| )} | ||
| /> |
There was a problem hiding this comment.
Hits component always renders alongside empty query message
Medium Severity
The Hits component renders unconditionally, but Algolia returns results for empty queries by default. When the search modal opens, users see the "Start typing to search the docs…" message from EmptyQuery and a list of search results from Hits at the same time. The Hits component needs to be conditionally hidden when no query has been entered.
Additional Locations (1)
There was a problem hiding this comment.
I think this is fine? I like the way this shows top docs pages and also prompts for input when empty. Open to other opinions.
| export const config = { | ||
| matcher: [ | ||
| "/((?!api|_next/static|_next/image|favicon.ico|manifest|_pagefind|public|.*.svg|.*.png|.*.jpg|.*.jpeg|.*.gif|.*.webp|.*.ico|.*.css|.*.js|.*.woff|.*.woff2|.*.ttf|.*.eot|.*.otf|.*.pdf|.*.txt|.*.xml|.*.json|.*.py|.*.mp4).*)", | ||
| "/((?!api|_next/static|_next/image|favicon.ico|manifest|public|.*.svg|.*.png|.*.jpg|.*.jpeg|.*.gif|.*.webp|.*.ico|.*.webmanifest|.*.css|.*.js|.*.woff|.*.woff2|.*.ttf|.*.eot|.*.otf|.*.pdf|.*.txt|.*.xml|.*.json|.*.py|.*.mp4).*)", |
There was a problem hiding this comment.
I had a bunch of 404s locally from this. Idk if I should have just ignored them. This fixed it.
|
nice |
Adds Configure with attributesToSnippet, imports Snippet component, and updates SearchHit to show a content snippet when present (new per-section index format) with fallback to description highlight for existing flat records. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This reverts commit 25b7afe.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
url.startsWith("/") was passing for //evil.com which browsers
interpret as https://evil.com, enabling open redirect.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The PR link was returning 404 and failing the external URL check test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolve conflicts after main switched search from Pagefind to Algolia (PR #835). Remove pagefind scripts/tests/helpers (now dead code), drop postbuild and generate:markdown scripts, keep Algolia deps. Made-with: Cursor


Summary
AlgoliaSearchcomponent usingreact-instantsearchwith a keyboard-accessible modal (⌘K), theme-aware light/dark styling, and brand red hit highlightspagefind,rehype-stringify,remark,remark-rehypedeps and thecustompagefindbuild script/site.webmanifest404'd because the middleware matcher didn't exclude.webmanifestfilesTest plan
pnpm buildcompletes without a Pagefind step🤖 Generated with Claude Code
Note
Medium Risk
Switches the docs search implementation and dependencies, which can affect navigation/UX and build/release behavior; also tweaks middleware routing for
.webmanifestassets. Risk is moderate as changes are mostly client-side but touch global layout and request matching.Overview
Replaces build-time Pagefind search with Algolia-powered search. Nextra’s built-in search is disabled and the layout now injects a new
AlgoliaSearch(⌘/Ctrl+K modal) usingreact-instantsearch, including URL sanitization for hits and new highlight styling.Removes Pagefind indexing from the build pipeline. The
custompagefindscript andscripts/pagefind.tsare deleted, related deps are dropped, and.env.local.exampleadds required Algolia public env vars.Fixes an asset routing edge case. Middleware matcher now excludes
*.webmanifestso/site.webmanifestis served instead of being intercepted.Written by Cursor Bugbot for commit 7a18739. This will update automatically on new commits. Configure here.