Next.js
Add cited search to a Next.js app. Register the search element from a client component; for content behind a login, keep the key on the server in a route handler and point the widget at it.
On this page
1Install
# Needs @docslurp/sdk 0.2.0 or newer (0.1.x lacks these exports).
npm install @docslurp/sdk@^0.3.0
# Then register the element once in the browser:
# import { defineDocSlurpSearch } from '@docslurp/sdk/widget';2Public or private?
Public content
Docs, manuals, a help center. The browser calls DocSlurp directly with a browser key, which is search-only and works only on your allowed websites. No server code needed.
Private content
Anything behind a login. A route handler authenticates the viewer, picks the workspace they may read, and searches with a server key.
3aSearch public content
'use client';
import { useEffect, useRef } from 'react';
import { defineDocSlurpSearch } from '@docslurp/sdk/widget';
export function DocumentSearch() {
const host = useRef<HTMLDivElement>(null);
useEffect(() => {
defineDocSlurpSearch(); // SSR-safe: a no-op on the server
const element = document.createElement('docslurp-search-box');
element.setAttribute('client-key', process.env.NEXT_PUBLIC_DOCSLURP_CLIENT_KEY!);
// Optional: a workspace slug or UUID. Omitted = the default workspace.
if (process.env.NEXT_PUBLIC_DOCSLURP_WORKSPACE) element.setAttribute('workspace-id', process.env.NEXT_PUBLIC_DOCSLURP_WORKSPACE);
host.current?.append(element);
return () => element.remove();
}, []);
return <div ref={host} />;
}# .env.local — NEXT_PUBLIC_ values ship to every visitor.
NEXT_PUBLIC_DOCSLURP_CLIENT_KEY=dslp_pub_… # browser key: search-only, allowed websites only
NEXT_PUBLIC_DOCSLURP_WORKSPACE=product-docs # optionalRender <DocumentSearch /> from any page or layout. A runnable copy lives at packages/sdk/examples/next-search.tsx.
3bSearch private content
// app/api/search/route.ts — runs on the server; the key never reaches the browser.
import { createDocSlurpClient } from '@docslurp/sdk';
import { getViewer } from '@/lib/auth'; // your existing session check
const docslurp = createDocSlurpClient(); // DOCSLURP_API_KEY: server key, search scope
export async function POST(request: Request) {
const viewer = await getViewer(request);
if (!viewer) return Response.json({ error: 'Sign in to search' }, { status: 401 });
const { q } = await request.json();
if (typeof q !== 'string' || !q.trim() || q.length > 500) return Response.json({ results: [] });
// Fix the workspace on the server from what this viewer may read.
const { results } = await docslurp.search({ q, workspaceId: viewer.workspace, limit: 6 });
return Response.json({ results });
}'use client';
import { useEffect, useRef } from 'react';
import { defineDocSlurpSearch } from '@docslurp/sdk/widget';
export function PrivateDocumentSearch() {
const host = useRef<HTMLDivElement>(null);
useEffect(() => {
defineDocSlurpSearch();
const element = document.createElement('docslurp-search-box');
// No key in the browser: the widget POSTs { q } to your route
// and renders the returned { results } with citations.
element.setAttribute('search-endpoint', '/api/search');
host.current?.append(element);
return () => element.remove();
}, []);
return <div ref={host} />;
}# .env.local — server only. Never prefix with NEXT_PUBLIC_.
DOCSLURP_API_KEY=dslp_priv_… # search scopeNotes
Importing the widget is SSR-safe: defineDocSlurpSearch() does nothing on the server, and registration happens in the client effect. This is a documented recipe rather than a dedicated Next.js package, so it works the same with the Pages Router. For custom result markup, use a search adapter or the Node SDK directly.
Hand off to an agent
Working in a real codebase? Copy a prompt that tells a coding agent exactly what to change for Next.js: which entry point to use, the supported contract, and what to validate before shipping. It contains no keys or secrets.
Review agent instructions
Before switching traffic, run through the go-live checklist. Stuck? See troubleshooting.