@headband/react 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +249 -159
- package/dist/index.d.mts +168 -5
- package/dist/index.d.ts +168 -5
- package/dist/index.js +279 -96
- package/dist/index.mjs +267 -102
- package/package.json +2 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ZVN DEV
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @headband/react
|
|
2
2
|
|
|
3
|
-
React hooks and components for [Headband](https://headband.dev) search. Drop-in building blocks for building fast, faceted search UIs -- designed as an InstantSearch-
|
|
3
|
+
React hooks and components for [Headband](https://headband.dev) search. Drop-in building blocks for building fast, faceted search UIs -- designed as an InstantSearch-inspired alternative that works with Headband's unified search API.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,6 +10,8 @@ npm install @headband/react
|
|
|
10
10
|
|
|
11
11
|
**Peer dependencies:** React 18+ and React DOM 18+.
|
|
12
12
|
|
|
13
|
+
Browser SDK examples must use scoped `hb_src_*` search keys. Keep `hb_adm_*` admin keys server-side for indexing, settings, and connector jobs.
|
|
14
|
+
|
|
13
15
|
## Quick Start
|
|
14
16
|
|
|
15
17
|
```tsx
|
|
@@ -23,7 +25,7 @@ import {
|
|
|
23
25
|
Stats,
|
|
24
26
|
} from "@headband/react";
|
|
25
27
|
|
|
26
|
-
const client = headband("https://your-headband-instance.com", "
|
|
28
|
+
const client = headband("https://your-headband-instance.com", "hb_src_YOUR_SEARCH_KEY");
|
|
27
29
|
|
|
28
30
|
function App() {
|
|
29
31
|
return (
|
|
@@ -68,6 +70,33 @@ HeadbandProvider -- owns all search state, executes searches
|
|
|
68
70
|
|-- useSearch() -- full state + actions (escape hatch)
|
|
69
71
|
```
|
|
70
72
|
|
|
73
|
+
### Base filters and full results pages
|
|
74
|
+
|
|
75
|
+
Use `baseFilter` on `HeadbandProvider` for app-owned constraints that should apply
|
|
76
|
+
to every query: tenant isolation, RBAC, publish status, availability, or custom
|
|
77
|
+
OR/AND groups. Refinement and range hooks keep adding UI filters, and Headband
|
|
78
|
+
ANDs them with `baseFilter` while preserving OR groups.
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { HeadbandProvider, filterEq, orFilters } from "@headband/react";
|
|
82
|
+
|
|
83
|
+
const availability = orFilters(filterEq("status", "published"), filterEq("status", "backorder"));
|
|
84
|
+
|
|
85
|
+
<HeadbandProvider
|
|
86
|
+
client={client}
|
|
87
|
+
index="products"
|
|
88
|
+
baseFilter={[filterEq("tenantId", "acme"), availability]}
|
|
89
|
+
>
|
|
90
|
+
<SearchBox />
|
|
91
|
+
<RefinementList attribute="brand" />
|
|
92
|
+
<RangeInput attribute="price" />
|
|
93
|
+
<Hits />
|
|
94
|
+
</HeadbandProvider>;
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This supports text search plus filters, filter-only browse pages, and mixed
|
|
98
|
+
boolean filters on every Headband engine.
|
|
99
|
+
|
|
71
100
|
---
|
|
72
101
|
|
|
73
102
|
## Client
|
|
@@ -79,15 +108,62 @@ Creates a Headband API client.
|
|
|
79
108
|
```ts
|
|
80
109
|
import { headband } from "@headband/react";
|
|
81
110
|
|
|
82
|
-
const client = headband("https://search.example.com", "
|
|
111
|
+
const client = headband("https://search.example.com", "hb_src_YOUR_SEARCH_KEY");
|
|
83
112
|
```
|
|
84
113
|
|
|
85
|
-
| Param | Type | Description
|
|
86
|
-
| -------- | -------- |
|
|
87
|
-
| `host` | `string` | Headband instance URL
|
|
88
|
-
| `apiKey` | `string` | API key for authentication
|
|
114
|
+
| Param | Type | Description |
|
|
115
|
+
| -------- | -------- | -------------------------- |
|
|
116
|
+
| `host` | `string` | Headband instance URL |
|
|
117
|
+
| `apiKey` | `string` | API key for authentication |
|
|
118
|
+
|
|
119
|
+
Returns a `HeadbandClient` with `search()`, `multiSearch()`, `getExperimentConfigs()`, `assignExperiment()`, and `recordExperimentEvent()` methods.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Retrieval experiments
|
|
89
124
|
|
|
90
|
-
|
|
125
|
+
Headband can store managed experiment rules, rollout percentages, variant weights, and kill-switch state. Apps can call hosted `client.assignExperiment()` when they want Headband to apply those rules, or use deterministic local helpers when offline/app-owned assignment is required.
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { HeadbandProvider, headband } from "@headband/react";
|
|
129
|
+
|
|
130
|
+
const client = headband("https://headband.dev", "hb_src_your_search_key");
|
|
131
|
+
const assignment = await client.assignExperiment({
|
|
132
|
+
indexUid: "products",
|
|
133
|
+
experimentKey: "engine-ab-test",
|
|
134
|
+
subjectId: sessionId,
|
|
135
|
+
sessionId,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (assignment.assigned) {
|
|
139
|
+
await client.recordExperimentEvent({
|
|
140
|
+
eventType: "experiment_view",
|
|
141
|
+
experimentKey: assignment.experimentKey,
|
|
142
|
+
variant: assignment.variant!,
|
|
143
|
+
indexUid: assignment.indexUid!,
|
|
144
|
+
sessionId,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
<HeadbandProvider
|
|
149
|
+
client={client}
|
|
150
|
+
index={assignment.assigned ? assignment.indexUid! : "products"}
|
|
151
|
+
experiment={
|
|
152
|
+
assignment.assigned
|
|
153
|
+
? {
|
|
154
|
+
experimentKey: assignment.experimentKey,
|
|
155
|
+
variant: assignment.variant!,
|
|
156
|
+
sessionId: assignment.sessionId,
|
|
157
|
+
}
|
|
158
|
+
: undefined
|
|
159
|
+
}
|
|
160
|
+
>
|
|
161
|
+
<SearchBox />
|
|
162
|
+
<Hits />
|
|
163
|
+
</HeadbandProvider>;
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Hosted assignment only returns a variant when the rule is `active`, the kill switch is off, and the subject falls inside `trafficPercent`. For local assignment, `assignExperiment(config, subjectId)` uses `assignmentSalt`, `experimentKey`, and your subject/session ID for stable bucketing. Headband receives `experimentKey`, `variant`, and `sessionId` on search calls for analytics attribution.
|
|
91
167
|
|
|
92
168
|
---
|
|
93
169
|
|
|
@@ -98,21 +174,23 @@ Returns a `HeadbandClient` with `search()` and `multiSearch()` methods.
|
|
|
98
174
|
Wraps your search UI and manages all search state.
|
|
99
175
|
|
|
100
176
|
```tsx
|
|
101
|
-
<HeadbandProvider
|
|
102
|
-
client={client}
|
|
103
|
-
index="products"
|
|
104
|
-
initialQuery="shoes"
|
|
105
|
-
>
|
|
177
|
+
<HeadbandProvider client={client} index="products" initialQuery="shoes">
|
|
106
178
|
{/* search components */}
|
|
107
179
|
</HeadbandProvider>
|
|
108
180
|
```
|
|
109
181
|
|
|
110
|
-
| Prop | Type
|
|
111
|
-
| -------------- |
|
|
112
|
-
| `client` | `HeadbandClient`
|
|
113
|
-
| `index` | `string`
|
|
114
|
-
| `initialQuery` | `string`
|
|
115
|
-
| `
|
|
182
|
+
| Prop | Type | Default | Description |
|
|
183
|
+
| -------------- | ---------------------------------- | ------- | ------------------------------------------------------ |
|
|
184
|
+
| `client` | `HeadbandClient` | -- | Client returned by `headband()` |
|
|
185
|
+
| `index` | `string` | -- | Index to search |
|
|
186
|
+
| `initialQuery` | `string` | `""` | Starting query value |
|
|
187
|
+
| `experiment` | `ExperimentSearchMetadata \| null` | `null` | Optional experiment telemetry attached to search calls |
|
|
188
|
+
| `mode` | `"instant" \| "retrieval"` | -- | Visor's ranking mode; other engines ignore it |
|
|
189
|
+
| `children` | `ReactNode` | -- | Child components |
|
|
190
|
+
|
|
191
|
+
`mode` is also a field of `client.search()` params and of each `client.multiSearch()` query.
|
|
192
|
+
On a Visor index, `instant` (the default) is typo-tolerant, as-you-type search, and
|
|
193
|
+
`retrieval` ranks longer queries by BM25F.
|
|
116
194
|
|
|
117
195
|
---
|
|
118
196
|
|
|
@@ -126,11 +204,11 @@ Binds to the search input. Returns the current query plus setters.
|
|
|
126
204
|
const { query, setQuery, clear } = useSearchBox();
|
|
127
205
|
```
|
|
128
206
|
|
|
129
|
-
| Return | Type
|
|
130
|
-
| ---------- |
|
|
131
|
-
| `query` | `string`
|
|
132
|
-
| `setQuery` | `(query: string) => void`
|
|
133
|
-
| `clear` | `() => void`
|
|
207
|
+
| Return | Type | Description |
|
|
208
|
+
| ---------- | ------------------------- | ------------------------------------ |
|
|
209
|
+
| `query` | `string` | Current query string |
|
|
210
|
+
| `setQuery` | `(query: string) => void` | Update the query (resets pagination) |
|
|
211
|
+
| `clear` | `() => void` | Clear the query to an empty string |
|
|
134
212
|
|
|
135
213
|
---
|
|
136
214
|
|
|
@@ -142,11 +220,11 @@ Returns the current search result hits with loading and error state.
|
|
|
142
220
|
const { hits, isLoading, error } = useHits<Movie>();
|
|
143
221
|
```
|
|
144
222
|
|
|
145
|
-
| Return | Type | Description
|
|
146
|
-
| ----------- | ------------- |
|
|
147
|
-
| `hits` | `Hit<T>[]` | Array of hit objects
|
|
148
|
-
| `isLoading` | `boolean` | Whether a search is in progress
|
|
149
|
-
| `error` | `Error\|null` | Error from the last search, if any
|
|
223
|
+
| Return | Type | Description |
|
|
224
|
+
| ----------- | ------------- | ---------------------------------- |
|
|
225
|
+
| `hits` | `Hit<T>[]` | Array of hit objects |
|
|
226
|
+
| `isLoading` | `boolean` | Whether a search is in progress |
|
|
227
|
+
| `error` | `Error\|null` | Error from the last search, if any |
|
|
150
228
|
|
|
151
229
|
Each `Hit<T>` includes the original document fields, an optional `_formatted` object with highlighted values, and a typed `__data: T` accessor.
|
|
152
230
|
|
|
@@ -164,18 +242,18 @@ const { items, refine, canToggle, isLoading } = useRefinementList({
|
|
|
164
242
|
});
|
|
165
243
|
```
|
|
166
244
|
|
|
167
|
-
| Prop | Type | Default | Description
|
|
168
|
-
| ----------- | -------------------- | --------- |
|
|
169
|
-
| `attribute` | `string` | -- | Facet attribute (e.g. `"genres"`)
|
|
170
|
-
| `limit` | `number` | `20` | Max facet values to show
|
|
171
|
-
| `sortBy` | `"count" \| "alpha"` | `"count"` | Sort order for facet values
|
|
245
|
+
| Prop | Type | Default | Description |
|
|
246
|
+
| ----------- | -------------------- | --------- | --------------------------------- |
|
|
247
|
+
| `attribute` | `string` | -- | Facet attribute (e.g. `"genres"`) |
|
|
248
|
+
| `limit` | `number` | `20` | Max facet values to show |
|
|
249
|
+
| `sortBy` | `"count" \| "alpha"` | `"count"` | Sort order for facet values |
|
|
172
250
|
|
|
173
|
-
| Return | Type
|
|
174
|
-
| ----------- |
|
|
175
|
-
| `items` | `RefinementListItem[]`
|
|
176
|
-
| `refine` | `(value: string) => void
|
|
177
|
-
| `canToggle` | `boolean`
|
|
178
|
-
| `isLoading` | `boolean`
|
|
251
|
+
| Return | Type | Description |
|
|
252
|
+
| ----------- | ------------------------- | -------------------------------- |
|
|
253
|
+
| `items` | `RefinementListItem[]` | Facet values with count & state |
|
|
254
|
+
| `refine` | `(value: string) => void` | Toggle a facet value on/off |
|
|
255
|
+
| `canToggle` | `boolean` | Whether any values are available |
|
|
256
|
+
| `isLoading` | `boolean` | Loading state |
|
|
179
257
|
|
|
180
258
|
Each `RefinementListItem` has `{ value: string; count: number; isRefined: boolean }`.
|
|
181
259
|
|
|
@@ -190,15 +268,15 @@ const { currentPage, totalPages, totalHits, setPage, canPrevious, canNext, pages
|
|
|
190
268
|
usePagination();
|
|
191
269
|
```
|
|
192
270
|
|
|
193
|
-
| Return | Type
|
|
194
|
-
| ------------- |
|
|
195
|
-
| `currentPage` | `number`
|
|
196
|
-
| `totalPages` | `number`
|
|
197
|
-
| `totalHits` | `number`
|
|
198
|
-
| `setPage` | `(page: number) => void`
|
|
199
|
-
| `canPrevious` | `boolean`
|
|
200
|
-
| `canNext` | `boolean`
|
|
201
|
-
| `pages` | `number[]`
|
|
271
|
+
| Return | Type | Description |
|
|
272
|
+
| ------------- | ------------------------ | ------------------------------------ |
|
|
273
|
+
| `currentPage` | `number` | Current page index (0-based) |
|
|
274
|
+
| `totalPages` | `number` | Total number of pages |
|
|
275
|
+
| `totalHits` | `number` | Total result count |
|
|
276
|
+
| `setPage` | `(page: number) => void` | Navigate to a page (clamped) |
|
|
277
|
+
| `canPrevious` | `boolean` | Whether a previous page exists |
|
|
278
|
+
| `canNext` | `boolean` | Whether a next page exists |
|
|
279
|
+
| `pages` | `number[]` | Window of page numbers for rendering |
|
|
202
280
|
|
|
203
281
|
---
|
|
204
282
|
|
|
@@ -210,12 +288,12 @@ Returns summary statistics about the current search.
|
|
|
210
288
|
const { totalHits, processingTimeMs, query, isLoading } = useStats();
|
|
211
289
|
```
|
|
212
290
|
|
|
213
|
-
| Return
|
|
214
|
-
|
|
|
215
|
-
| `totalHits`
|
|
216
|
-
| `processingTimeMs
|
|
217
|
-
| `query`
|
|
218
|
-
| `isLoading`
|
|
291
|
+
| Return | Type | Description |
|
|
292
|
+
| ------------------ | --------- | -------------------------------- |
|
|
293
|
+
| `totalHits` | `number` | Total results for the query |
|
|
294
|
+
| `processingTimeMs` | `number` | Server-side processing time (ms) |
|
|
295
|
+
| `query` | `string` | The query that produced results |
|
|
296
|
+
| `isLoading` | `boolean` | Loading state |
|
|
219
297
|
|
|
220
298
|
---
|
|
221
299
|
|
|
@@ -233,17 +311,17 @@ const { currentSort, setSort, items } = useSortBy({
|
|
|
233
311
|
});
|
|
234
312
|
```
|
|
235
313
|
|
|
236
|
-
| Prop | Type | Description
|
|
237
|
-
| ------- | -------------- |
|
|
238
|
-
| `items` | `SortByItem[]` | Available sort options
|
|
314
|
+
| Prop | Type | Description |
|
|
315
|
+
| ------- | -------------- | ---------------------- |
|
|
316
|
+
| `items` | `SortByItem[]` | Available sort options |
|
|
239
317
|
|
|
240
318
|
Each `SortByItem` has `{ value: string; label: string }`.
|
|
241
319
|
|
|
242
|
-
| Return | Type | Description
|
|
243
|
-
| ------------- | --------------------------------- |
|
|
320
|
+
| Return | Type | Description |
|
|
321
|
+
| ------------- | --------------------------------- | ------------------------------------ |
|
|
244
322
|
| `currentSort` | `string \| null` | Active sort value (null = relevance) |
|
|
245
|
-
| `setSort` | `(value: string\|null) => void` | Set the sort order
|
|
246
|
-
| `items` | `(SortByItem & { isSelected })[]` | Items with `isSelected` flag
|
|
323
|
+
| `setSort` | `(value: string\|null) => void` | Set the sort order |
|
|
324
|
+
| `items` | `(SortByItem & { isSelected })[]` | Items with `isSelected` flag |
|
|
247
325
|
|
|
248
326
|
---
|
|
249
327
|
|
|
@@ -257,18 +335,18 @@ const { min, max, stats, setRange, clear, isLoading } = useRange({
|
|
|
257
335
|
});
|
|
258
336
|
```
|
|
259
337
|
|
|
260
|
-
| Prop | Type | Description
|
|
261
|
-
| ----------- | -------- |
|
|
262
|
-
| `attribute` | `string` | Numeric attribute (e.g. `"price"`)
|
|
338
|
+
| Prop | Type | Description |
|
|
339
|
+
| ----------- | -------- | ---------------------------------- |
|
|
340
|
+
| `attribute` | `string` | Numeric attribute (e.g. `"price"`) |
|
|
263
341
|
|
|
264
|
-
| Return | Type
|
|
265
|
-
| ----------- |
|
|
266
|
-
| `min` | `number \| undefined`
|
|
267
|
-
| `max` | `number \| undefined`
|
|
268
|
-
| `stats` | `{ min: number; max: number } \| undefined
|
|
269
|
-
| `setRange` | `(range: { min?: number; max?: number }) => void` | Set range filter
|
|
270
|
-
| `clear` | `() => void`
|
|
271
|
-
| `isLoading` | `boolean`
|
|
342
|
+
| Return | Type | Description |
|
|
343
|
+
| ----------- | ------------------------------------------------- | --------------------------- |
|
|
344
|
+
| `min` | `number \| undefined` | Current min bound |
|
|
345
|
+
| `max` | `number \| undefined` | Current max bound |
|
|
346
|
+
| `stats` | `{ min: number; max: number } \| undefined` | Overall min/max from engine |
|
|
347
|
+
| `setRange` | `(range: { min?: number; max?: number }) => void` | Set range filter |
|
|
348
|
+
| `clear` | `() => void` | Remove the range filter |
|
|
349
|
+
| `isLoading` | `boolean` | Loading state |
|
|
272
350
|
|
|
273
351
|
---
|
|
274
352
|
|
|
@@ -283,15 +361,15 @@ const { value, highlighted } = useHighlight({
|
|
|
283
361
|
});
|
|
284
362
|
```
|
|
285
363
|
|
|
286
|
-
| Prop | Type
|
|
287
|
-
| ----------- |
|
|
288
|
-
| `hit` | `Record<string, unknown
|
|
289
|
-
| `attribute` | `string`
|
|
364
|
+
| Prop | Type | Description |
|
|
365
|
+
| ----------- | ------------------------- | ------------------------------- |
|
|
366
|
+
| `hit` | `Record<string, unknown>` | The hit object from `useHits()` |
|
|
367
|
+
| `attribute` | `string` | Attribute to highlight |
|
|
290
368
|
|
|
291
|
-
| Return | Type | Description
|
|
292
|
-
| ------------- | --------- |
|
|
293
|
-
| `value` | `string` | Highlighted HTML string (safe for innerHTML)
|
|
294
|
-
| `highlighted` | `boolean` | Whether the value contains highlight marks
|
|
369
|
+
| Return | Type | Description |
|
|
370
|
+
| ------------- | --------- | -------------------------------------------- |
|
|
371
|
+
| `value` | `string` | Highlighted HTML string (safe for innerHTML) |
|
|
372
|
+
| `highlighted` | `boolean` | Whether the value contains highlight marks |
|
|
295
373
|
|
|
296
374
|
Supports nested attributes via dot notation (e.g. `"author.name"`).
|
|
297
375
|
|
|
@@ -303,9 +381,25 @@ Escape-hatch hook that exposes the full search state and all actions. Prefer the
|
|
|
303
381
|
|
|
304
382
|
```tsx
|
|
305
383
|
const {
|
|
306
|
-
query,
|
|
307
|
-
|
|
308
|
-
|
|
384
|
+
query,
|
|
385
|
+
results,
|
|
386
|
+
isLoading,
|
|
387
|
+
error,
|
|
388
|
+
refinements,
|
|
389
|
+
ranges,
|
|
390
|
+
sortBy,
|
|
391
|
+
page,
|
|
392
|
+
hitsPerPage,
|
|
393
|
+
facets,
|
|
394
|
+
setQuery,
|
|
395
|
+
setPage,
|
|
396
|
+
setSortBy,
|
|
397
|
+
setHitsPerPage,
|
|
398
|
+
toggleRefinement,
|
|
399
|
+
setRange,
|
|
400
|
+
clearRange,
|
|
401
|
+
registerFacet,
|
|
402
|
+
unregisterFacet,
|
|
309
403
|
refresh,
|
|
310
404
|
} = useSearch();
|
|
311
405
|
```
|
|
@@ -321,20 +415,16 @@ All components accept a `styled` prop (default `true`) that toggles built-in Tai
|
|
|
321
415
|
Search input with built-in clear button and search icon.
|
|
322
416
|
|
|
323
417
|
```tsx
|
|
324
|
-
<SearchBox
|
|
325
|
-
placeholder="Search products..."
|
|
326
|
-
autoFocus
|
|
327
|
-
onQueryChange={(q) => console.log(q)}
|
|
328
|
-
/>
|
|
418
|
+
<SearchBox placeholder="Search products..." autoFocus onQueryChange={(q) => console.log(q)} />
|
|
329
419
|
```
|
|
330
420
|
|
|
331
|
-
| Prop | Type
|
|
332
|
-
| --------------- |
|
|
333
|
-
| `placeholder` | `string`
|
|
334
|
-
| `className` | `string`
|
|
335
|
-
| `autoFocus` | `boolean`
|
|
336
|
-
| `onQueryChange` | `(query: string) => void`
|
|
337
|
-
| `styled` | `boolean`
|
|
421
|
+
| Prop | Type | Default | Description |
|
|
422
|
+
| --------------- | ------------------------- | ------------- | ---------------------------- |
|
|
423
|
+
| `placeholder` | `string` | `"Search..."` | Input placeholder |
|
|
424
|
+
| `className` | `string` | -- | Additional CSS classes |
|
|
425
|
+
| `autoFocus` | `boolean` | `false` | Auto-focus on mount |
|
|
426
|
+
| `onQueryChange` | `(query: string) => void` | -- | Called on every query change |
|
|
427
|
+
| `styled` | `boolean` | `true` | Enable built-in styles |
|
|
338
428
|
|
|
339
429
|
---
|
|
340
430
|
|
|
@@ -346,12 +436,12 @@ Renders search result hits in a grid layout.
|
|
|
346
436
|
<Hits hitComponent={ProductCard} emptyComponent={<p>No results.</p>} />
|
|
347
437
|
```
|
|
348
438
|
|
|
349
|
-
| Prop | Type
|
|
350
|
-
| ---------------- |
|
|
351
|
-
| `hitComponent` | `ComponentType<{ hit: Hit }>`
|
|
352
|
-
| `className` | `string`
|
|
353
|
-
| `emptyComponent` | `ReactNode`
|
|
354
|
-
| `styled` | `boolean`
|
|
439
|
+
| Prop | Type | Default | Description |
|
|
440
|
+
| ---------------- | ----------------------------- | ------------ | ----------------------------- |
|
|
441
|
+
| `hitComponent` | `ComponentType<{ hit: Hit }>` | JSON card | Custom component per hit |
|
|
442
|
+
| `className` | `string` | -- | CSS classes for the container |
|
|
443
|
+
| `emptyComponent` | `ReactNode` | "No results" | Shown when there are no hits |
|
|
444
|
+
| `styled` | `boolean` | `true` | Enable built-in styles |
|
|
355
445
|
|
|
356
446
|
---
|
|
357
447
|
|
|
@@ -363,14 +453,14 @@ Checkbox list of facet values with counts.
|
|
|
363
453
|
<RefinementList attribute="brand" header="Brand" limit={10} sortBy="count" />
|
|
364
454
|
```
|
|
365
455
|
|
|
366
|
-
| Prop | Type | Default | Description
|
|
367
|
-
| ----------- | -------------------- | --------- |
|
|
368
|
-
| `attribute` | `string` | -- | Facet attribute
|
|
369
|
-
| `limit` | `number` | `20` | Max values to show
|
|
370
|
-
| `sortBy` | `"count" \| "alpha"` | `"count"` | Value sort order
|
|
371
|
-
| `header` | `string` | -- | Optional heading above the list
|
|
372
|
-
| `className` | `string` | -- | Additional CSS classes
|
|
373
|
-
| `styled` | `boolean` | `true` | Enable built-in styles
|
|
456
|
+
| Prop | Type | Default | Description |
|
|
457
|
+
| ----------- | -------------------- | --------- | ------------------------------- |
|
|
458
|
+
| `attribute` | `string` | -- | Facet attribute |
|
|
459
|
+
| `limit` | `number` | `20` | Max values to show |
|
|
460
|
+
| `sortBy` | `"count" \| "alpha"` | `"count"` | Value sort order |
|
|
461
|
+
| `header` | `string` | -- | Optional heading above the list |
|
|
462
|
+
| `className` | `string` | -- | Additional CSS classes |
|
|
463
|
+
| `styled` | `boolean` | `true` | Enable built-in styles |
|
|
374
464
|
|
|
375
465
|
---
|
|
376
466
|
|
|
@@ -382,10 +472,10 @@ Page navigation with Previous/Next buttons and numbered pages.
|
|
|
382
472
|
<Pagination />
|
|
383
473
|
```
|
|
384
474
|
|
|
385
|
-
| Prop | Type | Default | Description
|
|
386
|
-
| ----------- | --------- | ------- |
|
|
387
|
-
| `className` | `string` | -- | Additional CSS classes
|
|
388
|
-
| `styled` | `boolean` | `true` | Enable built-in styles
|
|
475
|
+
| Prop | Type | Default | Description |
|
|
476
|
+
| ----------- | --------- | ------- | ---------------------- |
|
|
477
|
+
| `className` | `string` | -- | Additional CSS classes |
|
|
478
|
+
| `styled` | `boolean` | `true` | Enable built-in styles |
|
|
389
479
|
|
|
390
480
|
---
|
|
391
481
|
|
|
@@ -400,11 +490,11 @@ Displays result count and processing time (e.g. "1,234 results in 3ms").
|
|
|
400
490
|
} />
|
|
401
491
|
```
|
|
402
492
|
|
|
403
|
-
| Prop | Type
|
|
404
|
-
| ----------- |
|
|
405
|
-
| `render` | `(stats: { totalHits; processingTimeMs; query }) => string` | --
|
|
406
|
-
| `className` | `string`
|
|
407
|
-
| `styled` | `boolean`
|
|
493
|
+
| Prop | Type | Default | Description |
|
|
494
|
+
| ----------- | ----------------------------------------------------------- | ------- | ---------------------- |
|
|
495
|
+
| `render` | `(stats: { totalHits; processingTimeMs; query }) => string` | -- | Custom render function |
|
|
496
|
+
| `className` | `string` | -- | Additional CSS classes |
|
|
497
|
+
| `styled` | `boolean` | `true` | Enable built-in styles |
|
|
408
498
|
|
|
409
499
|
---
|
|
410
500
|
|
|
@@ -421,12 +511,12 @@ Sort-by select dropdown.
|
|
|
421
511
|
/>
|
|
422
512
|
```
|
|
423
513
|
|
|
424
|
-
| Prop | Type | Default | Description
|
|
425
|
-
| ----------- | -------------- | ----------- |
|
|
426
|
-
| `items` | `SortByItem[]` | -- | Sort options
|
|
427
|
-
| `label` | `string` | `"Sort by"` | Accessibility label
|
|
428
|
-
| `className` | `string` | -- | Additional CSS classes
|
|
429
|
-
| `styled` | `boolean` | `true` | Enable built-in styles
|
|
514
|
+
| Prop | Type | Default | Description |
|
|
515
|
+
| ----------- | -------------- | ----------- | ---------------------- |
|
|
516
|
+
| `items` | `SortByItem[]` | -- | Sort options |
|
|
517
|
+
| `label` | `string` | `"Sort by"` | Accessibility label |
|
|
518
|
+
| `className` | `string` | -- | Additional CSS classes |
|
|
519
|
+
| `styled` | `boolean` | `true` | Enable built-in styles |
|
|
430
520
|
|
|
431
521
|
---
|
|
432
522
|
|
|
@@ -438,13 +528,13 @@ Renders highlighted text for a hit attribute. Safe for use with `dangerouslySetI
|
|
|
438
528
|
<Highlight hit={hit} attribute="title" />
|
|
439
529
|
```
|
|
440
530
|
|
|
441
|
-
| Prop | Type
|
|
442
|
-
| ----------- |
|
|
443
|
-
| `hit` | `Record<string, unknown>`
|
|
444
|
-
| `attribute` | `string`
|
|
445
|
-
| `tagName` | `keyof HTMLElementTagNameMap` | `"span"` | Wrapper element tag
|
|
446
|
-
| `className` | `string`
|
|
447
|
-
| `styled` | `boolean`
|
|
531
|
+
| Prop | Type | Default | Description |
|
|
532
|
+
| ----------- | ----------------------------- | -------- | --------------------------- |
|
|
533
|
+
| `hit` | `Record<string, unknown>` | -- | Hit object from `useHits()` |
|
|
534
|
+
| `attribute` | `string` | -- | Attribute to highlight |
|
|
535
|
+
| `tagName` | `keyof HTMLElementTagNameMap` | `"span"` | Wrapper element tag |
|
|
536
|
+
| `className` | `string` | -- | Additional CSS classes |
|
|
537
|
+
| `styled` | `boolean` | `true` | Enable built-in styles |
|
|
448
538
|
|
|
449
539
|
---
|
|
450
540
|
|
|
@@ -456,14 +546,14 @@ Min/max number inputs for numeric range filtering.
|
|
|
456
546
|
<RangeInput attribute="price" header="Price" />
|
|
457
547
|
```
|
|
458
548
|
|
|
459
|
-
| Prop | Type | Default | Description
|
|
460
|
-
| ---------------- | --------- | ------- |
|
|
461
|
-
| `attribute` | `string` | -- | Numeric attribute to filter
|
|
462
|
-
| `header` | `string` | -- | Optional heading
|
|
463
|
-
| `minPlaceholder` | `string` | `"Min"` | Min input placeholder
|
|
464
|
-
| `maxPlaceholder` | `string` | `"Max"` | Max input placeholder
|
|
465
|
-
| `className` | `string` | -- | Additional CSS classes
|
|
466
|
-
| `styled` | `boolean` | `true` | Enable built-in styles
|
|
549
|
+
| Prop | Type | Default | Description |
|
|
550
|
+
| ---------------- | --------- | ------- | --------------------------- |
|
|
551
|
+
| `attribute` | `string` | -- | Numeric attribute to filter |
|
|
552
|
+
| `header` | `string` | -- | Optional heading |
|
|
553
|
+
| `minPlaceholder` | `string` | `"Min"` | Min input placeholder |
|
|
554
|
+
| `maxPlaceholder` | `string` | `"Max"` | Max input placeholder |
|
|
555
|
+
| `className` | `string` | -- | Additional CSS classes |
|
|
556
|
+
| `styled` | `boolean` | `true` | Enable built-in styles |
|
|
467
557
|
|
|
468
558
|
---
|
|
469
559
|
|
|
@@ -475,30 +565,30 @@ Min/max number inputs for numeric range filtering.
|
|
|
475
565
|
<PoweredBy />
|
|
476
566
|
```
|
|
477
567
|
|
|
478
|
-
| Prop | Type | Default | Description
|
|
479
|
-
| ----------- | --------- | ------------------------ |
|
|
480
|
-
| `href` | `string` | `"https://headband.dev"` | Link URL
|
|
481
|
-
| `className` | `string` | -- | Additional CSS classes
|
|
482
|
-
| `styled` | `boolean` | `true` | Enable built-in styles
|
|
568
|
+
| Prop | Type | Default | Description |
|
|
569
|
+
| ----------- | --------- | ------------------------ | ---------------------- |
|
|
570
|
+
| `href` | `string` | `"https://headband.dev"` | Link URL |
|
|
571
|
+
| `className` | `string` | -- | Additional CSS classes |
|
|
572
|
+
| `styled` | `boolean` | `true` | Enable built-in styles |
|
|
483
573
|
|
|
484
574
|
---
|
|
485
575
|
|
|
486
576
|
## Comparison to Algolia InstantSearch
|
|
487
577
|
|
|
488
|
-
| Feature
|
|
489
|
-
|
|
|
490
|
-
| Provider
|
|
491
|
-
| Search input
|
|
492
|
-
| Results
|
|
493
|
-
| Facets
|
|
494
|
-
| Pagination
|
|
495
|
-
| Stats
|
|
496
|
-
| Sort
|
|
497
|
-
| Range
|
|
498
|
-
| Highlighting
|
|
499
|
-
| Bundle size
|
|
500
|
-
| Backend lock-in
|
|
501
|
-
| Built-in styles
|
|
578
|
+
| Feature | @headband/react | react-instantsearch |
|
|
579
|
+
| --------------- | ---------------------------------------- | ------------------------------ |
|
|
580
|
+
| Provider | `<HeadbandProvider>` | `<InstantSearch>` |
|
|
581
|
+
| Search input | `<SearchBox>` / `useSearchBox` | `<SearchBox>` / `useSearchBox` |
|
|
582
|
+
| Results | `<Hits>` / `useHits` | `<Hits>` / `useHits` |
|
|
583
|
+
| Facets | `<RefinementList>` | `<RefinementList>` |
|
|
584
|
+
| Pagination | `<Pagination>` | `<Pagination>` |
|
|
585
|
+
| Stats | `<Stats>` | `<Stats>` |
|
|
586
|
+
| Sort | `<SortBy>` | `<SortBy>` |
|
|
587
|
+
| Range | `<RangeInput>` / `useRange` | `<RangeInput>` / `useRange` |
|
|
588
|
+
| Highlighting | `<Highlight>` | `<Highlight>` |
|
|
589
|
+
| Bundle size | ~33 KB (ESM, unminified) | ~120 KB+ |
|
|
590
|
+
| Backend lock-in | None (Headband is engine-agnostic) | Algolia only |
|
|
591
|
+
| Built-in styles | Tailwind (opt-out with `styled={false}`) | CSS classes |
|
|
502
592
|
|
|
503
593
|
## TypeScript
|
|
504
594
|
|