1
0
Fork 0

feat(v5): Add new footerAction prop (#2952)

* feat(v5): Add new footerAction prop

* Resolve PR comments
This commit is contained in:
Paul Jankowski 2026-08-05 12:07:54 -04:00 committed by GitHub
parent 4e44b558e1
commit b28bc84904
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 428 additions and 99 deletions

View file

@ -0,0 +1,14 @@
---
"@docsearch/react": patch
"@docsearch/css": patch
"@docsearch/js": patch
---
feat(v5): add customizable footer action
- New `footerAction` prop renders a custom action in the modal footer,
before the Algolia logo, inside `.DocSearch-Footer-Action`
- `@docsearch/js` supports `footerAction` via template patterns (html helper,
JSX, or function-based)
- Fixes typing differences between `@docsearch/react` and `@docsearch/js`
- Restyle the footer with a `.DocSearch-Footer-Actions` wrapper

View file

@ -31,7 +31,7 @@
},
"adapters/docusaurus-theme-search-algolia": {
"name": "@docsearch/docusaurus-adapter",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"dependencies": {
"@algolia/autocomplete-core": "1.19.2",
"@docsearch/core": "workspace:*",
@ -115,7 +115,7 @@
},
"packages/docsearch-core": {
"name": "@docsearch/core",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"devDependencies": {
"@testing-library/jest-dom": "6.6.3",
"@testing-library/react": "16.2.0",
@ -137,7 +137,7 @@
},
"packages/docsearch-css": {
"name": "@docsearch/css",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"devDependencies": {
"browserslist": "4.28.2",
"lightningcss": "1.32.0",
@ -146,7 +146,7 @@
},
"packages/docsearch-js": {
"name": "@docsearch/js",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"dependencies": {
"@docsearch/core": "4.6.0",
"@docsearch/react": "4.6.0",
@ -160,7 +160,7 @@
},
"packages/docsearch-modal": {
"name": "@docsearch/modal",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"dependencies": {
"@docsearch/core": "4.6.0",
"@docsearch/react": "4.6.0",
@ -185,13 +185,13 @@
},
"packages/docsearch-react": {
"name": "@docsearch/react",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"dependencies": {
"@ai-sdk/react": "^2.0.30",
"@algolia/autocomplete-core": "1.19.2",
"@base-ui/react": "^1.5.0",
"@docsearch/core": "5.0.0-beta.1",
"@docsearch/css": "5.0.0-beta.1",
"@docsearch/core": "5.0.0-beta.2",
"@docsearch/css": "5.0.0-beta.2",
"ai": "^5.0.30",
"algoliasearch": "^5.28.0",
"marked": "^16.3.0",
@ -199,7 +199,7 @@
},
"devDependencies": {
"@algolia/autocomplete-core": "1.19.2",
"@docsearch/core": "5.0.0-beta.1",
"@docsearch/core": "5.0.0-beta.2",
"@testing-library/jest-dom": "6.6.3",
"@testing-library/react": "16.2.0",
"preact": "11.0.0-beta.0",
@ -222,7 +222,7 @@
},
"packages/docsearch-sidepanel": {
"name": "@docsearch/sidepanel",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"dependencies": {
"@docsearch/core": "4.6.0",
"@docsearch/css": "4.6.0",
@ -248,7 +248,7 @@
},
"packages/docsearch-sidepanel-js": {
"name": "@docsearch/sidepanel-js",
"version": "5.0.0-beta.1",
"version": "5.0.0-beta.2",
"dependencies": {
"@docsearch/core": "4.6.0",
"@docsearch/react": "4.6.0",

View file

@ -10,9 +10,9 @@
"preview": "vite preview"
},
"dependencies": {
"@docsearch/css": "5.0.0-beta.2",
"@docsearch/js": "5.0.0-beta.2",
"@docsearch/sidepanel-js": "5.0.0-beta.2"
"@docsearch/css": "workspace:*",
"@docsearch/js": "workspace:*",
"@docsearch/sidepanel-js": "workspace:*"
},
"devDependencies": {
"vite": "^6.0.7"

View file

@ -1,7 +1,4 @@
import docsearch, {
type DocSearchInstance,
type TemplateHelpers,
} from '@docsearch/js';
import docsearch, { type DocSearchInstance } from '@docsearch/js';
import sidepanel, { type SidepanelInstance } from '@docsearch/sidepanel-js';
import './app.css';
@ -91,10 +88,7 @@ docsearchInstance = docsearch({
// eslint-disable-next-line no-console
console.log('[demo-js] docsearch onClose()');
},
resultsFooterComponent: ({ state }, helpers?: TemplateHelpers) => {
const { html } = helpers || {};
if (!html) return null;
resultsFooterComponent: ({ state }, { html }) => {
return html`
<div class="DocSearch-HitsFooter">
<a href="https://docsearch.algolia.com/apply" target="_blank">

View file

@ -23,6 +23,10 @@ const customTools: ToolCalls = {
},
};
const FooterAction = (): JSX.Element => {
return <span>Bonjour</span>;
};
export default function BasicAskAI({
theme,
}: {
@ -50,6 +54,7 @@ export default function BasicAskAI({
translations={{ button: { buttonText: 'Search with Ask AI' } }}
theme={theme}
resultBadgeKey="type"
footerAction={<FooterAction />}
/>
);
}

View file

@ -1034,7 +1034,6 @@
border-block-start: 1px solid var(--docsearch-subtle-color);
border-radius: 0 0 var(--docsearch-modal-radius) var(--docsearch-modal-radius);
display: flex;
flex-direction: row-reverse;
flex-shrink: 0;
height: var(--docsearch-footer-height);
justify-content: space-between;
@ -1091,6 +1090,23 @@
text-transform: uppercase;
}
.DocSearch-Footer-Actions {
display: flex;
align-items: center;
gap: 1rem;
justify-content: space-between;
width: 100%;
@media screen and (width > 768px) {
justify-content: unset;
width: auto;
}
&:not(:has(.DocSearch-Footer-Action)) .DocSearch-Logo {
margin-inline-start: auto;
}
}
/* Hide element accessibly, so that it is still accessible to
assistive tech users */
.DocSearch-VisuallyHiddenForAccessibility {

View file

@ -0,0 +1,103 @@
import { createElement, type JSX } from 'preact';
import { afterEach, describe, expect, it } from 'vitest';
import { createDocSearch, type DocSearchInstance } from '../createDocSearch';
import type { FooterActionFn as KeywordFooterActionFn } from '../docsearch';
import type { FooterActionFn as AskAiFooterActionFn } from '../index';
interface TestComponentProps {
footerAction?: JSX.Element | null;
}
function TestComponent({ footerAction }: TestComponentProps): JSX.Element {
return createElement('div', null, footerAction);
}
const askAiFooterAction: AskAiFooterActionFn = (_props, { html }) =>
html`<button type="button">Ask AI footer action</button>`;
const keywordFooterAction: KeywordFooterActionFn = (_props, { html }) =>
html`<button type="button">Keyword footer action</button>`;
describe('createDocSearch', () => {
let instance: DocSearchInstance | undefined;
afterEach(() => {
instance?.destroy();
instance = undefined;
});
it('adapts footerAction templates for the rendered component', () => {
const container = document.createElement('div');
const footerAction = (): JSX.Element =>
createElement('button', { type: 'button' }, 'Footer action');
const docsearch = createDocSearch<TestComponentProps>(
TestComponent,
'test'
);
instance = docsearch({ container, footerAction });
expect(container.textContent).toContain('Footer action');
});
it('provides the html helper to footerAction templates', () => {
const container = document.createElement('div');
const docsearch = createDocSearch<TestComponentProps>(
TestComponent,
'test'
);
instance = docsearch({ container, footerAction: askAiFooterAction });
expect(container.textContent).toContain('Ask AI footer action');
});
it('accepts footerAction templates from the keyword-only entry point', () => {
const container = document.createElement('div');
const docsearch = createDocSearch<TestComponentProps>(
TestComponent,
'test'
);
instance = docsearch({ container, footerAction: keywordFooterAction });
expect(container.textContent).toContain('Keyword footer action');
});
it('renders string and component footerAction template returns', () => {
const container = document.createElement('div');
const docsearch = createDocSearch<TestComponentProps>(
TestComponent,
'test'
);
instance = docsearch({
container,
footerAction: () => () =>
createElement('button', { type: 'button' }, 'Component footer action'),
});
expect(container.textContent).toContain('Component footer action');
instance.destroy();
instance = docsearch({
container,
footerAction: () => 'Text footer action',
});
expect(container.textContent).toContain('Text footer action');
});
it('supports footerAction templates that return null', () => {
const container = document.createElement('div');
const docsearch = createDocSearch<TestComponentProps>(
TestComponent,
'test'
);
instance = docsearch({ container, footerAction: () => null });
expect(container.textContent).toBe('');
});
});

View file

@ -1,4 +1,8 @@
import type { DocSearchRef, InitialAskAiMessage } from '@docsearch/core';
import type {
ResultsFooterComponentProps,
HitComponentProps,
} from '@docsearch/react';
import htm from 'htm';
import type { ComponentType, JSX, Attributes } from 'preact';
import {
@ -25,10 +29,64 @@ export interface DocSearchCallbacks {
interceptAskAiEvent?: (initialMessage: InitialAskAiMessage) => boolean | void;
}
export type TemplateHelpers = { html: typeof html };
// Defines the public facing interface for each "template" function
type TemplateFnReturnType = JSX.Element | string | (() => JSX.Element) | null;
export type HitComponentFn = (
props: HitComponentProps,
helpers: TemplateHelpers
) => TemplateFnReturnType;
export type ResultsFooterComponentFn = (
props: ResultsFooterComponentProps,
helpers: TemplateHelpers
) => TemplateFnReturnType;
export type FooterActionFn = (
props: never,
helpers: TemplateHelpers
) => TemplateFnReturnType;
export type DocSearchProps<TProps> = DocSearchCallbacks &
Omit<TProps, 'onSidepanelClose' | 'onSidepanelOpen'> & {
Omit<
TProps,
| 'onSidepanelClose'
| 'onSidepanelOpen'
| 'hitComponent'
| 'resultsFooterComponent'
| 'footerAction'
> & {
container: HTMLElement | string;
environment?: typeof window;
/**
* Custom component to render an individual hit. Supports template patterns:
*
* - HTML strings with html helper: (props, { html }) => html`<div>...</div>`
* - JSX templates: (props) => <div>...</div>
* - Function-based templates: (props) => string | JSX.Element | Function.
*/
hitComponent?: HitComponentFn;
/**
* Custom component rendered at the bottom of the results panel. Supports
* template patterns:
*
* - HTML strings with html helper: (props, { html }) => html`<div>...</div>`
* - JSX templates: (props) => <div>...</div>
* - Function-based templates: (props) => string | JSX.Element | Function.
*/
resultsFooterComponent?: ResultsFooterComponentFn;
/**
* A custom action that can be rendered in the Modal's footer before the
* Algolia logo. The component will be rendered as a child of `<div
* className="DocSearch-Footer-Action" />`. Supports template patterns:
*
* - HTML strings with html helper: (props, { html }) => html`<div>...</div>`
* - JSX templates: (props) => <div>...</div>
* - Function-based templates: (props) => string | JSX.Element | Function.
*/
footerAction?: FooterActionFn;
};
function getHTMLElement(
@ -51,14 +109,12 @@ const html = htm.bind(createElement) as unknown as (
...values: unknown[]
) => JSX.Element;
export type TemplateHelpers = Record<string, unknown> & { html: typeof html };
function createTemplateFunction<
P extends Record<string, unknown>,
R = JSX.Element | string | (() => JSX.Element),
P = Record<string, unknown>,
R = TemplateFnReturnType,
>(
original: ((props: P, helpers?: TemplateHelpers) => R) | undefined
): ((props: P) => JSX.Element) | undefined {
original: ((props: P, helpers: TemplateHelpers) => R) | undefined
): ((props: P) => JSX.Element | null) | undefined {
if (!original) return undefined;
return (props: P) => {
@ -73,15 +129,10 @@ function createTemplateFunction<
}
interface ComponentProps {
hitComponent?: (
props: Record<string, unknown>,
helpers?: TemplateHelpers
) => JSX.Element;
resultsFooterComponent?: (
props: Record<string, unknown>,
helpers?: TemplateHelpers
) => JSX.Element | null;
hitComponent?: HitComponentFn;
resultsFooterComponent?: ResultsFooterComponentFn;
transformSearchClient?: (searchClient: unknown) => unknown;
footerAction?: FooterActionFn;
}
export function createDocSearch<TComponentProps, TInputProps = TComponentProps>(
@ -96,6 +147,7 @@ export function createDocSearch<TComponentProps, TInputProps = TComponentProps>(
transformSearchClient,
hitComponent,
resultsFooterComponent,
footerAction,
...rest
} = input;
const containerElement = getHTMLElement(
@ -105,11 +157,19 @@ export function createDocSearch<TComponentProps, TInputProps = TComponentProps>(
const ref = createRef<DocSearchRef>();
let isReady = false;
const FooterAction = createTemplateFunction(footerAction);
const props: TComponentProps = {
...rest,
ref,
hitComponent: createTemplateFunction(hitComponent),
resultsFooterComponent: createTemplateFunction(resultsFooterComponent),
hitComponent: createTemplateFunction<HitComponentProps>(hitComponent),
resultsFooterComponent:
createTemplateFunction<ResultsFooterComponentProps>(
resultsFooterComponent
),
footerAction: FooterAction
? createElement(FooterAction, null)
: undefined,
transformSearchClient: (searchClient: unknown): unknown => {
if (
typeof searchClient === 'object' &&

View file

@ -1,7 +1,12 @@
export type {
HitComponentFn,
ResultsFooterComponentFn,
FooterActionFn,
TemplateHelpers,
} from './createDocSearch';
export { docsearch as default } from './docsearchComponent';
export type {
DocSearchCallbacks,
DocSearchInstance,
DocSearchProps,
TemplateHelpers,
} from './docsearchComponent';

View file

@ -8,11 +8,7 @@ import {
type DocSearchProps as CreateDocSearchProps,
} from './createDocSearch';
export type {
DocSearchCallbacks,
DocSearchInstance,
TemplateHelpers,
} from './createDocSearch';
export type { DocSearchCallbacks, DocSearchInstance } from './createDocSearch';
export type DocSearchAIProps = CreateDocSearchProps<DocSearchComponentProps>;
export const docsearchAi: (allProps: DocSearchAIProps) => DocSearchInstance =

View file

@ -7,11 +7,7 @@ import {
type DocSearchProps as CreateDocSearchProps,
} from './createDocSearch';
export type {
DocSearchCallbacks,
DocSearchInstance,
TemplateHelpers,
} from './createDocSearch';
export type { DocSearchCallbacks, DocSearchInstance } from './createDocSearch';
export type DocSearchProps = CreateDocSearchProps<DocSearchComponentProps>;
export const docsearch: (allProps: DocSearchProps) => DocSearchInstance =

View file

@ -1,7 +1,12 @@
export type {
HitComponentFn,
ResultsFooterComponentFn,
FooterActionFn,
TemplateHelpers,
} from './createDocSearch';
export { docsearchAi as default } from './docsearchAi';
export type {
DocSearchAIProps as DocSearchProps,
DocSearchInstance,
DocSearchCallbacks,
TemplateHelpers,
} from './docsearchAi';

View file

@ -43,6 +43,15 @@ export interface DocSearchFacet {
label?: string;
}
export interface HitComponentProps {
hit: InternalDocSearchHit | StoredDocSearchHit;
children: React.ReactNode;
}
export interface ResultsFooterComponentProps {
state: AutocompleteState<InternalDocSearchHit>;
}
export interface DocSearchProps {
/** Algolia application id used by the search client. */
appId: string;
@ -69,38 +78,18 @@ export interface DocSearchProps {
maxResultsPerGroup?: number;
/** Hook to post-process hits before rendering. */
transformItems?: (items: DocSearchHit[]) => DocSearchHit[];
/**
* Custom component to render an individual hit. Supports template patterns:
*
* - HTML strings with html helper: (props, { html }) => html`<div>...</div>`
* - JSX templates: (props) => <div>...</div>
* - Function-based templates: (props) => string | JSX.Element | Function.
*/
hitComponent?: (
props: {
hit: InternalDocSearchHit | StoredDocSearchHit;
children: React.ReactNode;
},
helpers?: {
html: (template: TemplateStringsArray, ...values: any[]) => any;
}
) => JSX.Element;
/**
* Custom component rendered at the bottom of the results panel. Supports
* template patterns:
*
* - HTML strings with html helper: (props, { html }) => html`<div>...</div>`
* - JSX templates: (props) => <div>...</div>
* - Function-based templates: (props) => string | JSX.Element | Function.
*/
/** Custom component to render an individual hit. */
hitComponent?: (props: HitComponentProps) => JSX.Element;
/** Custom component rendered at the bottom of the results panel. */
resultsFooterComponent?: (
props: {
state: AutocompleteState<InternalDocSearchHit>;
},
helpers?: {
html: (template: TemplateStringsArray, ...values: any[]) => any;
}
props: ResultsFooterComponentProps
) => JSX.Element | null;
/**
* A custom action that can be rendered in the Modal's footer before the
* Algolia logo. The component will be rendered as a child of `<div
* className="DocSearch-Footer-Action" />`.
*/
footerAction?: React.ReactNode;
/** Hook to wrap or modify the algolia search client. */
transformSearchClient?: (
searchClient: DocSearchTransformClient

View file

@ -102,6 +102,7 @@ export function DocSearchAskAiModal({
indices,
facets,
isHybridModeSupported = false,
footerAction,
...props
}: DocSearchAskAiModalProps): JSX.Element {
const {
@ -645,6 +646,7 @@ export function DocSearchAskAiModal({
<Footer
translations={footerTranslations}
isAskAiActive={isAskAiActive}
footerAction={footerAction}
/>
}
onClose={onClose}

View file

@ -70,6 +70,7 @@ export function DocSearchModal({
recentSearchesWithFavoritesLimit = 4,
indices,
facets,
footerAction,
...props
}: DocSearchModalProps): JSX.Element {
const {
@ -296,7 +297,9 @@ export function DocSearchModal({
}}
/>
}
footer={<Footer translations={footerTranslations} />}
footer={
<Footer translations={footerTranslations} footerAction={footerAction} />
}
onClose={onClose}
/>
);

View file

@ -18,6 +18,7 @@ export type FooterTranslations = Partial<{
type FooterProps = Partial<{
translations: FooterTranslations;
isAskAiActive: boolean;
footerAction: React.ReactNode;
}>;
interface CommandIconProps {
@ -50,6 +51,7 @@ function CommandIcon(props: CommandIconProps): JSX.Element {
export function Footer({
translations = {},
isAskAiActive = false,
footerAction,
}: FooterProps): JSX.Element {
const {
selectText = 'Select',
@ -64,11 +66,13 @@ export function Footer({
poweredByText = 'Powered by',
} = translations;
const hasFooterAction =
footerAction !== null &&
footerAction !== undefined &&
typeof footerAction !== 'boolean';
return (
<>
<div className="DocSearch-Logo">
<AlgoliaLogo translations={{ poweredByText }} />
</div>
<ul className="DocSearch-Commands">
<li>
<kbd className="DocSearch-Commands-Key">
@ -105,6 +109,14 @@ export function Footer({
</span>
</li>
</ul>
<div className="DocSearch-Footer-Actions">
{hasFooterAction && (
<div className="DocSearch-Footer-Action">{footerAction}</div>
)}
<div className="DocSearch-Logo">
<AlgoliaLogo translations={{ poweredByText }} />
</div>
</div>
</>
);
}

View file

@ -32,6 +32,14 @@ function DocSearchAI(props: Partial<DocSearchAIProps>): JSX.Element {
);
}
function FooterAction(): JSX.Element {
return <button type="button">Footer action</button>;
}
function EmptyFooterAction(): null {
return null;
}
// mock empty response
function noResultSearch(_queries: any, _requestOptions?: any): Promise<any> {
return new Promise((resolve) => {
@ -96,6 +104,80 @@ describe('api', () => {
expect(document.querySelector(docSearchSelector)).toBeInTheDocument();
});
describe('footerAction', () => {
it('renders the action in the standard modal', async () => {
render(<DocSearch footerAction={<FooterAction />} />);
await act(async () => {
fireEvent.click(await screen.findByText('Search'));
});
expect(
await screen.findByRole('button', { name: 'Footer action' })
).toBeInTheDocument();
});
it('does not render an empty action', async () => {
render(<DocSearch footerAction={<EmptyFooterAction />} />);
await act(async () => {
fireEvent.click(await screen.findByText('Search'));
});
expect(
screen.queryByRole('button', { name: 'Footer action' })
).not.toBeInTheDocument();
});
it('does not render an action wrapper when no action is provided', async () => {
render(<DocSearch />);
await act(async () => {
fireEvent.click(await screen.findByText('Search'));
});
expect(
document.querySelector('.DocSearch-Footer-Action')
).not.toBeInTheDocument();
});
it('does not render an action wrapper for a boolean action', async () => {
render(<DocSearch footerAction={false} />);
await act(async () => {
fireEvent.click(await screen.findByText('Search'));
});
expect(
document.querySelector('.DocSearch-Footer-Action')
).not.toBeInTheDocument();
});
it('renders numeric footer actions', async () => {
render(<DocSearch footerAction={0} />);
await act(async () => {
fireEvent.click(await screen.findByText('Search'));
});
expect(document.querySelector('.DocSearch-Footer-Action')).toHaveTextContent(
'0'
);
});
it('renders the action in the Ask AI modal', async () => {
render(<DocSearchAI footerAction={<FooterAction />} />);
await act(async () => {
fireEvent.click(await screen.findByText('Search'));
});
expect(
await screen.findByRole('button', { name: 'Footer action' })
).toBeInTheDocument();
});
});
describe('translations', () => {
it('overrides the default DocSearchButton text', () => {
render(

View file

@ -176,16 +176,22 @@ Function that transforms hits before grouping and rendering. Defaults to the ide
### `hitComponent`
> `type: template function` | **optional**
> `type: ({ hit, children }, { html }) => JSX.Element` | **optional**
Template for a result link. Defaults to the built-in hit template. See [Templates](#templates).
### `resultsFooterComponent`
> `type: template function` | **optional**
> `type: ({ state }, { html }) => JSX.Element | null` | **optional**
Template below the result collections. There's no default. See [Templates](#templates).
### `footerAction`
> `type: (_, { html }) => JSX.Element | null` | **optional**
Optional function to render a custom action in the Modal's footer. It will be rendered in line with the Algolia "powered by" logo. See [Templates](#templates).
### `transformSearchClient`
> `type: (client) => client` | **optional**
@ -290,16 +296,26 @@ Set `translations.modal.resultsScreen.resultBadgeLabelText` to describe the badg
### Templates
`hitComponent` receives `{ hit, children }`. `resultsFooterComponent` receives `{ state }`. JavaScript templates can return a Preact element, a string, or a component function. The optional second argument provides an `html` tagged-template helper.
- `hitComponent` receives `{ hit, children }`.
- `resultsFooterComponent` receives `{ state }`.
- `footerAction` currently does not receive any extra props.
JavaScript templates can return a Preact element, a string, or a component function. The optional second argument provides an `html` tagged-template helper.
```js title="docsearch-options.js"
hitComponent({ hit, children }, { html }) {
return html`<a href=${hit.url} data-result-type=${hit.type}>${children}</a>`;
}
docsearch({
hitComponent({ hit, children }, { html }) {
return html`<a href=${hit.url} data-result-type=${hit.type}>${children}</a>`;
},
resultsFooterComponent({ state }, { html }) {
return html`<p>No results found for query: ${state.query}</p>`;
},
footerAction(_, { html }) {
return html`<a href="https://algolia.com">Our other project</a>`;
},
});
```
String returns render as text, not HTML.
## Ask AI options
Ask AI is available from the default `@docsearch/js` entry. Its `askAi` option is required by that entry's `DocSearchProps` type.

View file

@ -121,16 +121,22 @@ Receives the modal portal. Defaults to `document.body`.
#### `hitComponent`
> `type: (props, helpers?) => JSX.Element` | **optional**
> `type: (props) => JSX.Element` | **optional**
Renders an individual result. Defaults to the built-in hit component.
#### `resultsFooterComponent`
> `type: (props, helpers?) => JSX.Element | null` | **optional**
> `type: (props) => JSX.Element | null` | **optional**
Renders content below the results. Defaults to `null`.
#### `footerAction`
> `type: React.ReactNode` | **optional**
Renders a custom element in the modal footer, before the "Powered by Algolia" logo. Passing `null` renders nothing.
#### `disableUserPersonalization`
> `type: boolean` | **optional**

View file

@ -95,12 +95,12 @@ Transforms hits before DocSearch groups and renders them. Defaults to the identi
### `hitComponent`
> `type: React.ComponentType<HitProps>` | **optional**
> `type: ({ hit, children }: HitComponentProps) => JSX.Element` | **optional**
Renders one result link. The default component renders the standard result content.
```tsx title="Hit.tsx"
function Hit({ hit, children }: HitProps): JSX.Element {
function Hit({ hit, children }: HitComponentProps): JSX.Element {
return (
<a href={hit.url} data-result-type={hit.type}>
{children}
@ -113,10 +113,35 @@ Preserve `children` to retain the default hit content. `hit` is an `InternalDocS
### `resultsFooterComponent`
> `type: React.ComponentType<ResultsFooterProps>` | **optional**
> `type: ({ state }: ResultsFooterComponentProps) => JSX.Element | null` | **optional**
Renders below result collections. It receives the current Autocomplete state. By default, DocSearch doesn't render a footer.
### `footerAction`
> `type: React.ReactNode` | **optional**
Renders a custom element in the modal footer, before the "Powered by Algolia" logo. Passing `null` renders nothing.
```tsx title="Search.tsx"
function FooterAction(): JSX.Element {
return (
<a href="https://algolia.com">
Our other project
</a>
);
}
function Search() {
return (
<DocSearch
// ...
footerAction={<FooterAction />}
/>
);
}
```
### `transformSearchClient`
> `type: (client: SearchClient) => SearchClient` | **optional**