1
0
Fork 0
docsearch/packages/website/docs/composable-api.mdx
Paul Jankowski ecd905d440
feat: promote DocSearch v5 to main (#2968)
* feat(askai): add compatibility with algolia mcp search tool [DASH-2294] (#2862)

## Summary
Fixes DASH-2294
Add compatibility with the Algolia MCP search tool (`algolia_search_index_${string}`) in AskAI.

## Changes
- Add `AlgoliaMCPSearchTool` type to handle the Algolia MCP server search tool
- Refactor how number of hits are retrieved in `ToolCall` into a `getNumberOfHits` helper

## Test plan
- Added unit tests for modified code 

* chore: Update to use tsdown for build system (#2824)

* chore: Update to use tsdown for build system

* fix: docsearch-react build

* fix: lint

* fix: glob resolved to incorrect version

* chore: migrate to from yarn, lerna and shipjs to bun & changesets (#2827)

* chore: add tool-versions file for node and bun versions (#2866)

* chore: watch in parallel (#2867)

* feat: agent studio feedback integration (#2868)

* feat(askai): Split Ask AI modal into own component (#2884)

* feat(askai): Split Ask AI modal into own component

* refactor(react): share modal utilities

* refactor(react): share search box form

* refactor(react): extract start screen sections

* refactor(react): extract shared modal hooks

* fix: lint adapter

* refactor(react): reorganize modal files

* fix: type error in examples

* fix: remove ai modal from adapter for now, fix import paths of react package

* feat(askai): Agent Studio core tools (#2886)

* feat(askai): Implement dynamic tool calls

* move ToolCall to components dir

* Converge Agent Studio search tools to same definition, fix client side tools breaking UI state

* add examples for custom tools

* fix: lint & types

* feat(askai): add Agent Studio memory support (#2888)

* feat(askai): remove Ask AI transport layer (#2889)

* feat(askai): add Agent Studio memory support

* refactor(askai): remove Ask AI transport abstraction

* feat(askai): Feedback notes and tags (#2890)

* feat(askai): add Agent Studio memory support

* refactor(askai): remove Ask AI transport abstraction

* feat(askai): Feedback notes and tags

* fix: bump css bundle size limit

* move feedback actions to components

* chore: fix deploys for v5 branch

* feat(askai): Aggregate MCP search tool calls (#2891)

* feat(askai): add Agent Studio memory support

* refactor(askai): remove Ask AI transport abstraction

* feat(askai): Feedback notes and tags

* fix: bump css bundle size limit

* move feedback actions to components

* feat(askai): Aggregate MCP search tool calls

* feat(askai): Allow dynamic indices for Agent Studio (#2893)

* feat(v5): UI updates (#2896)

* feat(v5): UI updates

* fix: css file size

* fix: e2e tests

* fix: e2e tests

* fix: e2e tests

* chore: add theme toggle to react demo example

* Update sources panel display, update dark theme

* fix: lint

* fix(askai): address ui review feedback

* fix: pin icon positioning

* fix(askai): improve a11y and dark-mode shimmer for thinking and error states

- add role=alert/status and aria-hidden on error/thinking UI
- support dark-mode shimmer gradients via CSS variables
- respect prefers-reduced-motion for shimmer
- handle null date in useRelativeFormattedDate with fallback translation

* feat(v5): Add hit breadcrumbs (#2897)

* feat(v5): UI updates

* fix: css file size

* fix: e2e tests

* fix: e2e tests

* fix: e2e tests

* chore: add theme toggle to react demo example

* Update sources panel display, update dark theme

* fix: lint

* fix(askai): address ui review feedback

* fix: pin icon positioning

* fix(askai): improve a11y and dark-mode shimmer for thinking and error states

- add role=alert/status and aria-hidden on error/thinking UI
- support dark-mode shimmer gradients via CSS variables
- respect prefers-reduced-motion for shimmer
- handle null date in useRelativeFormattedDate with fallback translation

* feat(v5): Add hit breadcrumbs

* fix: bump css bundle size limit

* Fix after conflicts

* chore: move CSS building to lightning css (#2898)

* feat: Facet filters for search (#2899)

* feat(v5): Initial facet filters work

* Perf updates, dark theme, facet chips, a11y improvements

* fix: bump css bundle size limit

* Dedupe facet filters, refetch facets on searchParameters changes

* Add chevron flourish

* fix(askai): Fix new conversation causing thread depth errors (#2900)

* feat(v5): Add hit result badge (#2901)

* feat(v5): Add hit result badge

* Add background to hit result badge

* feat(v5): Add follow up prompt suggestions (#2902)

* feat(v5): Add follow up prompt suggestions

* fix: bump css bundle size limit

* docs(agents): document Cursor Cloud dev environment setup for v5 (Bun) (#2903)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* feat(mcp): setup mcp plugins (#2895)

* feat(v5): Add prompt suggestions to keyword search (#2912)

* feat(v5): Add prompt suggestions to keyword search

* cleanup: Move consistent object to reusable constant

* chore(v5): Split Ask AI related CSS into own bundle (#2913)

* chore(v5): Split Ask AI related CSS into own bundle

* move style.css to include modal and askai

* fix: Ensure stage level and watch level scripts use bun runtime (#2915)

* fix: Ensure stage level and watch level scripts use bun runtime

* chore: move to node@24 update imports

* fix: lint

* feat(js): Document JS based hybrid mode, fix JS packages (#2916)

* feat(js): Document JS based hybrid mode, fix JS packages

* update: add model onOpen to docs

* feat(cli): add @docsearch/cli for MCP setup and search (#2911)

* chore(tsdown): Bump to latest tsdown version (#2918)

* chore(tsdown): Bump to latest tsdown version

* fix: bump nvmrc node version

* fix: cli tsconfig

* fix: website build

* fix: example build

* fix: circleci install bun

* fix: lint

* fix: circleci install bun

* fix: circleci install bun

* fix: circleci install bun

* refactor(docusaurus-adapter): rework theme config for v5 and modularize SearchPage (#2904)

Co-authored-by: Paul Jankowski <8BitTitan@gmail.com>

* feat(askai): Move askai related props under root askai (#2919)

* feat(askai): Move askai related props under root askai

* fix: playwright test case

* fix(docusaurus): validate Ask AI options

* feat(js): Split JS bundles for search only (#2920)

* chore: Move to oxlint and oxfmt (#2923)

* chore: Get NPM OIDC token before publishing (#2924)

* chore: Enter v5 beta (#2925)

* chore: Enter pre release mode for v5

* chore: update release summary

* chore: version bump

* fix: Remove NPM_ID_TOKEN for release

* fix: Try setting blank NPM_TOKEN

* fix: Try blank NPM_AUTH_TOKEN

* chore: bump node and npm for release job

* docs(mcp): add service disclaimer (#2921)

* fix: Agent Studio MCP search tool (#2927)

* fix: Agent Studio MCP search tool

* Add changeset

* chore: Update stylelint (#2926)

* chore: stylelint update

* bun.lock

* Add changeset

* fix(website): use bare @import for tailwindcss (#2933)

Tailwind's build-time `@import` cannot be written with `url()` notation,
so `@import url('tailwindcss')` was passed through as a plain CSS import
instead of being processed by Tailwind.

Also syncs bun.lock with the 5.0.0-beta.0 versions already committed to
package.json.

* chore: version v5.0.0-beta.1 (#2932)

* feat(react): remove deprecated index props (#2936)

* feat(mcp): add ChatGPT and Codex DocSearch plugin package (#2938)

* fix: cleanup claude

* feat: website redesign (#2930)

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: lint

* fix: crash on the demo (#2940)

* feat(docs): Document v5 beta (#2935)

* chore(docs): v5 documentation

* Writing style clean up

* fix: website after conflicts

* fix: reported issues on mobile (#2944)

* chore: Introduce a11y smoke tests (#2943)

* chore: Add Lorris as codeowner (#2946)

* fix(askai): Ask AI fixes for v5 (#2945)

* fix: General v5 fixes (#2947)

- Fix `ref` console error for a `FacetMenu`
- Whitespace only search/conversation input does not trigger requests
- Fix flash of no results page on search

* feat: v5 general improvements (#2948)

* feat(v5): General fixes and improvements

* add changeset

* fix: bundlesize

* feat(v5): UI and DX improvements (#2949)

* feat: Rename assistantId to agentId

* feat: Allow reading default facet values from index searchParameters

* feat: Remove indexName prop from Sidepanel, cleanup documentation pages

* feat: Move appId and apiKey up into @docsearch/core

* feat: Add back nested grouping of search results

* add changeset

* revert changes to example demo

* fix: e2e tests

* chore: push git tags on version release (#2951)

* chore: release v5.0.0-beta.2 (#2950)

* chore: Fix pushing git tags (#2953)

* fix(askai): sanitize markdown HTML in v5 (#2954)

Backport of #2929.\n\nOriginal commit: 681cbfec03

Co-authored-by: Vasco Bettencourt <32492444+vascobettencourt@users.noreply.github.com>

* fix(v5): stop truncating mobile snippets (#2958)

* fix(v5): stop truncating mobile snippets

Backport of #2907.\n\nOriginal commit: 9ad6d169fe

* fix(v5): allow mobile hit text to wrap

Completes the v5 adaptation of #2907 by overriding later v5 child-level truncation rules.\n\nOriginal commit: 9ad6d169fe

* chore(v5): account for mobile wrapping CSS

Updates the CSS size budget for the v5 adaptation of #2907.\n\nOriginal commit: 9ad6d169fe

---------

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>

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

* feat(v5): Add new footerAction prop

* Resolve PR comments

* fix(v5): recognize conversation depth errors (#2957)

Backport of #2881.\n\nOriginal commit: f68e52251c

Co-authored-by: Felipe Bermudez <felipeberm@gmail.com>

* fix(v5): expose Sidepanel search parameter types (#2956)

* fix(v5): expose Sidepanel search parameter types

Backport of #2906.\n\nOriginal commit: 4710d0ca77

* Delete sidepanel.test.ts

Had a pointless test case in it.

---------

Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>

* fix(v5): ignore slash shortcut on focused buttons (#2955)

Backport of #2871.\n\nOriginal commit: 0e41a78c44

Co-authored-by: Sigmabro <122412346+Sigmabrogz@users.noreply.github.com>

* fix(agentStudio): agents dynamic mode enabled (#2959)

* fix(agentStudio): agents dynamic mode enabled

* fix(askai): use string[] for dynamic agentStudio indices

* feat(docs): add Ask AI to Agent Studio migration guide (#2931)

* feat(docs): add Ask AI to Agent Studio migration guide

* feat(docs): agentStudio migrating from askAI

* feat(docs): renaming agentId

* feat(agentStudio): dynamic mode indices updated

* fix: Docusaurus adapter styling, DocSearch website fixes (#2960)

* chore: release v5.0.0-beta.3 (#2961)

* feat(website): Launch updates (#2964)

* fix(website): Fix font loading (#2966)

* feat(v5): Back port cost control errors (#2965)

* chore: release v5.0.0-beta.4 (#2967)

---------

Co-authored-by: Vincent Lemeunier <vincentlemeunier+git@gmail.com>
Co-authored-by: Dylan Tientcheu <dylan.tientcheu@algolia.com>
Co-authored-by: Lorris Saint-Genez <lorrissaintgenez@gmail.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Dylan Tientcheu <dylantientcheu@gmail.com>
Co-authored-by: Vasco Bettencourt <32492444+vascobettencourt@users.noreply.github.com>
Co-authored-by: Divyansh Singh <40380293+brc-dd@users.noreply.github.com>
Co-authored-by: Felipe Bermudez <felipeberm@gmail.com>
Co-authored-by: Sigmabro <122412346+Sigmabrogz@users.noreply.github.com>
2026-08-06 15:31:45 -04:00

410 lines
13 KiB
Text

---
title: Composable API
description: Build a DocSearch v5 interface from the button, keyword modal, Ask AI modal, and shared state provider.
---
import TabItem from '@theme/TabItem';
import Tabs from '@theme/Tabs';
The composable API gives you control over where DocSearch renders, when its modal code loads, and how the rest of your application opens or closes it.
Use this API in React applications. For the connected component reference, see the [modal package overview](/docs/packages/modal/overview).
## Choose a modal
DocSearch v5 provides two modal components. Render one modal for each `DocSearch` provider.
| Component | Use it for |
| --------------------- | ------------------------------------------- |
| `DocSearchModal` | Keyword search without Ask AI |
| `DocSearchAskAiModal` | Keyword search and Ask AI in the same modal |
`DocSearchAskAiModal` includes keyword search. Don't render both modal components to add Ask AI.
The composable components come from three packages:
- [`@docsearch/core`](/docs/packages/core/overview) provides `DocSearch`, shared state, keyboard handling, and the imperative ref.
- [`@docsearch/modal`](/docs/packages/modal/overview) provides the provider-connected button and modal components.
- [`@docsearch/css`](/docs/packages/css/styling) provides the styles.
## Install the packages
Install matching v5 versions of the DocSearch packages:
<Tabs groupId="package-manager" aria-label="Package manager">
<TabItem value="npm" label="npm">
```bash
npm install @docsearch/core@^5.0.0-beta @docsearch/modal@^5.0.0-beta @docsearch/css@^5.0.0-beta
```
</TabItem>
<TabItem value="yarn" label="Yarn">
```bash
yarn add @docsearch/core@^5.0.0-beta @docsearch/modal@^5.0.0-beta @docsearch/css@^5.0.0-beta
```
</TabItem>
<TabItem value="pnpm" label="pnpm">
```bash
pnpm add @docsearch/core@^5.0.0-beta @docsearch/modal@^5.0.0-beta @docsearch/css@^5.0.0-beta
```
</TabItem>
<TabItem value="bun" label="Bun">
```bash
bun add @docsearch/core@^5.0.0-beta @docsearch/modal@^5.0.0-beta @docsearch/css@^5.0.0-beta
```
</TabItem>
</Tabs>
## Add keyword search
Wrap the button and keyword modal in one `DocSearch` provider. Pass a public search-only API key to the provider; its descendants use these credentials by default.
```tsx title="KeywordSearch.tsx"
import { DocSearch } from '@docsearch/core';
import { DocSearchButton, DocSearchModal } from '@docsearch/modal';
import type { JSX } from 'react';
import '@docsearch/css/dist/style.css';
interface KeywordSearchProps {
appId: string;
apiKey: string;
indexName: string;
}
export function KeywordSearch({
appId,
apiKey,
indexName,
}: KeywordSearchProps): JSX.Element {
return (
<DocSearch appId={appId} apiKey={apiKey}>
<DocSearchButton
translations={{
buttonText: 'Search docs',
buttonAriaLabel: 'Search documentation',
}}
/>
<DocSearchModal indices={[indexName]} />
</DocSearch>
);
}
```
The provider opens the modal when a user selects the button, presses <kbd>Ctrl</kbd>/<kbd>Command</kbd>+<kbd>K</kbd>, or presses <kbd>/</kbd> outside an editable field. Closing the modal returns focus to `DocSearchButton`.
## Add keyword search and Ask AI
Replace `DocSearchModal` with `DocSearchAskAiModal`. Create the assistant in [Agent Studio](/docs/agent-studio/getting-started), then configure its ID on the provider.
```tsx title="SearchWithAskAi.tsx"
import { DocSearch } from '@docsearch/core';
import { DocSearchAskAiModal, DocSearchButton } from '@docsearch/modal';
import type { JSX } from 'react';
import '@docsearch/css/dist/style.css';
interface SearchWithAskAiProps {
appId: string;
apiKey: string;
indexName: string;
agentId: string;
}
export function SearchWithAskAi({
appId,
apiKey,
indexName,
agentId,
}: SearchWithAskAiProps): JSX.Element {
return (
<DocSearch appId={appId} apiKey={apiKey}>
<DocSearchButton />
<DocSearchAskAiModal
indices={[indexName]}
askAi={agentId}
/>
</DocSearch>
);
}
```
The `askAi` prop accepts an assistant ID string or an object with `agentId`. Use the object form when you need options such as `indices`, `searchParameters`, `suggestedQuestions`, `promptSuggestions`, `tools`, or `memory`. Set `appId` and `apiKey` on an individual modal or Sidepanel only when they must override the provider's defaults.
## Understand the shared state
`DocSearch` holds one state value and shares it with its descendants:
| State | Meaning |
| -------------- | -------------------------------- |
| `ready` | No modal or Sidepanel is open. |
| `modal-search` | The keyword search view is open. |
| `modal-askai` | Ask AI is open in the modal. |
| `sidepanel` | The Ask AI Sidepanel is open. |
The connected components manage these transitions for you:
- `DocSearchButton` calls its own `onClick` handler, then opens keyword search.
- Each modal registers itself with the provider and renders in a React portal only while a modal state is active.
- `DocSearchModal` reads the provider's initial query and close action.
- `DocSearchAskAiModal` also reads and updates the Ask AI state.
Use `useDocSearch` in a component under the provider when your application needs declarative access to this state.
```tsx
import { useDocSearch } from '@docsearch/core';
import type { JSX } from 'react';
export function SearchControls(): JSX.Element {
const { closeModal, docsearchState, openModal, onAskAiToggle } =
useDocSearch();
return (
<div>
<span>Search state: {docsearchState}</span>
<button type="button" onClick={openModal}>
Open search
</button>
<button type="button" onClick={() => onAskAiToggle(true)}>
Open Ask AI
</button>
<button type="button" onClick={closeModal}>
Close search
</button>
</div>
);
}
```
Only call `onAskAiToggle(true)` when the provider contains `DocSearchAskAiModal` or a compatible Ask AI view.
## Control DocSearch with a ref
Attach a `DocSearchRef` to the provider when non-React code or a parent component must control DocSearch.
```tsx
import { DocSearch, type DocSearchRef } from '@docsearch/core';
import { DocSearchAskAiModal, DocSearchButton } from '@docsearch/modal';
import { useRef, type JSX } from 'react';
interface ControlledSearchProps {
appId: string;
apiKey: string;
indexName: string;
agentId: string;
onReady?: () => void;
onOpen?: () => void;
onClose?: () => void;
}
export function ControlledSearch(props: ControlledSearchProps): JSX.Element {
const searchRef = useRef<DocSearchRef>(null);
return (
<DocSearch
ref={searchRef}
onReady={props.onReady}
onOpen={props.onOpen}
onClose={props.onClose}
>
<DocSearchButton />
<button
type="button"
onClick={() =>
searchRef.current?.openAskAi({
query: 'How do I configure DocSearch?',
})
}
>
Ask a question
</button>
<DocSearchAskAiModal
appId={props.appId}
apiKey={props.apiKey}
indices={[props.indexName]}
askAi={props.agentId}
/>
</DocSearch>
);
}
```
The ref exposes these methods and read-only values. `initialMessage` has a required `query` and optional `messageId` and `suggestedQuestionId` fields.
The provider accepts `onReady`, `onOpen`, `onClose`, `onSidepanelOpen`, and `onSidepanelClose`. `onReady` runs after mount. The other callbacks run once when their corresponding view changes state, not every time React renders.
### `open`
> `type: () => void`
Opens keyword search.
### `close`
> `type: () => void`
Returns the provider to `ready`.
### `openAskAi`
> `type: (initialMessage?: InitialAskAiMessage) => void`
Opens Ask AI in a registered Sidepanel on desktop, or in the modal otherwise.
### `openSidepanel`
> `type: (initialMessage?: InitialAskAiMessage) => void`
Opens a registered Sidepanel. It does nothing when no Sidepanel is registered.
### `isReady`
> `type: readonly boolean`
Reports whether the provider is mounted.
### `isOpen`
> `type: readonly boolean`
Reports whether a modal view is open.
### `isSidepanelOpen`
> `type: readonly boolean`
Reports whether the Sidepanel is open.
### `isSidepanelSupported`
> `type: readonly boolean`
Reports whether desktop hybrid mode is available.
## Load the modal on demand
Import the button eagerly and split the larger modal into another JavaScript chunk. The following Ask AI example preloads that chunk on hover, focus, or touch, then renders it only after the provider opens a modal state.
```tsx title="LazySearch.tsx"
import { DocSearch, useDocSearch } from '@docsearch/core';
import { DocSearchButton } from '@docsearch/modal/button';
import type { DocSearchAskAiModalProps } from '@docsearch/modal/askai';
import { lazy, Suspense, type JSX } from 'react';
import '@docsearch/css/dist/style.css';
let modalImport: Promise<typeof import('@docsearch/modal/askai')> | undefined;
function loadModal(): Promise<typeof import('@docsearch/modal/askai')> {
modalImport ??= import('@docsearch/modal/askai');
return modalImport;
}
function preloadModal(): void {
void loadModal().catch(() => {
modalImport = undefined;
});
}
const LazyDocSearchAskAiModal = lazy(() =>
loadModal().then(({ DocSearchAskAiModal }) => ({
default: DocSearchAskAiModal,
}))
);
function ModalWhenOpen(props: DocSearchAskAiModalProps): JSX.Element | null {
const { isModalActive } = useDocSearch();
if (!isModalActive) {
return null;
}
return (
<Suspense fallback={<span role="status">Loading search...</span>}>
<LazyDocSearchAskAiModal {...props} />
</Suspense>
);
}
interface LazySearchProps {
appId: string;
apiKey: string;
indexName: string;
agentId: string;
}
export function LazySearch(props: LazySearchProps): JSX.Element {
return (
<DocSearch>
<DocSearchButton
onFocus={preloadModal}
onMouseEnter={preloadModal}
onTouchStart={preloadModal}
/>
<ModalWhenOpen
appId={props.appId}
apiKey={props.apiKey}
indices={[props.indexName]}
askAi={props.agentId}
/>
</DocSearch>
);
}
```
Render the provider and connected components only in the browser. The modal wrappers read `document.body` and `window.scrollY` when they render.
## Use the exact entry points
Use the package root for convenience or a subpath to keep eager bundles focused.
| Import | Exports |
| --- | --- |
| `@docsearch/core` | `DocSearch`, `useDocSearch`, their types, keyboard utilities, and theme utilities |
| `@docsearch/modal` | `DocSearchButton`, `DocSearchModal`, `DocSearchAskAiModal`, and their prop types |
| `@docsearch/modal/button` | `DocSearchButton`, `DocSearchButtonProps` |
| `@docsearch/modal/modal` | `DocSearchModal`, `DocSearchModalProps` |
| `@docsearch/modal/askai` | `DocSearchAskAiModal`, `DocSearchAskAiModalProps` |
The lower-level React entry points are `@docsearch/react/button`, `@docsearch/react/modal`, and `@docsearch/react/askaiModal`. They don't connect themselves to the composable provider. Use them only when you intend to manage portal rendering, refs, `initialScrollY`, close behavior, and Ask AI state yourself.
## Load the styles
For either complete modal, import the combined stylesheet once:
```ts
import '@docsearch/css/dist/style.css';
```
The combined stylesheet contains variables, button styles, keyword modal styles, and Ask AI modal styles. It doesn't contain Sidepanel styles.
For a keyword-only CSS bundle, import the layers in this order:
```ts
import '@docsearch/css/dist/_variables.css';
import '@docsearch/css/dist/button.css';
import '@docsearch/css/dist/modal.css';
```
Add `@docsearch/css/dist/_askai.css` when you use `DocSearchAskAiModal`. Bundlers can also load the same files through `@docsearch/react/style`, `@docsearch/react/style/button`, `@docsearch/react/style/modal`, `@docsearch/react/style/askai`, and `@docsearch/react/style/variables`.
## API summary
| API | Required configuration | Provider-managed behavior |
| --- | --- | --- |
| `DocSearch` | `children` | State, credentials defaults, theme, initial query, shortcuts, focus restoration, lifecycle callbacks, and `DocSearchRef` |
| `DocSearchButton` | None | Button ref, theme, shortcuts, and opening keyword search |
| `DocSearchModal` | At least one `indices` entry or deprecated `indexName`; `appId` and `apiKey` must be set here or on `DocSearch` | Open state, close action, initial scroll position, initial query, theme, and shortcuts |
| `DocSearchAskAiModal` | The keyword modal configuration plus `askAi` | Keyword modal behavior, Ask AI state, Ask AI transitions, and hybrid detection |
| `useDocSearch` | A parent `DocSearch` provider | Reads the context and throws when used outside the provider |
`DocSearchButton` accepts native React button props and `translations`. The connected wrapper doesn't accept `theme` or `keyboardShortcuts`; set those on `DocSearch`.
Both connected modal wrappers accept the corresponding low-level modal options, except the state and lifecycle fields supplied by the provider. Explicit `appId` and `apiKey` props override their respective provider values. Consult the [modal API](/docs/packages/modal/api) before adding options.