--- title: Dynamic Search description: Implement server-side search for large registries. --- By default, `shadcn search` fetches your entire `registry.json` and filters items locally. This works well for most registries and requires nothing more than a static file. For large registries with thousands of items, you can implement search on your registry server instead. The CLI forwards the search parameters to your registry and your server returns only the matching items. Dynamic search is opt-in and fully backwards compatible. Static registries keep working without any changes. ## How It Works When you run `shadcn search`, the CLI appends the search parameters to the catalog request: ```bash npx shadcn@latest search @acme --query button --limit 50 ``` ```txt GET https://acme.com/r/registry.json?q=button&limit=50&offset=0 ``` What happens next depends on your registry: - **Static registries** ignore the query parameters and return the full `registry.json`. The CLI filters the items locally. This is the default behavior and requires no changes. - **Dynamic registries** filter the items server-side and return the matching items along with a `pagination` object. When the CLI sees `pagination` in the response, it trusts the results as pre-filtered and skips local filtering. The presence of `pagination` in the response is what tells the CLI your registry handles search server-side. There is no configuration or capability negotiation required. ## Query Parameters The CLI sends the following query parameters with every search request: | Parameter | Description | | --------- | -------------------------------------------------------------- | | `q` | The search query string. | | `type` | Comma-separated item types, e.g. `registry:ui,registry:block`. | | `limit` | Maximum number of items to return. | | `offset` | Number of items to skip. | All parameters are optional. A request without `q` or `type` should return all items, paginated. ## Response Format Return your regular `registry.json` shape with an additional `pagination` object: ```json title="registry.json?q=button&limit=2" { "name": "acme", "homepage": "https://acme.com", "items": [ { "name": "button", "type": "registry:ui", "description": "A button component." }, { "name": "icon-button", "type": "registry:ui", "description": "A button component with an icon." } ], "pagination": { "total": 12, "offset": 0, "limit": 2, "hasMore": true } } ``` Search results only need `name`, `type` and `description` for each item. You do not need to include `files`, `dependencies` or other item properties. The CLI fetches the full item definition when the user runs `shadcn add`. ### pagination | Property | Type | Description | | --------- | --------- | -------------------------------------------------- | | `total` | `number` | Total number of items matching the query. | | `offset` | `number` | Number of items skipped. | | `limit` | `number` | Maximum number of items in this response. | | `hasMore` | `boolean` | Whether more items are available beyond this page. | ## Server Implementation Here's an example using a Next.js route handler: ```typescript title="app/r/registry.json/route.ts" import { NextRequest, NextResponse } from "next/server" export async function GET(request: NextRequest) { const { searchParams } = request.nextUrl const query = searchParams.get("q") const types = searchParams.get("type")?.split(",") const limit = Number(searchParams.get("limit") ?? 100) const offset = Number(searchParams.get("offset") ?? 0) // Filter items using your database or search index. const { items, total } = await searchItems({ query, types, limit, offset }) return NextResponse.json({ name: "acme", homepage: "https://acme.com", items, pagination: { total, offset, limit, hasMore: offset + limit < total, }, }) } ``` You can back `searchItems` with anything: a database query, a full-text search index or an external search service. ## Multiple Registries When searching a single registry, the CLI forwards all parameters and uses your `pagination` response as-is. When searching multiple registries at once, e.g. `shadcn search @acme @lib`, a global `offset` cannot be split across registries. The CLI forwards `q` and `type` to each registry along with a `limit` large enough to fill the requested page (`offset + limit`), then merges and paginates the combined results locally. Server-side filtering still applies, so each registry only returns matching items. If your registry caps the number of items per response below the requested `limit`, the CLI treats it as exhausted for deeper pages. Honor the requested `limit` where possible so all your matches stay reachable through paging. ## Authentication Dynamic search works with all [authentication](/docs/registry/authentication) patterns. The CLI sends the configured headers and params with the search request, so you can scope search results to the authenticated user: ```typescript title="app/r/registry.json/route.ts" export async function GET(request: NextRequest) { const token = request.headers.get("authorization")?.replace("Bearer ", "") const team = await getTeamFromToken(token) // Only search items this team can access. const { items, total } = await searchItems({ query: request.nextUrl.searchParams.get("q"), team, }) // ... } ``` ## Backwards Compatibility - **Static registries** require no changes. Query parameters on a static file are ignored by the file server and the CLI falls back to local filtering. - **Older CLI versions** fetch the catalog without query parameters and ignore the `pagination` field. Your registry should return a sensible default response for requests without parameters, e.g. the first page of items. - **Ranking** is up to your server. When your registry returns pre-filtered results, the CLI preserves your order instead of re-ranking locally. ## Testing Test your dynamic registry with `curl`: ```bash curl "https://acme.com/r/registry.json?q=button&limit=10" ``` Then verify with the CLI: ```bash npx shadcn@latest search @acme --query button ``` To confirm server-side search is active, check that the response includes the `pagination` object and only the matching items.