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/react

Add 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:

components/site-search.tsx
"use client";

import "@hopfield/react/styles.css";
import { Search } from "@hopfield/react";

export function SiteSearch() {
  return (
    <Search publicApiKey={process.env.NEXT_PUBLIC_HOPFIELD_KEY!} />
  );
}
app/layout.tsx
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):

app/search/page.tsx
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:

app/api/publish/route.ts
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.