Integrations
Next.js
Hopfield works with both the App Router and the Pages Router. Render the search widget in a client component, call the API from server components or route handlers, and index new content whenever you publish.
Install
npm install @hopfield/reactAdd the widget (App Router)
The widget is interactive, so mark its wrapper as a client component and render it anywhere, typically in your root layout header:
"use client";
import "@hopfield/react/styles.css";
import { Search } from "@hopfield/react";
export function SiteSearch() {
return (
<Search publicApiKey={process.env.NEXT_PUBLIC_HOPFIELD_KEY!} />
);
}import { SiteSearch } from "@/components/site-search";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<header>
<SiteSearch />
</header>
{children}
</body>
</html>
);
}Public keys are safe to expose with the NEXT_PUBLIC_ prefix. They are read-only and scoped to your allowed origins.
Search from server components
For server-rendered search pages, call the API with @hopfield/core (also re-exported from @hopfield/react):
import { HopfieldClient } from "@hopfield/core";
const hopfield = new HopfieldClient({
publicApiKey: process.env.NEXT_PUBLIC_HOPFIELD_KEY!,
});
export default async function SearchPage({
searchParams,
}: {
searchParams: Promise<{ q?: string }>;
}) {
const { q } = await searchParams;
const { hits } = q ? await hopfield.search({ q }) : { hits: [] };
return (
<ul>
{hits.map((hit) => (
<li key={hit.chunkId}>
<a href={hit.url}>{hit.title ?? hit.url}</a>
</li>
))}
</ul>
);
}Index content on publish
If your site has content that isn’t in the sitemap yet, like CMS entries or user-generated pages, index it from a route handler or server action using your admin token:
import { HopfieldClient } from "@hopfield/core";
const hopfield = new HopfieldClient({
adminToken: process.env.HOPFIELD_ADMIN_TOKEN, // server-only
});
export async function POST(request: Request) {
const post = await request.json();
await hopfield.indexDocument("YOUR_COLLECTION_ID", {
title: post.title,
content: post.body,
url: `https://your-site.com/blog/${post.slug}`,
description: post.excerpt,
});
return Response.json({ ok: true });
}Related
See the React guide for theming and headless hooks, and the indexing API for the full write surface.