# Identifying users
**Category**: agents
**URL**: https://heroui.pro/docs/agents/identifying-users
> Connect Agent conversations to the people in your product, and keep their history when they sign in.
Every browser that opens the Agent gets an anonymous identity. When you tell HeroUI who the person is, their earlier anonymous conversations move with them, so history survives login.
Identity is always minted on your server. The browser never sees your API key, and HeroUI never stores the raw id you send — it is hashed into a pseudonymous key before it reaches the runtime or monitoring.
## Anonymous by default
The SDK creates a per-agent id, stores it in `localStorage`, and passes it to your `getAuthToken` callback. Minting a token with that id is all an unauthenticated visitor needs:
```ts title="app/api/heroui-agent/auth-token/route.ts"
await createAuthToken({
apiKey: process.env.HEROUI_AGENT_API_KEY!,
identity: {id: anonymousId, type: "anonymous"},
agentId,
});
```
## Identify signed-in users
Read your own session inside the token route and mint an identified token instead. Pass the browser's `anonymousId` alongside it so conversations started before login are merged into the identified person:
```ts title="app/api/heroui-agent/auth-token/route.ts"
import {createAuthToken} from "@heroui/agent/server";
import {getSession} from "@/lib/session";
export async function POST(request: Request) {
const {anonymousId, agentId} = await request.json();
if (agentId !== process.env.HEROUI_AGENT_ID) {
return Response.json({error: "Invalid agent"}, {status: 400});
}
const session = await getSession();
return Response.json(
await createAuthToken({
anonymousId,
apiKey: process.env.HEROUI_AGENT_API_KEY!,
identity: session
? {id: session.user.id, type: "user"}
: {id: anonymousId, type: "anonymous"},
profile: session ? {email: session.user.email, name: session.user.name} : undefined,
agentId,
}),
{headers: {"Cache-Control": "no-store"}},
);
}
```
Use the same stable id you use elsewhere in your systems, such as a database id. Avoid values that
change between sessions, and never send a password, token, or other secret.
### Profile metadata
`profile` accepts `name`, `email`, and `avatarUrl`. These are optional and only used to make the person recognizable in Agent monitoring. Send whatever you have on each call; omitted fields keep their previous value.
## Refresh after login
The SDK caches a credential for the length of its lifetime, so a person who signs in mid-session keeps the anonymous token until it expires. Call `refreshAuth()` after login to mint a new one immediately:
```tsx
"use client";
import {useAgent} from "@heroui/agent";
export function LoginButton() {
const agent = useAgent();
const handleLogin = async () => {
await signIn();
agent.refreshAuth();
};
return ;
}
```
## Reset on logout
Call `shutdown()` when a person signs out. It clears the active credential, the local conversation state, and the browser's anonymous id, so the next person on a shared device starts as a new anonymous visitor:
```tsx
const agent = useAgent();
const handleLogout = async () => {
await signOut();
agent.shutdown();
};
```
Call `shutdown()` on logout even when you do not expect devices to be shared. Without it, the next
visitor inherits the previous person's conversations.
## What merging does
When you identify a browser that already chatted anonymously:
* Its conversations and runs move to the identified person
* The identified person keeps the earliest first-seen timestamp
* Monitoring lists the person as identified, with your `externalId` and profile
Merging only ever moves an anonymous identity into an identified one. Identified users are never merged into each other, and an identity is never split back apart, so send a stable id from the start.
## Rejected identifiers
Placeholder values are rejected with a `400` because they would collapse unrelated visitors into one person. This includes empty strings and, case-insensitively, `anonymous`, `guest`, `id`, `distinct_id`, `distinctid`, `email`, `not_authenticated`, `undefined`, `null`, `none`, `nan`, `true`, `false`, `0`, and `[object Object]`.
# Overview
**Category**: agents
**URL**: https://heroui.pro/docs/agents
> Embed an AI assistant that understands your product, takes action, and generates interfaces from your data.
HeroUI Agent is a hosted agent for any modern website. A small JavaScript or React bridge connects
the tools and data you choose to expose, then the hosted UI turns the results into streaming
metrics, charts, comparisons, tables, and other interactive interfaces.
The hosted runtime handles the model, conversation state, and generative UI. Your client tools continue to run in the user's browser with the user's existing permissions, so they can safely read application state, call your authenticated APIs, and perform actions.
HeroUI Agent is currently available as an invite-only beta. [Request beta
access](https://heroui.pro/agents) to receive an agent ID.
## What you get
* **Framework-independent embed** — use the hosted JavaScript loader on any website, or the React bridge in React and Next.js applications
* **Generative UI** — render validated charts, tables, metrics, comparisons, and records from tool results
* **Client tools** — connect the agent to your APIs and application state with typed functions
* **Approval controls** — choose which browser actions run automatically and which require confirmation
* **Hosted capabilities** — optionally enable web search, model selection, voice dictation, and file attachments
* **Custom appearance** — configure the launcher, layout, theme, typography, and start screen
## Requirements
* A backend or serverless endpoint that exchanges your server-only API key for browser credentials
* Chrome or Edge 120+, Firefox 121+, or Safari 17.2+
React 19 and Next.js 15 are required only when you choose their corresponding integration. The
hosted JavaScript loader has no framework dependency.
## Where things are configured
An agent is set up in two places. The dashboard holds what the hosted runtime needs to reason and
render — instructions, documents, server-side tools, appearance, and generated UI — and applies
changes without a customer deploy. Your website exposes browser tools, page context, and the token
exchange.
## Explore
## Get started
1. [Install HeroUI Agent](https://heroui.pro/docs/agents/installation) and add your Agent credentials
2. [Follow the quickstart](https://heroui.pro/docs/agents/quickstart) to mount a working embed
3. [Configure appearance](https://heroui.pro/docs/agents/configure/appearance) to match your product
## Keep exploring
* [Explore the live examples](https://heroui.pro/docs/agents/examples)
* [Review the API reference](https://heroui.pro/docs/agents/api-reference)
* [Browse Agent UI components](https://heroui.pro/docs/agents/components)
* [Request beta access](https://heroui.pro/agents)
# Installation
**Category**: agents
**URL**: https://heroui.pro/docs/agents/installation
> Install the HeroUI Agent host bridge in React, Next.js, or a vanilla web application.
## Requirements
* Chrome or Edge 120+, Firefox 121+, or Safari 17.2+
React integrations require React and React DOM 19 or newer. The optional vanilla loader has no
framework dependency.
Next.js is not required. If your application uses Next.js, the Agent's Next.js entry point requires
Next.js 15 or newer.
Hosts on Next.js 16 with Sentry (especially `tunnelRoute`) may see `MaxListenersExceededWarning`
on `ServerResponse`. Next itself attaches 6+ `close` listeners per response; the Agent only adds
more requests (auth token, root-layout CSS, widget), so the warning shows up more often. The Agent
does not attach those EventEmitter listeners. Workaround: raise `ServerResponse` max listeners on
the Node process, or wait for a Next.js fix.
HeroUI Agent is currently an invite-only beta. You need a provisioned agent ID and a project API key before the embed can connect to the hosted runtime. [Request beta access](https://heroui.pro/agents) if you do not have them yet.
## Get your Agent credentials
Open [Agents in the Pro dashboard](https://heroui.pro/dashboard/agents), select your Agent, then open
**Settings** in the sidebar and copy the **Agent ID**.
Next, open [API keys](https://heroui.pro/dashboard/agents/api-keys) in the same workspace and create
a key with `auth_tokens:create` permission.
## Install the React bridge
Install `@heroui/agent`. The package is the small customer-page bridge; React and React DOM are
optional package peers used by its React entry points. The conversation UI and its renderer
dependencies load from the hosted Agent iframe.
Skip this section if your website does not use React. The [vanilla loader](#optional-vanilla-loader)
needs no browser package or framework dependency.
{/* prettier-ignore */}
```bash
npm install @heroui/agent@latest
```
```bash
pnpm add @heroui/agent@latest
```
```bash
yarn add @heroui/agent@latest
```
```bash
bun add @heroui/agent@latest
```
Add `zod` to your own dependencies when your tool declarations import it. Tools that use raw JSON
Schema for `parameters` need nothing extra.
## Content Security Policy
Allow the hosted Agent origin in `frame-src` (or `child-src` for older policies):
```http
Content-Security-Policy: frame-src https://agent.heroui.pro
```
Use `https://staging-agent.heroui.pro` for staging. Credentials are never placed in the iframe URL;
the bridge exchanges the browser credential for a short-lived, origin-bound iframe session after a
versioned handshake.
The optional vanilla loader also needs its origin in `script-src`:
```http
Content-Security-Policy: frame-src https://agent.heroui.pro; script-src 'self' https://agent.heroui.pro
```
## Optional vanilla loader
Applications without React can load the revalidated bridge directly from HeroUI. The UI and all
heavy dependencies still run inside the iframe:
```html
```
Vanilla tool parameters use raw JSON Schema. The returned controller supports `show`, `hide`,
`toggle`, `newConversation`, `refreshAuth`, `shutdown`, and `destroy`.
## Add environment variables
Keep the API key and a server-side copy of the Agent ID in your server environment. The embed also
needs the Agent ID in the browser:
{/* prettier-ignore */}
```bash title="Server environment"
HEROUI_AGENT_API_KEY=he_...
HEROUI_AGENT_ID=your_agent_id
```
Put the public Agent ID directly in `HeroUIAgent.mount()` or expose it through your site's
public runtime configuration.
```bash title=".env.local"
HEROUI_AGENT_API_KEY=he_...
HEROUI_AGENT_ID=your_agent_id
NEXT_PUBLIC_HEROUI_AGENT_ID=your_agent_id
```
```bash title=".env"
HEROUI_AGENT_API_KEY=he_...
HEROUI_AGENT_ID=your_agent_id
VITE_HEROUI_AGENT_ID=your_agent_id
```
`HEROUI_AGENT_API_KEY` must stay server-only. The Agent ID is safe to expose; it identifies the
Agent but does not authorize requests.
## Choose an entry point
| Use | Import |
| --------------------------- | ---------------------------- |
| Next.js App Router embed | `@heroui/agent/next` |
| Vite embed | `@heroui/agent` |
| TanStack Start embed | `@heroui/agent` |
| React Router Framework Mode | `@heroui/agent` |
| Vanilla browser integration | `agent.heroui.pro/loader.js` |
| Server-side token exchange | `@heroui/agent/server` |
The React and Next.js entry points expose the same public bridge. The Next.js entry point is
packaged for App Router applications and is the recommended import in Next.js projects. All
integrations can use `@heroui/agent/server` on the server to exchange the API key for short-lived
browser tokens; its React peer dependency is optional when only the server entry point is used.
Both entry points and the server helper use `https://api.heroui.pro` by default.
## Next step
Continue to the [Quickstart](https://heroui.pro/docs/agents/quickstart) to create a token endpoint, mount the embed, and send your first message.
# Quickstart
**Category**: agents
**URL**: https://heroui.pro/docs/agents/quickstart
> Embed HeroUI Agent in any modern website with React, Next.js, or vanilla JavaScript.
## Before you begin
Complete [Installation](https://heroui.pro/docs/agents/installation) first. You should have an Agent ID and project API key
from the Pro dashboard. React integrations also install `@heroui/agent`; vanilla websites load the
browser bridge from `agent.heroui.pro/loader.js`.
The examples below read `HEROUI_AGENT_API_KEY` and `HEROUI_AGENT_ID` on the server. The browser
receives only the Agent ID, which identifies the Agent but does not authorize a request.
## 1. Choose your integration
Every integration needs a server-only token exchange and one browser bridge. Choose the setup that
fits your website:
Each example treats visitors as anonymous. See [Identifying users](https://heroui.pro/docs/agents/identifying-users) to
connect conversations to the people signed in to your product.
### Next.js
Use the `@heroui/agent/next` entry point with an App Router Route Handler.
On Next.js 16 with Sentry (especially `tunnelRoute`), you may see `MaxListenersExceededWarning` on
`ServerResponse`. Next attaches 6+ `close` listeners per response; the Agent only adds more
requests (token exchange, CSS, widget). Raise `ServerResponse` max listeners on the Node process,
or wait for Next.js to fix it — the Agent does not add those listeners.
#### Create the token endpoint
```ts title="app/api/heroui-agent/auth-token/route.ts"
import {createAuthToken} from "@heroui/agent/server";
export async function POST(request: Request) {
const {anonymousId, agentId} = await request.json();
const apiKey = process.env.HEROUI_AGENT_API_KEY;
if (!apiKey || agentId !== process.env.HEROUI_AGENT_ID) {
return Response.json({error: "Invalid agent"}, {status: 400});
}
return Response.json(
await createAuthToken({
apiKey,
identity: {id: anonymousId, type: "anonymous"},
agentId,
}),
{headers: {"Cache-Control": "no-store"}},
);
}
```
#### Add the embed
```tsx title="app/app-agent.tsx"
"use client";
import type {GetAuthToken} from "@heroui/agent";
import {HeroUIAgent} from "@heroui/agent/next";
const getAuthToken: GetAuthToken = async (context) => {
const response = await fetch("/api/heroui-agent/auth-token", {
body: JSON.stringify(context),
headers: {"Content-Type": "application/json"},
method: "POST",
});
if (!response.ok) throw new Error("Agent authentication failed");
return response.json();
};
export function AppAgent() {
return (
);
}
```
Add `` near the end of your root layout.
### Vite
Vite runs your React client, but the API key still belongs in a backend or serverless function.
Mount this Fetch-compatible handler at `POST /api/heroui-agent/auth-token` in your backend:
#### Create the token endpoint
```ts title="server/agent-auth.ts"
import type {GetAuthTokenContext} from "@heroui/agent";
import {createAuthToken} from "@heroui/agent/server";
export async function handleAgentAuth(request: Request) {
const {anonymousId, agentId} = (await request.json()) as GetAuthTokenContext;
const apiKey = process.env.HEROUI_AGENT_API_KEY;
if (!apiKey || agentId !== process.env.HEROUI_AGENT_ID) {
return Response.json({error: "Invalid agent"}, {status: 400});
}
return Response.json(
await createAuthToken({
apiKey,
identity: {id: anonymousId, type: "anonymous"},
agentId,
}),
{headers: {"Cache-Control": "no-store"}},
);
}
```
#### Add the embed
```tsx title="src/app-agent.tsx"
import type {GetAuthToken} from "@heroui/agent";
import {HeroUIAgent} from "@heroui/agent";
const getAuthToken: GetAuthToken = async (context) => {
const response = await fetch("/api/heroui-agent/auth-token", {
body: JSON.stringify(context),
headers: {"Content-Type": "application/json"},
method: "POST",
});
if (!response.ok) throw new Error("Agent authentication failed");
return response.json();
};
export function AppAgent() {
return ;
}
```
Render `` once from your root `App` component.
### TanStack Start
Use a TanStack Start server function for the token exchange. The function stays server-side while
its typed caller can be used directly by the embed.
#### Create the token endpoint
```ts title="src/lib/agent-auth.functions.ts"
import type {GetAuthTokenContext} from "@heroui/agent";
import {createAuthToken} from "@heroui/agent/server";
import {createServerFn} from "@tanstack/react-start";
export const getAgentAuthToken = createServerFn({method: "POST"})
.validator((context: GetAuthTokenContext) => context)
.handler(async ({data}) => {
const apiKey = process.env.HEROUI_AGENT_API_KEY;
const agentId = process.env.HEROUI_AGENT_ID;
if (!apiKey || !agentId || data.agentId !== agentId) {
throw new Error("Invalid agent");
}
return createAuthToken({
apiKey,
identity: {id: data.anonymousId, type: "anonymous"},
agentId,
});
});
```
#### Add the embed
```tsx title="src/app-agent.tsx"
import type {GetAuthToken} from "@heroui/agent";
import {HeroUIAgent} from "@heroui/agent";
import {getAgentAuthToken} from "./lib/agent-auth.functions";
const getAuthToken: GetAuthToken = (context) => getAgentAuthToken({data: context});
export function AppAgent() {
return ;
}
```
Render `` once from your root route.
### React Router
In React Router Framework Mode, expose the token exchange as a resource route with an `action`.
#### Register the resource route
```ts title="app/routes.ts"
import {type RouteConfig, route} from "@react-router/dev/routes";
export default [
route("api/heroui-agent/auth-token", "./routes/api.heroui-agent.auth-token.ts"),
// Keep your existing application routes here.
] satisfies RouteConfig;
```
#### Create the token endpoint
```ts title="app/routes/api.heroui-agent.auth-token.ts"
import type {Route} from "./+types/api.heroui-agent.auth-token";
import {createAuthToken} from "@heroui/agent/server";
export async function action({request}: Route.ActionArgs) {
const {anonymousId, agentId} = await request.json();
const apiKey = process.env.HEROUI_AGENT_API_KEY;
if (!apiKey || agentId !== process.env.HEROUI_AGENT_ID) {
return Response.json({error: "Invalid agent"}, {status: 400});
}
return Response.json(
await createAuthToken({
apiKey,
identity: {id: anonymousId, type: "anonymous"},
agentId,
}),
{headers: {"Cache-Control": "no-store"}},
);
}
```
#### Add the embed
```tsx title="app/app-agent.tsx"
import type {GetAuthToken} from "@heroui/agent";
import {HeroUIAgent} from "@heroui/agent";
const getAuthToken: GetAuthToken = async (context) => {
const response = await fetch("/api/heroui-agent/auth-token", {
body: JSON.stringify(context),
headers: {"Content-Type": "application/json"},
method: "POST",
});
if (!response.ok) throw new Error("Agent authentication failed");
return response.json();
};
export function AppAgent() {
return ;
}
```
Render `` once from your root route component.
### Vanilla JavaScript
Vanilla websites load the small browser bridge from HeroUI. React is not required. The token
exchange still runs on your backend or serverless platform so the project API key never reaches the
browser.
#### Create the token endpoint
```ts title="server/agent-auth.ts"
import {createAuthToken} from "@heroui/agent/server";
const AGENT_ID = process.env.HEROUI_AGENT_ID!;
// Mount this handler at POST /api/heroui-agent/auth-token.
export async function handleAgentAuth(request: Request) {
const {anonymousId, agentId} = await request.json();
if (typeof anonymousId !== "string" || agentId !== AGENT_ID) {
return Response.json({error: "Invalid agent"}, {status: 400});
}
return Response.json(
await createAuthToken({
anonymousId,
apiKey: process.env.HEROUI_AGENT_API_KEY!,
identity: {id: anonymousId, type: "anonymous"},
agentId: AGENT_ID,
}),
{headers: {"Cache-Control": "no-store"}},
);
}
```
#### Add the embed
```html title="index.html"
```
The returned controller exposes `show`, `hide`, `toggle`, `newConversation`, `refreshAuth`,
`shutdown`, and `destroy`. Vanilla client tools use raw JSON Schema for `parameters`; see
[Client Tools](https://heroui.pro/docs/agents/api-reference/client-tools).
Never expose your API key to the browser. Keep it in a server-only environment variable and mint
short-lived tokens through your own endpoint or server function.
## 2. Verify the integration
Start your application in a supported browser, open the Agent launcher, and send a message. If your
server endpoint can mint a token for the Agent, the conversation connects to the hosted runtime.
That is the whole integration. The embed applies the appearance saved for your project, so it picks
up your launcher icon, colors, typography, and greeting from the
[dashboard](https://heroui.pro/docs/agents/configure/appearance) without any style props. Pass props for anything you
would rather pin in code; they take precedence.
## How it loads
The hosted Agent ships in tiers so websites download only what the current experience needs:
* The **host bridge** creates the iframe and manages layout, focus, page margins, and browser tools. It contains no heavy UI dependencies.
* The **hosted panel shell** provides the launcher, greeting, suggested prompts, and composer inside the iframe.
* The **chat engine** loads on the first sign of intent and otherwise while the browser is idle. Drafts and queued messages stay in the customer page across iframe reloads.
* **Generated UI, charts, maps, and syntax highlighting** load on first use, so conversations that never produce a chart never pay for one.
This is automatic. React integrations do not need to lazy-load `HeroUIAgent`, and vanilla websites
can load the CDN bridge with `defer` as shown above.
## Next steps
[Configure appearance](https://heroui.pro/docs/agents/configure/appearance) to match the embed to your product, then
[identify your users](https://heroui.pro/docs/agents/identifying-users) so conversations follow them across sessions and
devices.
# What Is Generative UI?
**Category**: agents
**URL**: https://heroui.pro/docs/agents/what-is-generative-ui
> Learn how generative UI turns AI responses into useful, interactive interfaces and how HeroUI Agent renders them safely in React.
Generative UI, short for generative user interface, is an interface assembled at runtime by an AI model for the task in front of the user. The model does not stop at writing an answer. It decides whether that answer is easier to understand as a chart, table, comparison, form, approval step, or a combination of components.
That distinction matters. Ask a normal chatbot, “Which products lost the most revenue this quarter?” and you get a paragraph. Ask a product with generative UI and you can get KPIs, a ranked table, a trend chart, and an action to inspect the underlying orders in one response.
## Generative UI vs. traditional UI
Traditional software starts with fixed screens. Designers and developers decide which information appears, where it goes, and how people interact with it. AI chat changed the content inside the screen, but the response was still mostly text.
Generative UI makes the interface part of the response.
| A user asks for… | A text response gives them… | Generative UI can give them… |
| -------------------------------------- | --------------------------- | -------------------------------------------------------- |
| Sales performance by region | A written summary | Metrics, a bar chart, and a sortable table |
| A comparison between several plans | A list of differences | A side-by-side comparison with the tradeoffs highlighted |
| Help updating an account configuration | Step-by-step instructions | A prefilled form and an approval step |
| The cause of a failed workflow | An explanation | A timeline, error details, and the relevant next action |
The goal is not to make every response visual. It is to choose the format that removes the most work for the user.
## It is not the same as a generated website
Generative UI does not require an AI model to write arbitrary HTML, CSS, and JavaScript. That is one approach, but it gives the model control over the same things your product team normally owns: accessibility, security, layout, responsive behavior, and visual consistency.
Production systems usually give the model a smaller vocabulary. The AI can choose and compose approved components, while the application keeps control of their implementation. A chart is still your chart. A button still follows your interaction rules. The generated part is the structure and data, not an unchecked bundle of code.
## Three approaches to generative UI
| Approach | How it works | Best fit |
| --------------- | -------------------------------------------------------------- | -------------------------------------------- |
| **Static** | The model selects one of a few hand-built response components. | Narrow workflows where predictability wins |
| **Declarative** | The model composes approved components through a typed schema. | Production agents and data-rich applications |
| **Open-ended** | The model generates complete markup or application code. | Experiments and highly bespoke interfaces |
HeroUI Agent takes the declarative approach. It gives the model enough freedom to choose the right interface without giving up the component contracts, permissions, and design system that make the rest of your product reliable.
## How generative UI works
Most generative UI systems follow the same path:
1. **Understand the request.** The model identifies the user's intent and the information needed to answer it.
2. **Use tools to get real data.** Tools search documents, query services, or call the APIs exposed by the application.
3. **Choose an interface.** The model maps the result to components that fit the data and the task.
4. **Stream and validate the result.** The client validates each payload and renders the interface as it arrives.
5. **Keep the workflow moving.** Buttons, forms, follow-up prompts, and approvals let the user act on the result instead of starting over.
This is why generative UI and agents fit together. The agent handles intent and tools; the interface makes the result understandable and actionable.
## What good generative UI needs
A useful generated interface should feel like part of the product, not content pasted into it. That requires a few guardrails:
* **A constrained component library.** The model can only render components the application knows how to support.
* **Validated data.** Generated payloads are checked before they reach the screen.
* **Real permissions.** Tools run with the access rules of the signed-in user, not the assumptions of the model.
* **Consistent design.** Typography, color, spacing, and interaction states come from the product's design system.
* **Clear approvals.** Sensitive or irreversible actions stop for confirmation.
* **A text fallback.** When structured UI is unnecessary or incomplete, the answer should still make sense.
Keep core navigation, authentication, and critical settings conventional. Generative UI is strongest where the right presentation depends on the request or the data. It is not the right fit when consistency is the task.
## Generative UI in HeroUI Agent
HeroUI Agent turns tool results into validated [Agent UI components](https://heroui.pro/docs/agents/components). The hosted runtime chooses the presentation, then the embed streams metrics, charts, tables, comparisons, records, forms, and actions using your theme.
[Client tools](https://heroui.pro/docs/agents/api-reference/client-tools) continue to run in the user's browser with the user's existing session and permissions. Generated actions remain declarative, so your application decides what each action is allowed to do.
## Further reading
* [Google Research: Generative UI for any prompt](https://research.google/blog/generative-ui-a-rich-custom-visual-interactive-user-experience-for-any-prompt/)
# Actions
**Category**: agents
**URL**: https://heroui.pro/docs/agents/components/actions
> Generated actions and follow-up prompts that connect Agent UI to host behavior.
Browse the action components the hosted Agent can generate. Actions remain declarative so the host application controls what happens next.
## Action Group
Present declared actions with clear hierarchy and predictable runtime behavior.
### Buttons
```ts
import type {ActionGroupComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "actions",
"title": "Review recommendation",
"actions": [
{
"id": "approve",
"label": "Approve",
"variant": "primary"
},
{
"id": "edit",
"label": "Edit"
},
{
"id": "dismiss",
"label": "Dismiss",
"variant": "outline"
}
],
"kind": "action-group"
} satisfies ActionGroupComponent;
```
## Follow-up
Suggest useful prompts that continue the current conversation.
### Followups
```ts
import type {FollowupComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "followups",
"title": "Continue exploring",
"kind": "followup",
"prompts": [
{
"id": "accounts",
"label": "Which accounts are at risk?",
"prompt": "Show accounts at risk"
},
{
"id": "forecast",
"label": "Explain the forecast",
"prompt": "Explain the forecast methodology"
},
{
"id": "actions",
"label": "Recommend next actions",
"prompt": "Recommend next actions"
}
]
} satisfies FollowupComponent;
```
## Button
Trigger a declared prompt or approved client tool from within a composed interface.
```ts
import type {RowComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "button-variants",
"children": [
{
"id": "primary-button",
"icon": "document",
"kind": "button",
"label": "Primary",
"prompt": "Show me the full quarterly report",
"variant": "primary"
},
{
"id": "secondary-button",
"icon": "document",
"kind": "button",
"label": "Secondary",
"prompt": "Show me the full quarterly report",
"variant": "secondary"
},
{
"id": "tertiary-button",
"icon": "document",
"kind": "button",
"label": "Tertiary",
"prompt": "Show me the full quarterly report",
"variant": "tertiary"
},
{
"id": "outline-button",
"icon": "document",
"kind": "button",
"label": "Outline",
"prompt": "Show me the full quarterly report",
"variant": "outline"
}
],
"gap": "sm",
"justify": "center",
"kind": "row"
} satisfies RowComponent;
```
# Custom Components
**Category**: agents
**URL**: https://heroui.pro/docs/agents/components/custom-components
> Bring your own React components into hosted Agent conversations. Coming soon.
Custom components are not available in the hosted Agent yet. This page explains what you can build
today and what is planned.
HeroUI Agent renders its conversation UI in a hosted, cross-origin iframe. This lets HeroUI ship new
components, charts, file renderers, and fixes directly through the hosted runtime.
Because React components cannot cross the iframe boundary, client tools do not accept a custom
`render` function and Markdown does not accept React renderers. A dedicated custom component API
for the hosted Agent is in progress. Until it ships, use the hosted component catalog for
presentation and client tools for customer-product actions.
## What you can build today
### Hosted catalog components
Every generated component comes from the validated hosted catalog. Client tools return structured
data, and the Agent selects, validates, and renders a compatible component with the embed's theme.
See [Data Visualization](https://heroui.pro/docs/agents/components/data-visualization),
[Display Information](https://heroui.pro/docs/agents/components/display-information),
[Form Elements](https://heroui.pro/docs/agents/components/form-elements), and [Actions](https://heroui.pro/docs/agents/components/actions).
### Interactive actions with client tools
Generated component actions use the same client-tool bridge as model-initiated calls. Declare a
tool in the customer page and the hosted Agent renders approval, progress, success, and error
states while its `execute` function runs with the customer's authenticated product context.
```tsx title="product-tools.tsx"
import {HeroUIAgent, createToolHelper} from "@heroui/agent";
import {z} from "zod";
type ProductContext = {
products: {
create: (input: {name: string}) => Promise<{id: string; name: string}>;
open: (id: string) => void;
};
};
const tool = createToolHelper();
const tools = [
tool({
name: "add_product",
displayName: "Add product",
description: "Create a product in the current workspace",
icon: "add",
needsApproval: true,
parameters: z.object({name: z.string().min(1)}),
execute: ({name}, context) => context.products.create({name}),
}),
tool({
name: "view_product",
displayName: "View product",
description: "Open a product in the customer application",
icon: "view",
parameters: z.object({id: z.string()}),
execute: ({id}, context) => {
context.products.open(id);
return {opened: true};
},
}),
];
;
```
Only the tool manifest and JSON-safe page context enter the iframe. Tool functions, API clients,
routers, state setters, and the rest of `context` remain in the customer page.
## What is planned
The custom component API will let you attach your own UI to a client tool's states, so a
domain-specific view can replace the default tool card while the rest of the conversation keeps
the hosted catalog. Your component code stays in your application and ships on your own release
schedule; the hosted Agent keeps control of the conversation chrome, theming, and security
boundary.
Details of the API are not final. If you have a use case that depends on it, tell us through the
dashboard so it can shape the design.
## Requesting a new hosted visual
If a domain-specific view would be useful to many integrations, it can be added to the hosted
catalog and its validated data contract. Once deployed, it becomes available on the next reload.
For action and approval details, see [Client Tools](https://heroui.pro/docs/agents/api-reference/client-tools). For the
available hosted components, see [Display Information](https://heroui.pro/docs/agents/components/display-information).
# Data Visualization
**Category**: agents
**URL**: https://heroui.pro/docs/agents/components/data-visualization
> Charts, metrics, tables, comparisons, meters, and dashboards for analytical results.
Browse every data visualization component and distinct Storybook variant available to HeroUI Agent, including mobile and empty states. Every preview renders the real `agent-ui` implementation; schema-backed examples expose their exact payload in the Contract tab.
## Dashboard
Compose several generated components into chart-table, grid, metrics-chart, or stacked layouts.
### Dashboard
```ts
import type {DashboardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Headline performance with its supporting trend.",
"id": "business-dashboard",
"title": "Quarterly overview",
"children": [
{
"id": "dashboard-metrics",
"title": "Business health",
"kind": "metric-grid",
"metrics": [
{
"change": 12.4,
"label": "Revenue",
"value": 842000
},
{
"change": 8.7,
"label": "Active users",
"value": 18420
}
]
},
{
"description": "Revenue compared with the monthly target.",
"id": "dashboard-chart",
"title": "Revenue trend",
"data": [
{
"month": "Jan",
"revenue": 48000,
"target": 42000
},
{
"month": "Feb",
"revenue": 56000,
"target": 49000
},
{
"month": "Mar",
"revenue": 52000,
"target": 54000
},
{
"month": "Apr",
"revenue": 68000,
"target": 59000
},
{
"month": "May",
"revenue": 74000,
"target": 65000
},
{
"month": "Jun",
"revenue": 82000,
"target": 72000
}
],
"series": [
{
"dataKey": "revenue",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Revenue"
},
{
"dataKey": "target",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Target"
}
],
"xKey": "month",
"kind": "area-chart"
}
],
"kind": "dashboard",
"layout": "metrics-chart"
} satisfies DashboardComponent;
```
### Chart And Table Dashboard
```ts
import type {DashboardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "chart-table-dashboard",
"title": "Revenue detail",
"children": [
{
"description": "Revenue compared with the monthly target.",
"id": "dashboard-ranking-chart",
"title": "Revenue trend",
"data": [
{
"month": "Jan",
"revenue": 48000,
"target": 42000
},
{
"month": "Feb",
"revenue": 56000,
"target": 49000
},
{
"month": "Mar",
"revenue": 52000,
"target": 54000
},
{
"month": "Apr",
"revenue": 68000,
"target": 59000
},
{
"month": "May",
"revenue": 74000,
"target": 65000
},
{
"month": "Jun",
"revenue": 82000,
"target": 72000
}
],
"series": [
{
"dataKey": "revenue",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Revenue"
},
{
"dataKey": "target",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Target"
}
],
"xKey": "month",
"kind": "bar-chart"
},
{
"id": "dashboard-table",
"title": "Monthly detail",
"columns": [
{
"key": "month",
"label": "Month"
},
{
"key": "revenue",
"label": "Revenue"
}
],
"kind": "data-table",
"rows": [
{
"month": "Jan",
"revenue": 48000
},
{
"month": "Feb",
"revenue": 56000
},
{
"month": "Mar",
"revenue": 52000
},
{
"month": "Apr",
"revenue": 68000
},
{
"month": "May",
"revenue": 74000
},
{
"month": "Jun",
"revenue": 82000
}
],
"variant": "secondary"
}
],
"kind": "dashboard",
"layout": "chart-table"
} satisfies DashboardComponent;
```
### Composed Interface
```ts
import type {ColComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "composed-root",
"children": [
{
"id": "composed-metrics",
"title": "Business health",
"kind": "metric-grid",
"metrics": [
{
"change": 12.4,
"label": "Revenue",
"value": 842000
},
{
"change": 8.7,
"label": "Active users",
"value": 18420
}
]
},
{
"id": "composed-grid",
"children": [
{
"description": "Revenue compared with the monthly target.",
"id": "composed-chart",
"title": "Revenue trend",
"data": [
{
"month": "Jan",
"revenue": 48000,
"target": 42000
},
{
"month": "Feb",
"revenue": 56000,
"target": 49000
},
{
"month": "Mar",
"revenue": 52000,
"target": 54000
},
{
"month": "Apr",
"revenue": 68000,
"target": 59000
},
{
"month": "May",
"revenue": 74000,
"target": 65000
},
{
"month": "Jun",
"revenue": 82000,
"target": 72000
}
],
"series": [
{
"dataKey": "revenue",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Revenue"
},
{
"dataKey": "target",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Target"
}
],
"xKey": "month",
"kind": "area-chart"
},
{
"id": "composed-signals",
"children": [
{
"id": "composed-callout-growth",
"title": "Growth",
"content": "Revenue is compounding ahead of target for the third straight month.",
"kind": "callout",
"tone": "success"
},
{
"id": "composed-callout-churn",
"title": "Churn watch",
"content": "Enterprise churn ticked up 0.8 points — three renewals need attention.",
"kind": "callout",
"tone": "warning"
},
{
"id": "composed-callout-pipeline",
"title": "Pipeline",
"content": "Expansion pipeline added $210K in qualified opportunities this week.",
"kind": "callout",
"tone": "accent"
}
],
"gap": "sm",
"kind": "col"
}
],
"columns": 2,
"kind": "grid"
},
{
"id": "composed-divider",
"kind": "divider"
},
{
"description": "Exact values behind the trend.",
"id": "composed-detail",
"title": "Revenue detail",
"children": [
{
"id": "composed-table",
"title": "Monthly detail",
"columns": [
{
"key": "month",
"label": "Month"
},
{
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"key": "revenue",
"label": "Revenue"
}
],
"kind": "data-table",
"rows": [
{
"month": "Jan",
"revenue": 48000
},
{
"month": "Feb",
"revenue": 56000
},
{
"month": "Mar",
"revenue": 52000
},
{
"month": "Apr",
"revenue": 68000
},
{
"month": "May",
"revenue": 74000
},
{
"month": "Jun",
"revenue": 82000
}
],
"variant": "secondary"
},
{
"id": "composed-actions",
"title": "Next steps",
"actions": [
{
"id": "export",
"label": "Export report",
"variant": "outline"
},
{
"id": "share",
"label": "Share",
"variant": "primary"
}
],
"kind": "action-group"
}
],
"kind": "card",
"variant": "outline"
}
],
"gap": "md",
"kind": "col"
} satisfies ColComponent;
```
### Narrow Widget
```ts
import type {DashboardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Headline performance with its supporting trend.",
"id": "business-dashboard",
"title": "Quarterly overview",
"children": [
{
"id": "dashboard-metrics",
"title": "Business health",
"kind": "metric-grid",
"metrics": [
{
"change": 12.4,
"label": "Revenue",
"value": 842000
},
{
"change": 8.7,
"label": "Active users",
"value": 18420
}
]
},
{
"description": "Revenue compared with the monthly target.",
"id": "dashboard-chart",
"title": "Revenue trend",
"data": [
{
"month": "Jan",
"revenue": 48000,
"target": 42000
},
{
"month": "Feb",
"revenue": 56000,
"target": 49000
},
{
"month": "Mar",
"revenue": 52000,
"target": 54000
},
{
"month": "Apr",
"revenue": 68000,
"target": 59000
},
{
"month": "May",
"revenue": 74000,
"target": 65000
},
{
"month": "Jun",
"revenue": 82000,
"target": 72000
}
],
"series": [
{
"dataKey": "revenue",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Revenue"
},
{
"dataKey": "target",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Target"
}
],
"xKey": "month",
"kind": "area-chart"
}
],
"kind": "dashboard",
"layout": "metrics-chart"
} satisfies DashboardComponent;
```
## KPI Grid
Pair each headline value with a sparkline of its own history, so the number arrives with the shape behind it. Use this whenever the series is available and reach for the metric grid only when it is not — the trend is never inferred from a single point.
### Default
```ts
import type {KpiGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Each number carries the series it came from.",
"id": "workspace-kpis",
"title": "Revenue overview",
"kind": "kpi-grid",
"metrics": [
{
"change": 12.4,
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"icon": "dollar",
"label": "Monthly revenue",
"trend": [
88000,
90400,
93200,
102000,
109500,
114300,
128450
],
"value": 128450
},
{
"change": 3.1,
"icon": "users",
"label": "Active customers",
"trend": [
2440,
2512,
2578,
2631,
2696,
2762,
2847
],
"value": 2847
},
{
"change": 1.8,
"format": {
"maximumFractionDigits": 1,
"style": "percent"
},
"icon": "activity",
"label": "Net retention",
"trend": [
1.084,
1.091,
1.098,
1.112,
1.125,
1.131,
1.142
],
"value": 1.142
},
{
"change": -0.4,
"color": "danger",
"format": {
"maximumFractionDigits": 1,
"style": "percent"
},
"icon": "percent",
"label": "Churn rate",
"trend": [
0.027,
0.025,
0.026,
0.023,
0.022,
0.021,
0.019
],
"value": 0.019
}
]
} satisfies KpiGridComponent;
```
### Flat series
```ts
import type {KpiGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "flat-kpi",
"title": "Reliability",
"kind": "kpi-grid",
"metrics": [
{
"icon": "check",
"label": "Uptime",
"trend": [
100,
100,
100,
100
],
"value": 100
},
{
"label": "Open incidents",
"trend": [
0,
2,
1,
0,
3,
1
],
"value": 1
}
]
} satisfies KpiGridComponent;
```
### Single metric
```ts
import type {KpiGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "single-kpi",
"title": "Current churn rate",
"kind": "kpi-grid",
"metrics": [
{
"change": -0.6,
"color": "danger",
"format": {
"maximumFractionDigits": 1,
"style": "percent"
},
"icon": "percent",
"label": "Churn rate",
"trend": [
0.131,
0.129,
0.13,
0.127,
0.126,
0.125
],
"value": 0.125
}
]
} satisfies KpiGridComponent;
```
### Wide panel
```ts
import type {KpiGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Each number carries the series it came from.",
"id": "workspace-kpis",
"title": "Revenue overview",
"kind": "kpi-grid",
"metrics": [
{
"change": 12.4,
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"icon": "dollar",
"label": "Monthly revenue",
"trend": [
88000,
90400,
93200,
102000,
109500,
114300,
128450
],
"value": 128450
},
{
"change": 3.1,
"icon": "users",
"label": "Active customers",
"trend": [
2440,
2512,
2578,
2631,
2696,
2762,
2847
],
"value": 2847
},
{
"change": 1.8,
"format": {
"maximumFractionDigits": 1,
"style": "percent"
},
"icon": "activity",
"label": "Net retention",
"trend": [
1.084,
1.091,
1.098,
1.112,
1.125,
1.131,
1.142
],
"value": 1.142
},
{
"change": -0.4,
"color": "danger",
"format": {
"maximumFractionDigits": 1,
"style": "percent"
},
"icon": "percent",
"label": "Churn rate",
"trend": [
0.027,
0.025,
0.026,
0.023,
0.022,
0.021,
0.019
],
"value": 0.019
}
]
} satisfies KpiGridComponent;
```
### Full currency values
```ts
import type {KpiGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "full-currency-kpis",
"title": "Full currency values",
"kind": "kpi-grid",
"metrics": [
{
"change": 0.29,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "dollar",
"label": "Current Price",
"trend": [
58686.55,
61861,
72300,
81055.85,
79547.82
],
"value": 79547.82
},
{
"change": -12.34,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "activity",
"label": "Net Cash Flow",
"trend": [
-800000,
-1000000,
-900000,
-1100000,
-1234567.89
],
"value": -1234567.89
}
]
} satisfies KpiGridComponent;
```
## Metric Grid
Present headline values, semantic icons, and change indicators in a responsive metric grid.
### Metrics
```ts
import type {MetricGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Current performance compared with the prior period.",
"id": "performance-metrics",
"title": "Business health",
"kind": "metric-grid",
"metrics": [
{
"change": 12.4,
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"icon": "dollar",
"label": "Revenue",
"value": 842000
},
{
"change": 4.8,
"format": {
"style": "percent"
},
"icon": "percent",
"label": "Conversion",
"value": 0.184
},
{
"change": -2.1,
"icon": "activity",
"label": "Churn",
"value": 3.2
},
{
"change": 8.7,
"format": {
"compact": true,
"style": "number"
},
"icon": "users",
"label": "Active users",
"value": 18420
}
]
} satisfies MetricGridComponent;
```
### Financial Metrics
```ts
import type {MetricGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "financial-metrics",
"title": "Financial overview",
"kind": "metric-grid",
"metrics": [
{
"change": 0.29,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "dollar",
"label": "Current Price",
"value": 79547.82
},
{
"change": 0.29,
"format": {
"maximumFractionDigits": 2,
"style": "percent"
},
"icon": "percent",
"label": "90-Day Change",
"value": 0.2859
},
{
"change": 0.01,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "receipt",
"label": "Last 7 Days (Aug 2026 MTD)",
"value": 18600
},
{
"change": 0.01,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "activity",
"label": "6-Month Total",
"value": 107508
}
]
} satisfies MetricGridComponent;
```
### Narrow Financial Metrics
```ts
import type {MetricGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "financial-metrics",
"title": "Financial overview",
"kind": "metric-grid",
"metrics": [
{
"change": 0.29,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "dollar",
"label": "Current Price",
"value": 79547.82
},
{
"change": 0.29,
"format": {
"maximumFractionDigits": 2,
"style": "percent"
},
"icon": "percent",
"label": "90-Day Change",
"value": 0.2859
},
{
"change": 0.01,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "receipt",
"label": "Last 7 Days (Aug 2026 MTD)",
"value": 18600
},
{
"change": 0.01,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "activity",
"label": "6-Month Total",
"value": 107508
}
]
} satisfies MetricGridComponent;
```
### Wide Financial Metrics
```ts
import type {MetricGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "financial-metrics",
"title": "Financial overview",
"kind": "metric-grid",
"metrics": [
{
"change": 0.29,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "dollar",
"label": "Current Price",
"value": 79547.82
},
{
"change": 0.29,
"format": {
"maximumFractionDigits": 2,
"style": "percent"
},
"icon": "percent",
"label": "90-Day Change",
"value": 0.2859
},
{
"change": 0.01,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "receipt",
"label": "Last 7 Days (Aug 2026 MTD)",
"value": 18600
},
{
"change": 0.01,
"format": {
"currency": "USD",
"style": "currency"
},
"icon": "activity",
"label": "6-Month Total",
"value": 107508
}
]
} satisfies MetricGridComponent;
```
### Route Metrics
```ts
import type {MetricGridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Three central highlights in sequence; walking route geometry is an approximation.",
"id": "buenos-aires-route-metrics",
"title": "Buenos Aires highlights walk",
"kind": "metric-grid",
"metrics": [
{
"icon": "route",
"label": "Walking distance",
"value": 4.68
},
{
"icon": "clock",
"label": "Estimated walking time",
"value": 59
},
{
"icon": "flag",
"label": "Stops",
"value": 3
}
]
} satisfies MetricGridComponent;
```
## Line Chart
Plot one or more numeric series across an ordered horizontal dimension.
### Line Chart
```ts
import type {LineChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Balance movement across the selected period.",
"id": "portfolio-balance",
"title": "Portfolio",
"data": [
{
"balance": 4820,
"date": "Jan 14"
},
{
"balance": 4882.59,
"date": "Jan 15"
},
{
"balance": 4940.43,
"date": "Jan 16"
},
{
"balance": 4989.38,
"date": "Jan 17"
},
{
"balance": 5026.43,
"date": "Jan 18"
},
{
"balance": 5050.05,
"date": "Jan 19"
},
{
"balance": 5060.38,
"date": "Jan 20"
},
{
"balance": 5059.17,
"date": "Jan 21"
},
{
"balance": 5049.52,
"date": "Jan 22"
},
{
"balance": 5035.4,
"date": "Jan 23"
},
{
"balance": 5021.12,
"date": "Jan 24"
},
{
"balance": 5010.69,
"date": "Jan 25"
},
{
"balance": 5007.28,
"date": "Jan 26"
},
{
"balance": 5012.76,
"date": "Jan 27"
},
{
"balance": 5027.45,
"date": "Jan 28"
},
{
"balance": 5050.04,
"date": "Jan 29"
},
{
"balance": 5077.8,
"date": "Jan 30"
},
{
"balance": 5106.94,
"date": "Jan 31"
},
{
"balance": 5133.1,
"date": "Feb 1"
},
{
"balance": 5152.01,
"date": "Feb 2"
},
{
"balance": 5160.03,
"date": "Feb 3"
},
{
"balance": 5154.68,
"date": "Feb 4"
},
{
"balance": 5135.02,
"date": "Feb 5"
},
{
"balance": 5101.78,
"date": "Feb 6"
},
{
"balance": 5057.3,
"date": "Feb 7"
},
{
"balance": 5005.23,
"date": "Feb 8"
},
{
"balance": 4950.1,
"date": "Feb 9"
},
{
"balance": 4896.71,
"date": "Feb 10"
},
{
"balance": 4849.54,
"date": "Feb 11"
},
{
"balance": 4812.18,
"date": "Feb 12"
},
{
"balance": 4786.86,
"date": "Feb 13"
},
{
"balance": 4774.24,
"date": "Feb 14"
},
{
"balance": 4773.29,
"date": "Feb 15"
},
{
"balance": 4781.53,
"date": "Feb 16"
},
{
"balance": 4795.37,
"date": "Feb 17"
},
{
"balance": 4810.65,
"date": "Feb 18"
},
{
"balance": 4823.22,
"date": "Feb 19"
},
{
"balance": 4829.57,
"date": "Feb 20"
},
{
"balance": 4827.28,
"date": "Feb 21"
},
{
"balance": 4815.42,
"date": "Feb 22"
},
{
"balance": 4794.68,
"date": "Feb 23"
},
{
"balance": 4767.28,
"date": "Feb 24"
},
{
"balance": 4736.71,
"date": "Feb 25"
},
{
"balance": 4707.25,
"date": "Feb 26"
},
{
"balance": 4683.38,
"date": "Feb 27"
},
{
"balance": 4669.2,
"date": "Feb 28"
},
{
"balance": 4667.84,
"date": "Mar 1"
},
{
"balance": 4681.07,
"date": "Mar 2"
},
{
"balance": 4708.98,
"date": "Mar 3"
},
{
"balance": 4750.01,
"date": "Mar 4"
},
{
"balance": 4801.12,
"date": "Mar 5"
},
{
"balance": 4858.17,
"date": "Mar 6"
},
{
"balance": 4916.45,
"date": "Mar 7"
},
{
"balance": 4971.3,
"date": "Mar 8"
},
{
"balance": 5018.71,
"date": "Mar 9"
},
{
"balance": 5055.79,
"date": "Mar 10"
},
{
"balance": 5081.2,
"date": "Mar 11"
},
{
"balance": 5095.2,
"date": "Mar 12"
},
{
"balance": 5099.64,
"date": "Mar 13"
},
{
"balance": 5097.66,
"date": "Mar 14"
},
{
"balance": 5093.18,
"date": "Mar 15"
},
{
"balance": 5090.4,
"date": "Mar 16"
},
{
"balance": 5093.11,
"date": "Mar 17"
},
{
"balance": 5104.22,
"date": "Mar 18"
},
{
"balance": 5125.27,
"date": "Mar 19"
},
{
"balance": 5156.22,
"date": "Mar 20"
},
{
"balance": 5195.43,
"date": "Mar 21"
},
{
"balance": 5239.82,
"date": "Mar 22"
},
{
"balance": 5285.31,
"date": "Mar 23"
},
{
"balance": 5327.33,
"date": "Mar 24"
},
{
"balance": 5361.44,
"date": "Mar 25"
},
{
"balance": 5383.89,
"date": "Mar 26"
},
{
"balance": 5392.18,
"date": "Mar 27"
},
{
"balance": 5385.35,
"date": "Mar 28"
},
{
"balance": 5364.12,
"date": "Mar 29"
},
{
"balance": 5330.83,
"date": "Mar 30"
},
{
"balance": 5289.09,
"date": "Mar 31"
},
{
"balance": 5243.36,
"date": "Apr 1"
},
{
"balance": 5198.32,
"date": "Apr 2"
},
{
"balance": 5158.28,
"date": "Apr 3"
},
{
"balance": 5126.63,
"date": "Apr 4"
},
{
"balance": 5105.39,
"date": "Apr 5"
},
{
"balance": 5094.98,
"date": "Apr 6"
},
{
"balance": 5094.23,
"date": "Apr 7"
},
{
"balance": 5100.51,
"date": "Apr 8"
},
{
"balance": 5110.18,
"date": "Apr 9"
},
{
"balance": 5119.11,
"date": "Apr 10"
},
{
"balance": 5123.27,
"date": "Apr 11"
},
{
"balance": 5119.33,
"date": "Apr 12"
},
{
"balance": 5105.14,
"date": "Apr 13"
},
{
"balance": 5080.08,
"date": "Apr 14"
},
{
"balance": 5045.15,
"date": "Apr 15"
},
{
"balance": 5002.9,
"date": "Apr 16"
},
{
"balance": 4957.09,
"date": "Apr 17"
},
{
"balance": 4912.23,
"date": "Apr 18"
},
{
"balance": 4873.01,
"date": "Apr 19"
},
{
"balance": 4843.63,
"date": "Apr 20"
},
{
"balance": 4827.31,
"date": "Apr 21"
},
{
"balance": 4825.83,
"date": "Apr 22"
},
{
"balance": 4839.33,
"date": "Apr 23"
},
{
"balance": 4866.27,
"date": "Apr 24"
},
{
"balance": 4903.66,
"date": "Apr 25"
},
{
"balance": 4947.43,
"date": "Apr 26"
},
{
"balance": 4993.04,
"date": "Apr 27"
},
{
"balance": 5036.02,
"date": "Apr 28"
},
{
"balance": 5072.59,
"date": "Apr 29"
},
{
"balance": 5100.15,
"date": "Apr 30"
},
{
"balance": 5117.6,
"date": "May 1"
},
{
"balance": 5125.48,
"date": "May 2"
},
{
"balance": 5125.86,
"date": "May 3"
},
{
"balance": 5122.01,
"date": "May 4"
},
{
"balance": 5117.93,
"date": "May 5"
},
{
"balance": 5117.81,
"date": "May 6"
},
{
"balance": 5125.33,
"date": "May 7"
},
{
"balance": 5143.24,
"date": "May 8"
},
{
"balance": 5172.83,
"date": "May 9"
},
{
"balance": 5213.81,
"date": "May 10"
},
{
"balance": 5264.25,
"date": "May 11"
},
{
"balance": 5320.82,
"date": "May 12"
},
{
"balance": 5379.21,
"date": "May 13"
},
{
"balance": 5434.68,
"date": "May 14"
},
{
"balance": 5482.65,
"date": "May 15"
},
{
"balance": 5519.32,
"date": "May 16"
},
{
"balance": 5542.12,
"date": "May 17"
},
{
"balance": 5550.08,
"date": "May 18"
},
{
"balance": 5543.91,
"date": "May 19"
},
{
"balance": 5525.88,
"date": "May 20"
},
{
"balance": 5499.53,
"date": "May 21"
},
{
"balance": 5469.16,
"date": "May 22"
},
{
"balance": 5439.25,
"date": "May 23"
},
{
"balance": 5413.87,
"date": "May 24"
},
{
"balance": 5396.09,
"date": "May 25"
},
{
"balance": 5387.63,
"date": "May 26"
},
{
"balance": 5388.58,
"date": "May 27"
},
{
"balance": 5397.48,
"date": "May 28"
},
{
"balance": 5411.46,
"date": "May 29"
},
{
"balance": 5426.71,
"date": "May 30"
},
{
"balance": 5439.03,
"date": "May 31"
},
{
"balance": 5444.38,
"date": "Jun 1"
},
{
"balance": 5439.51,
"date": "Jun 2"
},
{
"balance": 5422.43,
"date": "Jun 3"
},
{
"balance": 5392.69,
"date": "Jun 4"
},
{
"balance": 5351.52,
"date": "Jun 5"
},
{
"balance": 5301.68,
"date": "Jun 6"
},
{
"balance": 5247.13,
"date": "Jun 7"
},
{
"balance": 5192.54,
"date": "Jun 8"
},
{
"balance": 5142.67,
"date": "Jun 9"
},
{
"balance": 5101.81,
"date": "Jun 10"
},
{
"balance": 5073.19,
"date": "Jun 11"
},
{
"balance": 5058.6,
"date": "Jun 12"
},
{
"balance": 5058.18,
"date": "Jun 13"
},
{
"balance": 5070.4,
"date": "Jun 14"
},
{
"balance": 5092.34,
"date": "Jun 15"
},
{
"balance": 5120.06,
"date": "Jun 16"
},
{
"balance": 5149.19,
"date": "Jun 17"
},
{
"balance": 5175.52,
"date": "Jun 18"
},
{
"balance": 5195.55,
"date": "Jun 19"
},
{
"balance": 5207.05,
"date": "Jun 20"
},
{
"balance": 5209.26,
"date": "Jun 21"
},
{
"balance": 5203.08,
"date": "Jun 22"
},
{
"balance": 5190.87,
"date": "Jun 23"
},
{
"balance": 5176.16,
"date": "Jun 24"
},
{
"balance": 5163.13,
"date": "Jun 25"
},
{
"balance": 5156.05,
"date": "Jun 26"
},
{
"balance": 5158.63,
"date": "Jun 27"
},
{
"balance": 5173.55,
"date": "Jun 28"
},
{
"balance": 5201.99,
"date": "Jun 29"
},
{
"balance": 5243.51,
"date": "Jun 30"
},
{
"balance": 5296.02,
"date": "Jul 1"
},
{
"balance": 5356.06,
"date": "Jul 2"
},
{
"balance": 5419.2,
"date": "Jul 3"
},
{
"balance": 5480.64,
"date": "Jul 4"
},
{
"balance": 5535.76,
"date": "Jul 5"
},
{
"balance": 5580.77,
"date": "Jul 6"
},
{
"balance": 5613.15,
"date": "Jul 7"
},
{
"balance": 5631.96,
"date": "Jul 8"
},
{
"balance": 5637.93,
"date": "Jul 9"
},
{
"balance": 5633.33,
"date": "Jul 10"
},
{
"balance": 5621.61,
"date": "Jul 11"
},
{
"balance": 5606.94,
"date": "Jul 12"
}
],
"defaultRangeId": "1m",
"ranges": [
{
"id": "1w",
"label": "1W",
"maxItems": 7
},
{
"id": "1m",
"label": "1M",
"maxItems": 30
},
{
"id": "3m",
"label": "3M",
"maxItems": 90
},
{
"id": "6m",
"label": "6M",
"maxItems": 180
},
{
"id": "all",
"label": "All"
}
],
"series": [
{
"dataKey": "balance",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Total balance"
}
],
"showSummary": true,
"xKey": "date",
"kind": "line-chart"
} satisfies LineChartComponent;
```
### Multi Series Line Chart
```ts
import type {LineChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Revenue compared with the monthly target.",
"id": "revenue-line",
"title": "Revenue trend",
"data": [
{
"month": "Jan",
"revenue": 48000,
"target": 42000
},
{
"month": "Feb",
"revenue": 56000,
"target": 49000
},
{
"month": "Mar",
"revenue": 52000,
"target": 54000
},
{
"month": "Apr",
"revenue": 68000,
"target": 59000
},
{
"month": "May",
"revenue": 74000,
"target": 65000
},
{
"month": "Jun",
"revenue": 82000,
"target": 72000
}
],
"series": [
{
"dataKey": "revenue",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Revenue"
},
{
"dataKey": "target",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Target"
}
],
"xKey": "month",
"kind": "line-chart"
} satisfies LineChartComponent;
```
### Live Line Chart
```ts
import type {LineChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "The newest eight one-minute observations remain in view.",
"id": "requests-live",
"title": "Live request rate",
"data": [
{
"requests": 128,
"time": "10:02"
},
{
"requests": 142,
"time": "10:03"
},
{
"requests": 136,
"time": "10:04"
},
{
"requests": 151,
"time": "10:05"
},
{
"requests": 165,
"time": "10:06"
},
{
"requests": 159,
"time": "10:07"
},
{
"requests": 173,
"time": "10:08"
},
{
"requests": 184,
"time": "10:09"
},
{
"requests": 177,
"time": "10:10"
},
{
"requests": 196,
"time": "10:11"
},
{
"requests": 205,
"time": "10:12"
},
{
"requests": 198,
"time": "10:13"
}
],
"series": [
{
"color": "accent",
"dataKey": "requests",
"format": {
"compact": true,
"style": "number"
},
"label": "Requests per minute"
}
],
"showSummary": true,
"xKey": "time",
"kind": "line-chart",
"mode": "live",
"windowSize": 8
} satisfies LineChartComponent;
```
### Profit Loss Line Chart
```ts
import type {LineChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Positive and negative periods are split around the zero baseline.",
"id": "trading-profit-loss",
"title": "Trading profit and loss",
"data": [
{
"date": "Jun 2",
"pnl": -3400
},
{
"date": "Jun 3",
"pnl": -1200
},
{
"date": "Jun 4",
"pnl": 800
},
{
"date": "Jun 5",
"pnl": 2700
},
{
"date": "Jun 6",
"pnl": 1900
},
{
"date": "Jun 9",
"pnl": 4200
},
{
"date": "Jun 10",
"pnl": -600
},
{
"date": "Jun 11",
"pnl": -2100
},
{
"date": "Jun 12",
"pnl": 1300
},
{
"date": "Jun 13",
"pnl": 5100
}
],
"series": [
{
"dataKey": "pnl",
"format": {
"compact": false,
"currency": "USD",
"style": "currency"
},
"label": "Cumulative P&L"
}
],
"xKey": "date",
"baseline": 0,
"kind": "line-chart",
"mode": "profit-loss",
"negativeColor": "danger",
"positiveColor": "success"
} satisfies LineChartComponent;
```
### Chart Line Trend
```ts
import type {LineChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Last 10 days",
"id": "widget-line",
"title": "Mobile sessions",
"data": [
{
"date": "Apr 14",
"mobile": 240
},
{
"date": "Apr 15",
"mobile": 310
},
{
"date": "Apr 16",
"mobile": 285
},
{
"date": "Apr 17",
"mobile": 380
},
{
"date": "Apr 18",
"mobile": 420
},
{
"date": "Apr 19",
"mobile": 510
}
],
"series": [
{
"dataKey": "mobile",
"label": "Mobile"
}
],
"xKey": "date",
"kind": "line-chart"
} satisfies LineChartComponent;
```
## Area Chart
Show time-series or categorical trends with an optional stacked area treatment.
### Area Chart
```ts
import type {AreaChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Revenue compared with the monthly target.",
"id": "revenue-area",
"title": "Revenue trend",
"data": [
{
"month": "Jan",
"revenue": 48000,
"target": 42000
},
{
"month": "Feb",
"revenue": 56000,
"target": 49000
},
{
"month": "Mar",
"revenue": 52000,
"target": 54000
},
{
"month": "Apr",
"revenue": 68000,
"target": 59000
},
{
"month": "May",
"revenue": 74000,
"target": 65000
},
{
"month": "Jun",
"revenue": 82000,
"target": 72000
}
],
"defaultRangeId": "all",
"ranges": [
{
"id": "3m",
"label": "3M",
"maxItems": 3
},
{
"id": "6m",
"label": "6M",
"maxItems": 6
},
{
"id": "all",
"label": "MAX"
}
],
"series": [
{
"dataKey": "revenue",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Revenue"
}
],
"showSummary": true,
"xKey": "month",
"kind": "area-chart",
"stacked": false
} satisfies AreaChartComponent;
```
### Dense Series
```ts
import type {AreaChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "dense-series",
"title": "Stacked performance",
"data": [
{
"month": "Jan",
"revenue": 48000,
"target": 42000
},
{
"month": "Feb",
"revenue": 56000,
"target": 49000
},
{
"month": "Mar",
"revenue": 52000,
"target": 54000
},
{
"month": "Apr",
"revenue": 68000,
"target": 59000
},
{
"month": "May",
"revenue": 74000,
"target": 65000
},
{
"month": "Jun",
"revenue": 82000,
"target": 72000
}
],
"series": [
{
"dataKey": "revenue",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Revenue"
},
{
"dataKey": "target",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Target"
}
],
"xKey": "month",
"kind": "area-chart",
"stacked": true
} satisfies AreaChartComponent;
```
### Chart Area Stacked
```ts
import type {AreaChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Monthly traffic split · Jan–Jun",
"id": "widget-area",
"title": "Active users",
"data": [
{
"desktop": 180,
"mobile": 120,
"month": "Jan"
},
{
"desktop": 210,
"mobile": 150,
"month": "Feb"
},
{
"desktop": 240,
"mobile": 190,
"month": "Mar"
},
{
"desktop": 280,
"mobile": 210,
"month": "Apr"
},
{
"desktop": 320,
"mobile": 240,
"month": "May"
},
{
"desktop": 350,
"mobile": 260,
"month": "Jun"
}
],
"series": [
{
"dataKey": "desktop",
"label": "Desktop"
},
{
"dataKey": "mobile",
"label": "Mobile"
}
],
"xKey": "month",
"kind": "area-chart",
"stacked": true
} satisfies AreaChartComponent;
```
## Bar Chart
Compare categories with horizontal or vertical bars and optional stacking.
### Bar Chart
```ts
import type {BarChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Revenue compared with the monthly target.",
"id": "revenue-bars",
"title": "Revenue trend",
"data": [
{
"month": "Jan",
"revenue": 48000,
"target": 42000
},
{
"month": "Feb",
"revenue": 56000,
"target": 49000
},
{
"month": "Mar",
"revenue": 52000,
"target": 54000
},
{
"month": "Apr",
"revenue": 68000,
"target": 59000
},
{
"month": "May",
"revenue": 74000,
"target": 65000
},
{
"month": "Jun",
"revenue": 82000,
"target": 72000
}
],
"series": [
{
"dataKey": "revenue",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Revenue"
},
{
"dataKey": "target",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Target"
}
],
"xKey": "month",
"kind": "bar-chart"
} satisfies BarChartComponent;
```
### Horizontal Bar Chart
```ts
import type {BarChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Full-year 2025 price return versus the S&P 500. Percentage-point outperformance is shown relative to the index's 16.4% price gain.",
"id": "sp500-outperformers",
"title": "Top five S&P 500 stocks of 2025",
"data": [
{
"return": 5.602,
"stock": "Sandisk (SNDK)"
},
{
"return": 2.833,
"stock": "Western Digital (WDC)"
},
{
"return": 2.39,
"stock": "Micron (MU)"
},
{
"return": 2.19,
"stock": "Seagate (STX)"
},
{
"return": 2.03,
"stock": "Robinhood (HOOD)"
},
{
"return": 0.164,
"stock": "S&P 500"
}
],
"series": [
{
"dataKey": "return",
"format": {
"maximumFractionDigits": 1,
"style": "percent"
},
"label": "2025 return"
}
],
"xKey": "stock",
"kind": "bar-chart",
"layout": "horizontal"
} satisfies BarChartComponent;
```
### Chart Stacked Bar
```ts
import type {BarChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Desktop vs mobile · Last 7 days",
"id": "widget-bars",
"title": "Install mix",
"data": [
{
"android": 120,
"date": "Apr 17",
"desktop": 260,
"ios": 90
},
{
"android": 150,
"date": "Apr 18",
"desktop": 280,
"ios": 110
},
{
"android": 130,
"date": "Apr 19",
"desktop": 310,
"ios": 125
},
{
"android": 170,
"date": "Apr 20",
"desktop": 300,
"ios": 140
}
],
"series": [
{
"dataKey": "desktop",
"label": "Desktop"
},
{
"dataKey": "android",
"label": "Android"
},
{
"dataKey": "ios",
"label": "iOS"
}
],
"xKey": "date",
"kind": "bar-chart",
"stacked": true
} satisfies BarChartComponent;
```
## Composed Chart
Combine area, bar, and line series across shared left and right axes.
### Composed Chart
```ts
import type {ComposedChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Monthly revenue with order volume on a secondary axis.",
"id": "revenue-orders-composed",
"title": "Revenue & orders",
"data": [
{
"month": "Jan",
"orders": 320,
"revenue": 42000
},
{
"month": "Feb",
"orders": 450,
"revenue": 58000
},
{
"month": "Mar",
"orders": 380,
"revenue": 49000
},
{
"month": "Apr",
"orders": 520,
"revenue": 72000
},
{
"month": "May",
"orders": 480,
"revenue": 61000
},
{
"month": "Jun",
"orders": 600,
"revenue": 84000
}
],
"series": [
{
"dataKey": "revenue",
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"label": "Revenue",
"type": "bar"
},
{
"dataKey": "orders",
"format": {
"compact": true,
"style": "number"
},
"label": "Orders",
"axis": "right",
"type": "line"
}
],
"xKey": "month",
"kind": "composed-chart"
} satisfies ComposedChartComponent;
```
## Candlestick Chart
Show genuine open, high, low, and close values across an ordered time dimension.
### Candlestick Chart
```ts
import type {CandlestickChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Daily open, high, low, and close prices across eight sessions.",
"id": "daily-price-candles",
"title": "Daily price movement",
"closeKey": "close",
"data": [
{
"close": 187.4,
"date": "Jun 3",
"high": 189.1,
"low": 182.8,
"open": 184.2
},
{
"close": 185.8,
"date": "Jun 4",
"high": 188.5,
"low": 184.6,
"open": 187.4
},
{
"close": 190.2,
"date": "Jun 5",
"high": 191.6,
"low": 185.1,
"open": 185.8
},
{
"close": 193.6,
"date": "Jun 6",
"high": 194.4,
"low": 188.9,
"open": 190.2
},
{
"close": 191.1,
"date": "Jun 9",
"high": 195.2,
"low": 190.4,
"open": 193.6
},
{
"close": 196.8,
"date": "Jun 10",
"high": 197.5,
"low": 190.7,
"open": 191.1
},
{
"close": 194.9,
"date": "Jun 11",
"high": 198.2,
"low": 193.8,
"open": 196.8
},
{
"close": 199.5,
"date": "Jun 12",
"high": 200.1,
"low": 194.1,
"open": 194.9
}
],
"downColor": "danger",
"format": {
"compact": false,
"currency": "USD",
"style": "currency"
},
"highKey": "high",
"kind": "candlestick-chart",
"lowKey": "low",
"openKey": "open",
"upColor": "success",
"xKey": "date"
} satisfies CandlestickChartComponent;
```
## Pie Chart
Show a part-to-whole comparison for a compact set of categories.
### Pie Chart
```ts
import type {PieChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Product contributes nearly half of total revenue.",
"id": "revenue-pie",
"title": "Revenue mix",
"data": [
{
"asset": "Product",
"value": 46
},
{
"asset": "Services",
"value": 28
},
{
"asset": "Partnerships",
"value": 16
},
{
"asset": "Other",
"value": 10
}
],
"labelKey": "asset",
"valueKey": "value",
"kind": "pie-chart"
} satisfies PieChartComponent;
```
### Semantic Color Pie Chart
```ts
import type {PieChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Semantic colors communicate the urgency of each category.",
"id": "ticket-priority-pie",
"title": "All tickets by priority",
"colors": {
"High": "warning",
"Low": "success",
"Medium": "accent",
"Urgent": "danger"
},
"data": [
{
"priority": "Low",
"tickets": 9
},
{
"priority": "Medium",
"tickets": 8
},
{
"priority": "High",
"tickets": 11
},
{
"priority": "Urgent",
"tickets": 12
}
],
"labelKey": "priority",
"valueKey": "tickets",
"kind": "pie-chart"
} satisfies PieChartComponent;
```
## Donut Chart
Display a part-to-whole comparison with a compact central opening.
### Donut Chart
```ts
import type {DonutChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Product contributes nearly half of total revenue.",
"id": "revenue-donut",
"title": "Revenue mix",
"data": [
{
"asset": "Product",
"value": 46
},
{
"asset": "Services",
"value": 28
},
{
"asset": "Partnerships",
"value": 16
},
{
"asset": "Other",
"value": 10
}
],
"labelKey": "asset",
"valueKey": "value",
"kind": "donut-chart"
} satisfies DonutChartComponent;
```
## Funnel Chart
Visualize ordered stage drop-off vertically or horizontally, with optional conversion percentages.
### Vertical Funnel Chart
```ts
import type {FunnelChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Stage-to-stage conversion from captured leads to won opportunities.",
"id": "sales-conversion-funnel",
"title": "Sales conversion",
"colors": {
"Evaluation": "chart-3",
"Leads": "accent",
"Proposal": "chart-4",
"Qualified": "chart-2",
"Won": "success"
},
"data": [
{
"stage": "Leads",
"value": 12800
},
{
"stage": "Qualified",
"value": 6900
},
{
"stage": "Evaluation",
"value": 3100
},
{
"stage": "Proposal",
"value": 1540
},
{
"stage": "Won",
"value": 860
}
],
"format": {
"compact": true,
"style": "number"
},
"labelKey": "stage",
"valueKey": "value",
"kind": "funnel-chart",
"orientation": "vertical",
"showPercentages": true
} satisfies FunnelChartComponent;
```
### Horizontal Funnel Chart
```ts
import type {FunnelChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Pipeline volume narrows from left to right across each stage.",
"id": "sales-pipeline-horizontal-funnel",
"title": "Sales pipeline progression",
"colors": {
"Evaluation": "chart-3",
"Leads": "accent",
"Proposal": "chart-4",
"Qualified": "chart-2",
"Won": "success"
},
"data": [
{
"stage": "Leads",
"value": 12800
},
{
"stage": "Qualified",
"value": 6900
},
{
"stage": "Evaluation",
"value": 3100
},
{
"stage": "Proposal",
"value": 1540
},
{
"stage": "Won",
"value": 860
}
],
"format": {
"compact": true,
"style": "number"
},
"labelKey": "stage",
"valueKey": "value",
"kind": "funnel-chart",
"orientation": "horizontal",
"showPercentages": true
} satisfies FunnelChartComponent;
```
## Radar Chart
Compare several series across a shared set of qualitative dimensions.
### Radar Chart
```ts
import type {RadarChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Current health leads the benchmark in four of five dimensions.",
"id": "health-radar",
"title": "Customer health",
"angleKey": "dimension",
"data": [
{
"benchmark": 72,
"current": 82,
"dimension": "Adoption"
},
{
"benchmark": 68,
"current": 74,
"dimension": "Retention"
},
{
"benchmark": 70,
"current": 64,
"dimension": "Expansion"
},
{
"benchmark": 76,
"current": 88,
"dimension": "Satisfaction"
},
{
"benchmark": 66,
"current": 71,
"dimension": "Efficiency"
}
],
"kind": "radar-chart",
"series": [
{
"dataKey": "current",
"label": "Current"
},
{
"dataKey": "benchmark",
"label": "Benchmark"
}
]
} satisfies RadarChartComponent;
```
## Radial Chart
Show progress or magnitude along a configurable radial range.
### Radial Chart
```ts
import type {RadialChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Activation leads while expansion has the most room to grow.",
"id": "goals-radial",
"title": "Goal completion",
"data": [
{
"label": "Activation",
"value": 84
},
{
"label": "Retention",
"value": 72
},
{
"label": "Expansion",
"value": 61
}
],
"labelKey": "label",
"valueKey": "value",
"kind": "radial-chart",
"maxValue": 100
} satisfies RadialChartComponent;
```
## Gauge Chart
Display one bounded value as an arc or compact linear gauge.
### Arc Gauge Chart
```ts
import type {GaugeChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Current compute load as a share of provisioned capacity.",
"id": "warehouse-utilization-gauge",
"title": "Warehouse utilization",
"color": "warning",
"format": {
"maximumFractionDigits": 0,
"style": "percent"
},
"kind": "gauge-chart",
"label": "Capacity used",
"max": 1,
"min": 0,
"notches": 30,
"orientation": "arc",
"value": 0.73
} satisfies GaugeChartComponent;
```
### Linear Gauge Chart
```ts
import type {GaugeChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Remaining error budget for the current reliability window.",
"id": "error-budget-gauge",
"title": "Monthly reliability budget",
"color": "success",
"format": {
"maximumFractionDigits": 0,
"style": "percent"
},
"kind": "gauge-chart",
"label": "Error budget remaining",
"max": 1,
"min": 0,
"notches": 20,
"orientation": "linear",
"value": 0.42
} satisfies GaugeChartComponent;
```
## Sunburst Chart
Explore hierarchical part-to-whole data through nested proportional rings.
### Sunburst Chart
```ts
import type {SunburstChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Annual operating spend by function, team, and engineering discipline.",
"id": "operating-spend-sunburst",
"title": "Operating spend allocation",
"data": {
"children": [
{
"children": [
{
"children": [
{
"id": "frontend",
"label": "Frontend",
"value": 140000
},
{
"id": "backend",
"label": "Backend",
"value": 180000
},
{
"id": "data-platform",
"label": "Data platform",
"value": 120000
}
],
"id": "engineering",
"label": "Engineering"
},
{
"id": "design",
"label": "Design",
"value": 80000
}
],
"color": "accent",
"id": "product",
"label": "Product"
},
{
"children": [
{
"id": "sales",
"label": "Sales",
"value": 240000
},
{
"id": "marketing",
"label": "Marketing",
"value": 160000
}
],
"color": "chart-3",
"id": "growth",
"label": "Growth"
},
{
"children": [
{
"id": "support",
"label": "Support",
"value": 110000
},
{
"id": "infrastructure",
"label": "Infrastructure",
"value": 190000
}
],
"color": "chart-5",
"id": "operations",
"label": "Operations"
}
],
"id": "company",
"label": "Company"
},
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"kind": "sunburst-chart"
} satisfies SunburstChartComponent;
```
## Sankey Chart
Visualize weighted flow between named nodes in a directed network.
### Sankey Chart
```ts
import type {SankeyChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Half of visitors activate; two-thirds of them convert to paid.",
"id": "journey-sankey",
"title": "Customer journey",
"kind": "sankey-chart",
"links": [
{
"source": "visitors",
"target": "signup",
"value": 720
},
{
"source": "visitors",
"target": "bounce",
"value": 280
},
{
"source": "signup",
"target": "activated",
"value": 510
},
{
"source": "signup",
"target": "inactive",
"value": 210
},
{
"source": "activated",
"target": "paid",
"value": 340
}
],
"nodes": [
{
"id": "visitors",
"label": "Visitors"
},
{
"id": "signup",
"label": "Signups"
},
{
"id": "bounce",
"label": "Bounced"
},
{
"id": "activated",
"label": "Activated"
},
{
"id": "inactive",
"label": "Inactive"
},
{
"id": "paid",
"label": "Paid"
}
]
} satisfies SankeyChartComponent;
```
## Scatter Chart
Plot two numeric dimensions with optional groups, labels, and bubble sizes.
### Scatter Chart
```ts
import type {ScatterChartComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Price and trading volume, sized by market capitalization.",
"id": "market-scatter",
"title": "Market correlation",
"data": [
{
"marketCap": 1200,
"price": 64,
"sector": "Platform",
"symbol": "A",
"volume": 88
},
{
"marketCap": 740,
"price": 42,
"sector": "Platform",
"symbol": "B",
"volume": 64
},
{
"marketCap": 510,
"price": 28,
"sector": "Infrastructure",
"symbol": "C",
"volume": 72
},
{
"marketCap": 330,
"price": 18,
"sector": "Infrastructure",
"symbol": "D",
"volume": 34
}
],
"groupKey": "sector",
"kind": "scatter-chart",
"labelKey": "symbol",
"sizeKey": "marketCap",
"xKey": "price",
"yKey": "volume"
} satisfies ScatterChartComponent;
```
## Heatmap
Encode intensity across two categorical or ordered dimensions.
### Heatmap
```ts
import type {HeatmapComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Request intensity by weekday and hour.",
"id": "request-heatmap",
"title": "Traffic intensity",
"data": [
{
"day": "Mon",
"hour": "09",
"requests": 42
},
{
"day": "Mon",
"hour": "12",
"requests": 81
},
{
"day": "Mon",
"hour": "15",
"requests": 63
},
{
"day": "Tue",
"hour": "09",
"requests": 31
},
{
"day": "Tue",
"hour": "12",
"requests": 95
},
{
"day": "Tue",
"hour": "15",
"requests": 74
},
{
"day": "Wed",
"hour": "09",
"requests": 52
},
{
"day": "Wed",
"hour": "12",
"requests": 68
},
{
"day": "Wed",
"hour": "15",
"requests": 88
}
],
"kind": "heatmap",
"valueKey": "requests",
"xKey": "hour",
"yKey": "day"
} satisfies HeatmapComponent;
```
## Data Table
Render structured rows with typed columns, number formatting, and optional filtering.
### Data Table
```ts
import type {DataTableComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Sort pipeline accounts by stage or value.",
"id": "pipeline-table",
"title": "Sales pipeline",
"columns": [
{
"key": "account",
"label": "Account"
},
{
"key": "status",
"label": "Status"
},
{
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"key": "value",
"label": "Value"
}
],
"kind": "data-table",
"rows": [
{
"account": "Acme",
"status": "Negotiation",
"value": 84000
},
{
"account": "Northstar",
"status": "Qualified",
"value": 62000
},
{
"account": "Linear",
"status": "Proposal",
"value": 49000
},
{
"account": "Evergreen",
"status": "Qualified",
"value": 38000
}
]
} satisfies DataTableComponent;
```
### Secondary Table
```ts
import type {DataTableComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "A flatter table treatment for content that sits directly on the page.",
"id": "pipeline-table-secondary",
"title": "Sales pipeline — secondary",
"columns": [
{
"key": "account",
"label": "Account"
},
{
"key": "status",
"label": "Status"
},
{
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"key": "value",
"label": "Value"
}
],
"kind": "data-table",
"rows": [
{
"account": "Acme",
"status": "Negotiation",
"value": 84000
},
{
"account": "Northstar",
"status": "Qualified",
"value": 62000
},
{
"account": "Linear",
"status": "Proposal",
"value": 49000
},
{
"account": "Evergreen",
"status": "Qualified",
"value": 38000
}
],
"variant": "secondary"
} satisfies DataTableComponent;
```
### Filterable Secondary Table
```ts
import type {DataTableComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Search accounts in the lower-emphasis secondary table treatment.",
"id": "pipeline-table-filterable-secondary",
"title": "Searchable pipeline — secondary",
"columns": [
{
"key": "account",
"label": "Account"
},
{
"key": "status",
"label": "Status"
},
{
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"key": "value",
"label": "Value"
}
],
"filterable": true,
"kind": "data-table",
"rows": [
{
"account": "Acme",
"status": "Negotiation",
"value": 84000
},
{
"account": "Northstar",
"status": "Qualified",
"value": 62000
},
{
"account": "Linear",
"status": "Proposal",
"value": 49000
},
{
"account": "Evergreen",
"status": "Qualified",
"value": 38000
}
],
"variant": "secondary"
} satisfies DataTableComponent;
```
### Financial Ranking Table
```ts
import type {DataTableComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Full-year price return compared with the S&P 500's 16.4% gain.",
"id": "stock-ranking-table",
"title": "Top five S&P 500 stocks of 2025",
"columns": [
{
"key": "rank",
"label": "Rank"
},
{
"key": "stock",
"label": "Stock"
},
{
"format": {
"maximumFractionDigits": 1,
"style": "percent"
},
"key": "return",
"label": "2025 return"
},
{
"key": "outperformance",
"label": "Beat S&P 500 by"
}
],
"kind": "data-table",
"rows": [
{
"outperformance": "543.8 pts",
"rank": 1,
"return": 5.602,
"stock": "Sandisk"
},
{
"outperformance": "266.9 pts",
"rank": 2,
"return": 2.833,
"stock": "Western Digital"
},
{
"outperformance": "222.7 pts",
"rank": 3,
"return": 2.391,
"stock": "Micron"
},
{
"outperformance": "202.5 pts",
"rank": 4,
"return": 2.189,
"stock": "Seagate"
},
{
"outperformance": "186.2 pts",
"rank": 5,
"return": 2.026,
"stock": "Robinhood"
}
]
} satisfies DataTableComponent;
```
### Filterable Catalog Table
```ts
import type {DataTableComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Filter by title, year, franchise, or category.",
"id": "movie-catalog-table",
"title": "Highest-grossing films",
"columns": [
{
"key": "rank",
"label": "Rank"
},
{
"key": "title",
"label": "Title"
},
{
"key": "year",
"label": "Year"
},
{
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"key": "gross",
"label": "Worldwide gross"
},
{
"key": "franchise",
"label": "Franchise"
},
{
"key": "category",
"label": "Category"
}
],
"filterable": true,
"kind": "data-table",
"rows": [
{
"category": "Sci-Fi / Action",
"franchise": "Avatar",
"gross": 2920000000,
"rank": 1,
"title": "Avatar",
"year": "2009"
},
{
"category": "Superhero",
"franchise": "MCU",
"gross": 2800000000,
"rank": 2,
"title": "Avengers: Endgame",
"year": "2019"
},
{
"category": "Sci-Fi / Action",
"franchise": "Avatar",
"gross": 2330000000,
"rank": 3,
"title": "Avatar: The Way of Water",
"year": "2022"
},
{
"category": "Animation",
"franchise": "Chinese Mythology",
"gross": 2270000000,
"rank": 4,
"title": "Ne Zha 2",
"year": "2025"
},
{
"category": "Drama / Romance",
"franchise": "Original",
"gross": 2260000000,
"rank": 5,
"title": "Titanic",
"year": "1997"
},
{
"category": "Sci-Fi / Action",
"franchise": "Star Wars",
"gross": 2070000000,
"rank": 6,
"title": "Star Wars: The Force Awakens",
"year": "2015"
},
{
"category": "Superhero",
"franchise": "MCU",
"gross": 2050000000,
"rank": 7,
"title": "Avengers: Infinity War",
"year": "2018"
},
{
"category": "Superhero",
"franchise": "MCU / Sony",
"gross": 1920000000,
"rank": 8,
"title": "Spider-Man: No Way Home",
"year": "2021"
},
{
"category": "Animation",
"franchise": "Disney",
"gross": 1870000000,
"rank": 9,
"title": "Zootopia 2",
"year": "2025"
},
{
"category": "Animation",
"franchise": "Pixar",
"gross": 1700000000,
"rank": 10,
"title": "Inside Out 2",
"year": "2024"
}
]
} satisfies DataTableComponent;
```
### Market Metrics Table
```ts
import type {DataTableComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "market-metrics-table",
"title": "Crypto market overview",
"columns": [
{
"key": "metric",
"label": "Market metric"
},
{
"key": "value",
"label": "Value"
},
{
"key": "status",
"label": "Trend status"
}
],
"kind": "data-table",
"rows": [
{
"metric": "Total market cap",
"status": "Stable",
"value": "$1.29 trillion"
},
{
"metric": "24h trading volume",
"status": "High activity",
"value": "$15.34 billion"
},
{
"metric": "BTC dominance",
"status": "Increasing",
"value": "58.62%"
}
]
} satisfies DataTableComponent;
```
### Empty Table
```ts
import type {DataTableComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Sort pipeline accounts by stage or value.",
"id": "empty-table",
"title": "No results yet",
"columns": [
{
"key": "account",
"label": "Account"
},
{
"key": "status",
"label": "Status"
},
{
"format": {
"compact": true,
"currency": "USD",
"style": "currency"
},
"key": "value",
"label": "Value"
}
],
"kind": "data-table",
"rows": []
} satisfies DataTableComponent;
```
## Comparison List
Rank or compare labeled values with optional change indicators and explanatory notes.
### Comparison List
```ts
import type {ComparisonListComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "plan-comparison",
"title": "Plans compared",
"items": [
{
"change": 18,
"label": "Enterprise",
"note": "Highest growth",
"value": 124
},
{
"change": 9,
"label": "Business",
"note": "Strong retention",
"value": 96
},
{
"change": -3,
"label": "Starter",
"note": "Seasonal decline",
"value": 72
}
],
"kind": "comparison-list"
} satisfies ComparisonListComponent;
```
## Meter List
Display capacity, progress, or goal values against configurable ranges.
### Default
```ts
import type {MeterListComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Quantities with meaningful lower and upper bounds.",
"id": "capacity",
"title": "Workspace capacity",
"items": [
{
"description": "36 GB of 50 GB used",
"id": "storage",
"label": "Storage",
"max": 50,
"tone": "warning",
"value": 36
},
{
"description": "Monthly automation allowance",
"format": {
"maximumFractionDigits": 0,
"style": "percent"
},
"id": "automations",
"label": "Automations",
"max": 1,
"tone": "accent",
"value": 0.58
},
{
"description": "Within the healthy range",
"format": {
"maximumFractionDigits": 0,
"style": "percent"
},
"id": "availability",
"label": "Availability",
"max": 1,
"tone": "success",
"value": 0.992
}
],
"kind": "meter-list"
} satisfies MeterListComponent;
```
# Display Information
**Category**: agents
**URL**: https://heroui.pro/docs/agents/components/display-information
> Cards, text, media, records, and composed interfaces for generated results.
This page covers the Display Information components the hosted Agent can generate, including cards, fallback states, products, records, composition widgets, and maps. Each example exposes its validated payload in the Contract view.
## Card
Group a title, description, actions, and nested generated content inside a clear surface.
```ts
import type {CardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"children": [
{
"id": "heading",
"kind": "heading",
"level": 2,
"text": "Order #8412 — Drift Runner"
},
{
"align": "center",
"children": [
{
"id": "status",
"kind": "badge",
"label": "Shipped",
"tone": "success"
},
{
"id": "priority",
"kind": "badge",
"label": "Gift order",
"tone": "accent"
},
{
"id": "space",
"kind": "spacer",
"size": "auto"
},
{
"count": 231,
"id": "rating",
"kind": "rating",
"value": 4.8
}
],
"gap": "sm",
"id": "status-row",
"kind": "row"
},
{
"id": "divider",
"kind": "divider"
},
{
"children": [
{
"icon": "route",
"id": "route-heading",
"kind": "heading",
"level": 4,
"text": "Route"
},
{
"content": "Left the regional hub this morning and is expected tomorrow.",
"id": "route-text",
"kind": "text",
"title": "Route details",
"variant": "clear"
},
{
"id": "delivery",
"kind": "progress",
"label": "Delivery progress",
"value": 72
}
],
"gap": "sm",
"id": "delivery-column",
"kind": "col"
}
],
"id": "order-summary",
"kind": "card",
"title": "Order summary",
"variant": "surface"
} satisfies CardComponent;
```
## Text
Render concise, Markdown-aware narrative content in a clear or card presentation.
### Text Content
```ts
import type {TextComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "summary",
"title": "Executive summary",
"content": "Revenue grew **18% this quarter**, led by *enterprise expansion* and improved retention.",
"kind": "text"
} satisfies TextComponent;
```
## Callout
Highlight an important message with semantic tone and an optional icon.
### Callout
```ts
import type {CalloutComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "warning",
"title": "Attention needed",
"content": "Three renewals worth **$142K** need attention before `Friday`.",
"icon": "alert",
"kind": "callout",
"tone": "warning"
} satisfies CalloutComponent;
```
## Image
Present a single image or responsive gallery with captions, accessible alternatives, and reliable fallbacks.
### Images
```ts
import type {ImageComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "images",
"title": "Visual evidence",
"images": [
{
"alt": "NEO home robot standing in a living room",
"caption": "NEO Home Robot · $499/m",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/neo1.jpeg"
}
],
"kind": "image"
} satisfies ImageComponent;
```
### Image Gallery
```ts
import type {ImageComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Photos pulled from the workspace events feed.",
"id": "image-gallery",
"title": "Event highlights",
"images": [
{
"alt": "Futuristic robot on stage",
"caption": "Bridging the Future · Today, 6:30 PM",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/robot1.jpeg"
},
{
"alt": "Avocado on a colored background",
"caption": "Avocado Hackathon · Wed, 4:30 PM",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/avocado.jpeg"
},
{
"alt": "Oranges under colored light",
"caption": "Sound Electro | Beyond art · Fri, 8:00 PM",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/oranges.jpeg"
},
{
"alt": "Bowl of fresh cherries",
"caption": "ACME Creators meetup · Oct 10",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/cherries.jpeg"
}
],
"kind": "image"
} satisfies ImageComponent;
```
### Horizontal gallery
```ts
import type {ImageComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Swipe through the event photos without leaving the conversation.",
"id": "horizontal-image-gallery",
"title": "Event highlights",
"images": [
{
"alt": "Futuristic robot on stage",
"caption": "Bridging the Future · Today, 6:30 PM",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/robot1.jpeg"
},
{
"alt": "Avocado on a colored background",
"caption": "Avocado Hackathon · Wed, 4:30 PM",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/avocado.jpeg"
},
{
"alt": "Oranges under colored light",
"caption": "Sound Electro | Beyond art · Fri, 8:00 PM",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/oranges.jpeg"
},
{
"alt": "Bowl of fresh cherries",
"caption": "ACME Creators meetup · Oct 10",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/cherries.jpeg"
}
],
"kind": "image",
"variant": "horizontal"
} satisfies ImageComponent;
```
### Image Gallery Unreliable Sources
```ts
import type {ImageComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Photos returned from public listings — one link is dead.",
"id": "image-gallery-unreliable",
"title": "Las Cuartetas photos",
"images": [
{
"alt": "Restaurant interior",
"caption": "Tripadvisor listing photo",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/robot1.jpeg"
},
{
"alt": "Las Cuartetas pizza photo",
"caption": "Instagram post",
"src": "https://scontent.example.com/blocked-by-hotlink-protection.jpg"
},
{
"alt": "Pizza al paso",
"aspectRatio": "portrait",
"caption": "Guide photo (portrait source)",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/avocado.jpeg"
},
{
"alt": "Dining room",
"aspectRatio": "wide",
"caption": "Official site (wide source)",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/oranges.jpeg"
}
],
"kind": "image"
} satisfies ImageComponent;
```
## Tag List
Summarize categories, filters, or attributes as a compact set of tags.
### Tags
```ts
import type {TagListComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "tags",
"title": "Segments",
"kind": "tag-list",
"tags": [
"Enterprise",
"Expansion",
"At risk",
"Q3"
]
} satisfies TagListComponent;
```
## List
Organize short items as bullets, numbered steps, or a checklist.
### List
```ts
import type {ListComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "list",
"title": "Next actions",
"items": [
{
"checked": true,
"id": "one",
"text": "Review enterprise renewals"
},
{
"id": "two",
"text": "Share forecast with finance"
},
{
"id": "three",
"text": "Schedule pipeline review"
}
],
"kind": "list",
"style": "checklist"
} satisfies ListComponent;
```
## List Block
Show richer repeated rows with icons or images, descriptions, trailing metadata, and narrow layouts.
### List Blocks
```ts
import type {ListBlockComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Accounts the Assistant flagged for this quarter.",
"id": "blocks",
"title": "Priority accounts",
"items": [
{
"description": "Renewal in 14 days",
"id": "acme",
"imageUrl": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/demo1.jpg",
"meta": "$84K",
"title": "Acme Corp"
},
{
"description": "Expansion opportunity",
"id": "northstar",
"imageUrl": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/demo2.jpg",
"meta": "+$62K",
"title": "Northstar Labs"
},
{
"description": "Usage down 18% this month",
"icon": "activity",
"id": "maple",
"meta": "-$18K",
"title": "Maple Studio"
},
{
"description": "Pilot converting to annual",
"icon": "rocket",
"id": "evergreen",
"meta": "$36K",
"title": "Evergreen"
}
],
"kind": "list-block"
} satisfies ListBlockComponent;
```
### List Blocks Narrow
```ts
import type {ListBlockComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Ranked places currently shown on the map.",
"id": "narrow-blocks",
"title": "Central Buenos Aires pizza picks",
"items": [
{
"description": "Guide-recommended for its Napolitana-style pizza.",
"id": "el-cuartito",
"imageAlt": "Pizza served at El Cuartito",
"imageUrl": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/oranges.jpeg",
"meta": "Recoleta",
"rating": 4.3,
"title": "1. El Cuartito"
},
{
"description": "Classic central Buenos Aires pizza experience.",
"id": "las-cuartetas",
"imageAlt": "Food served at Las Cuartetas",
"imageUrl": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/avocado.jpeg",
"meta": "San Nicolás · Rating not supplied",
"title": "2. Las Cuartetas"
},
{
"description": "Convenient casual stop in central CABA.",
"id": "kentucky",
"imageAlt": "Food served at Kentucky Pizzería",
"imageUrl": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/cherries.jpeg",
"meta": "San Nicolás · Rating not supplied",
"title": "3. Kentucky Pizzería"
}
],
"kind": "list-block"
} satisfies ListBlockComponent;
```
## Accordion
Reveal supporting explanations progressively without overwhelming the primary result.
### Accordion
```ts
import type {AccordionComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "faq",
"title": "About this analysis",
"kind": "accordion",
"sections": [
{
"content": "Based on CRM opportunities updated in the last 24 hours.",
"defaultOpen": true,
"id": "source",
"title": "Where does this data come from?"
},
{
"content": "The forecast uses weighted pipeline value and historical win rates.",
"id": "method",
"title": "How is the forecast calculated?"
}
]
} satisfies AccordionComponent;
```
## Steps
Communicate progress, delivery history, or an itinerary with statuses, timestamps, and optional detail.
### Steps
```ts
import type {StepsComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "steps",
"title": "Analysis progress",
"kind": "steps",
"steps": [
{
"id": "collect",
"status": "completed",
"title": "Collect account data"
},
{
"content": "Comparing renewal risk and expansion signals.",
"id": "analyze",
"progress": 64,
"status": "in-progress",
"title": "Analyze portfolio"
},
{
"id": "share",
"status": "pending",
"title": "Share recommendations"
}
]
} satisfies StepsComponent;
```
### Timeline
```ts
import type {StepsComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "delivery-timeline",
"title": "Delivery history",
"kind": "steps",
"steps": [
{
"id": "created",
"status": "completed",
"timestamp": "09:12",
"title": "Order created"
},
{
"content": "Package transferred to the regional distribution center.",
"id": "transit",
"meta": "Buenos Aires",
"status": "in-progress",
"timestamp": "13:45",
"title": "In transit"
},
{
"id": "delivery",
"status": "pending",
"timestamp": "Tomorrow",
"title": "Delivery"
}
],
"variant": "timeline"
} satisfies StepsComponent;
```
### Itinerary Timeline
```ts
import type {StepsComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "kyoto-itinerary",
"title": "Days 1–5: Kyoto & Nara",
"kind": "steps",
"steps": [
{
"content": "Fushimi Inari before 7am, then Tofuku-ji and a Nishiki market lunch.",
"icon": "flag",
"id": "day-1",
"title": "Day 1 — Torii gates at dawn"
},
{
"content": "Arashiyama bamboo grove, Tenryu-ji gardens, and monkey park viewpoint.",
"icon": "route",
"id": "day-2",
"title": "Day 2 — West Kyoto"
},
{
"content": "Kinkaku-ji golden pavilion, Ryoan-ji rock garden, and a tea ceremony.",
"icon": "star",
"id": "day-3",
"title": "Day 3 — Zen day"
},
{
"content": "Philosopher's Path, Nanzen-ji aqueduct, and a kaiseki dinner in Gion.",
"icon": "heart",
"id": "day-4",
"title": "Day 4 — East side classics"
},
{
"content": "Todai-ji Great Buddha and the deer park.",
"icon": "location",
"id": "day-5",
"title": "Day 5 — Nara day trip"
}
],
"variant": "timeline"
} satisfies StepsComponent;
```
### Image itinerary
```ts
import type {StepsComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Open any place photo for a closer look.",
"id": "tokyo-image-itinerary",
"title": "Tokyo places in pictures",
"kind": "steps",
"steps": [
{
"content": "Begin beneath the cedar canopy before the morning crowds arrive.",
"id": "meiji-jingu",
"image": {
"alt": "Visitors passing beneath a wooden torii at Meiji Jingu in the rain",
"src": "https://images.unsplash.com/photo-1663867384843-71ebf39ecd24?auto=format&fit=crop&w=1600&q=85"
},
"title": "Meiji Jingu"
},
{
"content": "Walk through Kaminarimon and browse the stalls along Nakamise-dori.",
"id": "sensoji",
"image": {
"alt": "Senso-ji Temple glowing red and gold in Asakusa",
"src": "https://images.unsplash.com/photo-1543402701-cfd2d56f773b?auto=format&fit=crop&w=1600&q=85"
},
"title": "Sensō-ji after lunch"
},
{
"content": "Finish with yakitori in the lantern-lit lanes beside Shinjuku Station.",
"id": "omoide-yokocho",
"image": {
"alt": "Lanterns glowing above the narrow lanes of Omoide Yokocho",
"src": "https://images.unsplash.com/photo-1720354800398-5c38abd962bf?auto=format&fit=crop&w=1600&q=85"
},
"title": "Omoide Yokocho"
},
{
"content": "Take in the illuminated skyline from the surrounding streets at dusk.",
"id": "tokyo-tower",
"image": {
"alt": "Tokyo Tower glowing orange over the city skyline at dusk",
"src": "https://images.unsplash.com/photo-1513407030348-c983a97b98d8?auto=format&fit=crop&w=1600&q=85"
},
"title": "Tokyo Tower"
}
],
"variant": "timeline"
} satisfies StepsComponent;
```
## Code Block
Display generated code with syntax highlighting and built-in copy affordances.
### Code Block
```ts
import type {CodeBlockComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "code",
"title": "Generated filter",
"code": "const atRisk = accounts.filter(\n (account) => account.health < 0.6,\n);",
"kind": "code-block",
"language": "typescript"
} satisfies CodeBlockComponent;
```
## Diagram
Draw a process, an exchange, a data model, or a state machine from [Mermaid](https://mermaid.js.org) source — the relationships prose describes badly and a linear list of steps cannot show at all. Flowchart, sequence, ER, and state diagrams all work, and the whole structure costs one node rather than the dozens a hand-built tree would spend.
Diagrams fit the panel width, so a wide one can get dense. An expand control opens it in a dialog with zoom and pan, which is where a large flowchart is actually readable.
Mermaid renders with click directives and HTML labels disabled, and the contract rejects both, so a diagram can never navigate or inject. Source that fails to parse falls back to showing itself, the same way a code block does when a grammar will not load.
The diagram renderer loads `mermaid` on demand from `@heroui/agent`'s dependencies — no separate install step. The renderer is fetched as its own chunk the first time a diagram appears, so pages that never show one download nothing extra.
### Diagram
```ts
import type {DiagramComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "How an order moves from checkout to dispatch.",
"id": "fulfillment",
"title": "Fulfillment flow",
"chart": "flowchart LR\n A[Order placed] --> B{In stock?}\n B -- yes --> C[Pick and pack]\n B -- no --> D[Backorder]\n C --> E[Ship]\n D --> F[Notify customer]",
"kind": "diagram"
} satisfies DiagramComponent;
```
### Sequence
```ts
import type {DiagramComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "handoff",
"title": "Order handoff",
"chart": "sequenceDiagram\n participant Shopper\n participant Store\n participant Warehouse\n Shopper->>Store: Place order\n Store->>Warehouse: Reserve stock\n Warehouse-->>Store: Confirmed\n Store-->>Shopper: Dispatch date",
"kind": "diagram"
} satisfies DiagramComponent;
```
### Unrenderable source
```ts
import type {DiagramComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Invalid source falls back to the diagram text.",
"id": "broken",
"title": "Unrenderable diagram",
"chart": "flowchart LR\n A[Unclosed --> B",
"kind": "diagram"
} satisfies DiagramComponent;
```
## Tabs
Group parallel views behind labeled tabs. A panel holds either explanatory prose or a full stack of Agent UI components — a table under one tab, a chart under the next — so a comparison does not have to become one long scroll.
### Tabs
```ts
import type {TabsComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Compare product health across the customer lifecycle.",
"id": "tabs",
"title": "Product signals",
"defaultValue": "summary",
"kind": "tabs",
"tabs": [
{
"content": "Activation reached 71%, led by guided setup and faster team invitations.",
"id": "summary",
"label": "Adoption"
},
{
"content": "Week-eight retention improved to 58%, up six points from the previous cohort.",
"id": "retention",
"label": "Retention"
},
{
"content": "Power users created 3.2× more automations and invited twice as many teammates.",
"id": "engagement",
"label": "Engagement"
}
],
"variant": "secondary"
} satisfies TabsComponent;
```
### Rich content
```ts
import type {TabsComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "A coordinated weeknight menu with prep notes and a shared cooking schedule.",
"id": "lemon-herb-dinner",
"title": "Lemon-Herb Dinner Plan",
"defaultValue": "chicken",
"kind": "tabs",
"tabs": [
{
"children": [
{
"id": "chicken-intro",
"title": "Lemon-Herb Chicken",
"content": "Roast the chicken over sliced lemons, then finish the pan juices while the meat rests.",
"kind": "text",
"variant": "clear"
},
{
"id": "chicken-ingredients",
"title": "What You’ll Need",
"items": [
{
"id": "chicken",
"text": "4–5 lb whole chicken"
},
{
"id": "lemons",
"text": "2 lemons, thinly sliced"
},
{
"id": "herbs",
"text": "Fresh oregano, parsley, and garlic"
},
{
"id": "chicken-stock",
"text": "1 cup low-sodium chicken stock"
}
],
"kind": "list",
"style": "bulleted"
},
{
"id": "chicken-timeline",
"title": "Dinner Timing",
"kind": "steps",
"steps": [
{
"content": "Pat the chicken dry, season it, and arrange it over the lemon slices.",
"id": "season-chicken",
"timestamp": "5:10 PM",
"title": "Season"
},
{
"content": "Cook until the thickest part of the thigh reaches 165°F.",
"id": "roast-chicken",
"timestamp": "5:25 PM",
"title": "Roast"
},
{
"content": "Rest the chicken while reducing the pan juices into a quick sauce.",
"id": "finish-sauce",
"timestamp": "6:20 PM",
"title": "Finish"
}
],
"variant": "timeline"
}
],
"id": "chicken",
"label": "Roast Chicken"
},
{
"children": [
{
"id": "sides-list",
"title": "Vegetable Sides",
"items": [
{
"id": "carrots",
"text": "Charred carrots with cumin"
},
{
"id": "potatoes",
"text": "Crispy rosemary potatoes"
},
{
"id": "salad",
"text": "Arugula salad with shaved fennel"
}
],
"kind": "list",
"style": "bulleted"
},
{
"id": "sides-tip",
"title": "Keep It Moving",
"content": "Slide the potatoes onto the lower rack with the chicken; dress the salad just before serving.",
"icon": "clock",
"kind": "callout",
"tone": "accent"
}
],
"id": "sides",
"label": "Vegetable Sides"
},
{
"children": [
{
"id": "desserts-list",
"title": "Something Sweet",
"items": [
{
"id": "cake",
"text": "Orange and olive oil yogurt cake"
},
{
"id": "pears",
"text": "Honey-poached pears with pistachios"
}
],
"kind": "list",
"style": "bulleted"
}
],
"id": "desserts",
"label": "Dessert"
}
],
"variant": "secondary"
} satisfies TabsComponent;
```
## Generated Composition
Combine typed layout and content nodes into one validated tree when no single catalog component fits the result.
### Composed Primitives
```ts
import type {ColComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "order-summary",
"children": [
{
"id": "heading",
"kind": "heading",
"level": 2,
"text": "Order #8412 — Drift Runner"
},
{
"id": "status-row",
"align": "center",
"children": [
{
"id": "status",
"kind": "badge",
"label": "Shipped",
"tone": "success"
},
{
"id": "priority",
"kind": "badge",
"label": "Gift order",
"tone": "accent"
},
{
"id": "spacer",
"kind": "spacer",
"size": "auto"
},
{
"id": "rating",
"count": 231,
"kind": "rating",
"value": 4.8
}
],
"gap": "sm",
"kind": "row"
},
{
"id": "divider",
"kind": "divider"
},
{
"id": "delivery-col",
"children": [
{
"id": "route-heading",
"icon": "route",
"kind": "heading",
"level": 4,
"text": "Route"
},
{
"id": "route-text",
"title": "Route details",
"content": "Left the regional hub in Newark this morning.",
"kind": "text",
"variant": "clear"
},
{
"id": "delivery",
"kind": "progress",
"label": "Delivery progress",
"value": 72
}
],
"gap": "sm",
"kind": "col"
},
{
"id": "actions-row",
"children": [
{
"id": "track",
"kind": "button",
"label": "Track package",
"toolCall": {
"arguments": {
"orderId": "8412"
},
"name": "openOrderTracking"
},
"variant": "primary"
},
{
"id": "help",
"kind": "button",
"label": "Something wrong?",
"prompt": "I need help with order #8412",
"variant": "outline"
}
],
"gap": "sm",
"kind": "row"
}
],
"gap": "md",
"kind": "col"
} satisfies ColComponent;
```
## Product Card
Display one or more products with imagery, pricing, ratings, badges, and actions.
### Product Card
```ts
import type {ProductCardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Chosen for its roomy forefoot and explicit wide-foot fit guidance.",
"id": "best-match",
"title": "Best match: Drift Runner",
"kind": "product-card",
"products": [
{
"actions": [
{
"id": "view",
"label": "View in store",
"toolCall": {
"arguments": {
"ids": [
"drift-runner-bone"
],
"mode": "detail"
},
"name": "presentProducts"
},
"variant": "primary"
},
{
"id": "fit",
"label": "Ask about fit",
"prompt": "How does the Drift Runner fit?"
}
],
"badge": {
"label": "US 8.5 in stock",
"tone": "success"
},
"description": "True to size with a roomy forefoot; a strong choice for wider feet. Everyday wear, walking, and travel.",
"id": "drift-runner-bone",
"image": {
"alt": "Drift Runner sneaker in bone",
"src": "https://images.unsplash.com/photo-1560769629-975ec94e6a86?auto=format&fit=crop&w=1200&q=80"
},
"meta": "Shoes · Bone",
"name": "Drift Runner",
"price": {
"amount": 128,
"currency": "USD"
},
"rating": {
"count": 231,
"value": 4.8
}
}
]
} satisfies ProductCardComponent;
```
### Product Grid
```ts
import type {ProductCardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "In-stock picks matching “everyday sneakers under $130”.",
"id": "search-results",
"title": "Everyday sneakers",
"kind": "product-card",
"products": [
{
"actions": [
{
"id": "view-drift",
"label": "View in store",
"toolCall": {
"arguments": {
"ids": [
"drift-runner-bone"
],
"mode": "detail"
},
"name": "presentProducts"
}
}
],
"badge": {
"label": "Bestseller",
"tone": "accent"
},
"id": "drift-runner-bone",
"image": {
"alt": "Drift Runner sneaker in bone",
"src": "https://images.unsplash.com/photo-1560769629-975ec94e6a86?auto=format&fit=crop&w=1200&q=80"
},
"meta": "Shoes · Bone",
"name": "Drift Runner",
"price": {
"amount": 128,
"currency": "USD"
},
"rating": {
"count": 231,
"value": 4.8
}
},
{
"actions": [
{
"id": "view-sola",
"label": "View in store",
"toolCall": {
"arguments": {
"ids": [
"sola-walk-sand"
],
"mode": "detail"
},
"name": "presentProducts"
}
}
],
"id": "sola-walk-sand",
"image": {
"alt": "Sola Walk sneaker in sand",
"src": "https://images.unsplash.com/photo-1525966222134-fcfa99b8ae77?auto=format&fit=crop&w=1200&q=80"
},
"meta": "Shoes · Sand",
"name": "Sola Walk",
"price": {
"amount": 108,
"currency": "USD"
},
"rating": {
"count": 156,
"value": 4.5
}
},
{
"actions": [
{
"id": "view-tide",
"label": "View in store",
"toolCall": {
"arguments": {
"ids": [
"tide-deck-navy"
],
"mode": "detail"
},
"name": "presentProducts"
}
}
],
"id": "tide-deck-navy",
"image": {
"alt": "Tide Deck sneaker in navy",
"src": "https://images.unsplash.com/photo-1542291026-7eec264c27ff?auto=format&fit=crop&w=1200&q=80"
},
"meta": "Shoes · Navy",
"name": "Tide Deck",
"price": {
"amount": 88,
"currency": "USD"
},
"rating": {
"count": 118,
"value": 4.4
}
},
{
"actions": [
{
"id": "view-harbor",
"label": "View in store",
"toolCall": {
"arguments": {
"ids": [
"harbor-court-oat"
],
"mode": "detail"
},
"name": "presentProducts"
}
}
],
"badge": {
"label": "Low stock",
"tone": "warning"
},
"id": "harbor-court-oat",
"image": {
"alt": "Harbor Court sneaker in oat",
"src": "https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=1200&q=80"
},
"meta": "Shoes · Oat",
"name": "Harbor Court",
"price": {
"amount": 116,
"currency": "USD"
},
"rating": {
"count": 192,
"value": 4.6
}
}
]
} satisfies ProductCardComponent;
```
## Record Card
Present a structured entity with labeled fields, status, media, and contextual actions.
### Record Card
```ts
import type {RecordCardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Payment processing recovered after an automatic retry.",
"id": "incident-record",
"title": "Elevated checkout errors",
"actions": [
{
"id": "dismiss",
"label": "Dismiss",
"variant": "outline"
},
{
"id": "open",
"label": "Open incident",
"variant": "primary"
}
],
"eyebrow": "Production · API",
"fields": [
{
"icon": "users",
"label": "Owner",
"value": "Payments team"
},
{
"icon": "calendar",
"label": "Started",
"value": "12 Jul, 10:24"
},
{
"icon": "clock",
"label": "Duration",
"value": "8 minutes"
},
{
"icon": "percent",
"label": "Requests affected",
"tone": "warning",
"value": "2.4%"
}
],
"kind": "record-card",
"layout": "details",
"status": {
"label": "Resolved",
"tone": "success"
},
"variant": "surface"
} satisfies RecordCardComponent;
```
### Compact Record
```ts
import type {RecordCardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "A concise peer card for a comparison grid.",
"id": "growth-plan",
"title": "Growth",
"eyebrow": "Growth plan",
"fields": [
{
"label": "Price",
"value": "$49/mo"
},
{
"label": "Seats",
"value": "10"
},
{
"label": "Projects",
"value": "Unlimited"
}
],
"kind": "record-card",
"layout": "compact",
"status": {
"label": "Recommended",
"tone": "accent"
},
"variant": "outline"
} satisfies RecordCardComponent;
```
### Media Record
```ts
import type {RecordCardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "A visual record for entities where the image carries useful context.",
"id": "buenos-aires-stay",
"title": "Palermo design stay",
"actions": [
{
"id": "view",
"label": "View itinerary",
"variant": "primary"
}
],
"eyebrow": "Buenos Aires · Argentina",
"fields": [
{
"label": "Dates",
"value": "18–23 September"
},
{
"label": "Guests",
"value": "2 adults"
},
{
"label": "Rating",
"tone": "success",
"value": "4.9"
},
{
"label": "Total",
"value": "$1,420"
}
],
"image": {
"alt": "Colorful buildings in Buenos Aires",
"src": "https://images.unsplash.com/photo-1589909202802-8f4aadce1849?auto=format&fit=crop&w=1200&q=80"
},
"kind": "record-card",
"layout": "media",
"status": {
"label": "Top match",
"tone": "success"
},
"variant": "surface"
} satisfies RecordCardComponent;
```
## Item Card
Present one compact row with composable leading, primary, and trailing content.
### Item Card
```ts
import type {ItemCardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "A standalone three-slot row.",
"id": "workspace-storage",
"title": "Workspace storage",
"action": {
"id": "review-storage",
"label": "Review storage",
"prompt": "Review the current workspace storage usage"
},
"kind": "item-card",
"left": [
{
"id": "storage-icon",
"kind": "icon",
"name": "database",
"size": "md"
}
],
"middle": [
{
"id": "storage-title",
"kind": "heading",
"level": 4,
"text": "Workspace storage"
},
{
"id": "storage-description",
"title": "Storage usage",
"content": "72 GB of 100 GB used",
"kind": "text",
"variant": "clear"
}
],
"right": [
{
"id": "storage-status",
"kind": "badge",
"label": "72%",
"tone": "warning"
}
]
} satisfies ItemCardComponent;
```
## Item Card Group
Group related item cards in a list or grid with an optional shared heading.
### Item Card Group
```ts
import type {ItemCardGroupComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Select one of the devices linked to your account.",
"id": "device-picker",
"title": "Which device is crashing?",
"children": [
{
"id": "iphone-air",
"title": "iPhone Air",
"action": {
"id": "choose-iphone-air",
"label": "Choose iPhone Air",
"prompt": "Troubleshoot the crash on my iPhone Air"
},
"kind": "item-card",
"left": [
{
"id": "iphone-air-icon",
"kind": "icon",
"name": "device-mobile"
}
],
"middle": [
{
"id": "iphone-air-title",
"kind": "heading",
"level": 4,
"text": "iPhone Air"
},
{
"id": "iphone-air-description",
"title": "iPhone Air activity",
"content": "Active today · iOS 26",
"kind": "text",
"variant": "clear"
}
],
"variant": "transparent"
},
{
"id": "ipad-mini",
"title": "iPad Mini 6",
"action": {
"id": "choose-ipad-mini",
"label": "Choose iPad Mini 6",
"prompt": "Troubleshoot the crash on my iPad Mini 6"
},
"kind": "item-card",
"left": [
{
"id": "ipad-mini-icon",
"kind": "icon",
"name": "device-mobile"
}
],
"middle": [
{
"id": "ipad-mini-title",
"kind": "heading",
"level": 4,
"text": "iPad Mini 6"
},
{
"id": "ipad-mini-description",
"title": "iPad Mini activity",
"content": "Last used 2 weeks ago · iOS 17.5",
"kind": "text",
"variant": "clear"
}
],
"variant": "transparent"
},
{
"id": "macbook-air",
"title": "MacBook Air M2",
"action": {
"id": "choose-macbook-air",
"label": "Choose MacBook Air M2",
"prompt": "Troubleshoot the crash on my MacBook Air M2"
},
"isSelected": true,
"kind": "item-card",
"left": [
{
"id": "macbook-air-icon",
"kind": "icon",
"name": "device-desktop"
}
],
"middle": [
{
"id": "macbook-air-title",
"kind": "heading",
"level": 4,
"text": "MacBook Air M2"
},
{
"id": "macbook-air-description",
"title": "MacBook Air activity",
"content": "Last used yesterday · macOS 14.4",
"kind": "text",
"variant": "clear"
}
],
"variant": "transparent"
},
{
"id": "pixel-7",
"title": "Pixel 7",
"action": {
"id": "choose-pixel",
"label": "Choose Pixel 7",
"prompt": "Troubleshoot the crash on my Pixel 7"
},
"isDisabled": true,
"kind": "item-card",
"left": [
{
"id": "pixel-icon",
"kind": "icon",
"name": "device-mobile"
}
],
"middle": [
{
"id": "pixel-title",
"kind": "heading",
"level": 4,
"text": "Pixel 7"
},
{
"id": "pixel-description",
"title": "Pixel activity",
"content": "Last used 2 years ago · Android 14",
"kind": "text",
"variant": "clear"
}
],
"variant": "transparent"
}
],
"kind": "item-card-group",
"layout": "list",
"showHeader": true,
"variant": "transparent"
} satisfies ItemCardGroupComponent;
```
## Product Signals
Compose related signal panels with the public Tabs API.
### Product Signals
```ts
import type {ProductSignalsComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Switch between adoption and retention views for the latest workspace cohort.",
"id": "product-signals",
"title": "Product signals",
"defaultValue": "adoption",
"kind": "product-signals",
"tabs": [
{
"component": {
"description": "Share of active workspaces using each capability this month.",
"id": "feature-adoption",
"title": "Feature adoption",
"data": [
{
"feature": "Guided setup",
"usage": 0.78
},
{
"feature": "Team invites",
"usage": 0.64
},
{
"feature": "Automations",
"usage": 0.52
},
{
"feature": "Shared views",
"usage": 0.43
}
],
"series": [
{
"dataKey": "usage",
"format": {
"maximumFractionDigits": 0,
"style": "percent"
},
"label": "Workspace adoption"
}
],
"xKey": "feature",
"kind": "bar-chart",
"layout": "horizontal"
},
"id": "adoption",
"label": "Adoption"
},
{
"component": {
"description": "Percentage of the latest customer cohort still active by week.",
"id": "cohort-retention",
"title": "Cohort retention",
"data": [
{
"retention": 0.72,
"week": "W1"
},
{
"retention": 0.67,
"week": "W2"
},
{
"retention": 0.63,
"week": "W3"
},
{
"retention": 0.61,
"week": "W4"
},
{
"retention": 0.59,
"week": "W6"
},
{
"retention": 0.58,
"week": "W8"
}
],
"series": [
{
"dataKey": "retention",
"format": {
"maximumFractionDigits": 0,
"style": "percent"
},
"label": "Active workspaces"
}
],
"xKey": "week",
"kind": "line-chart"
},
"id": "retention",
"label": "Retention"
}
]
} satisfies ProductSignalsComponent;
```
## Composition API
These polished response cards are validated Agent UI kinds, so the hosted Agent can select the same Flight Tracker, Player Card, event, notification, and weather interfaces shown in Storybook.
### Flight Tracker
```ts
import type {FlightTrackerComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Current flight progress and timing.",
"id": "flight-aa-900",
"title": "AA 900",
"airline": {
"logo": {
"alt": "Hero Air",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/heroui_isotipo.png"
},
"name": "Hero Air"
},
"date": "Fri, Apr 25",
"destination": {
"label": "San Francisco",
"status": "On time",
"time": "7:40 AM +1"
},
"flightNumber": "AA 900",
"kind": "flight-tracker",
"origin": {
"label": "Buenos Aires",
"status": "On time",
"time": "9:05 PM"
},
"progress": 30
} satisfies FlightTrackerComponent;
```
### Create Event
```ts
import type {CreateEventComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "A proposed meeting shown alongside the existing schedule.",
"id": "create-roadmap-event",
"title": "Create event",
"actions": [
{
"id": "discard",
"label": "Discard",
"prompt": "Discard the proposed event",
"variant": "outline"
},
{
"id": "add",
"label": "Add to calendar",
"prompt": "Add the proposed Q1 roadmap review to my calendar",
"variant": "primary"
}
],
"date": {
"day": 28,
"weekday": "Friday"
},
"events": [
{
"id": "lunch",
"time": "12:00 – 12:45 PM",
"title": "Lunch",
"tone": "danger"
},
{
"id": "roadmap",
"status": "proposed",
"time": "1:00 – 2:00 PM",
"title": "Q1 roadmap review",
"tone": "accent"
},
{
"id": "standup",
"time": "3:30 – 4:00 PM",
"title": "Team standup",
"tone": "danger"
}
],
"kind": "create-event"
} satisfies CreateEventComponent;
```
### Playlist
```ts
import type {PlaylistComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Three tracks selected for the current playlist.",
"id": "weekend-playlist",
"title": "Weekend playlist",
"actions": [
{
"id": "view",
"label": "View playlist",
"prompt": "Open this playlist",
"variant": "outline"
}
],
"cover": {
"alt": "Playlist cover artwork",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/heroui-pro/agent-ui/vinyl-records.png"
},
"kind": "playlist",
"tracks": [
{
"artist": "Erik Mclean",
"id": "eyes-closed",
"image": {
"alt": "Eyes closed cover",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/heroui-pro-landing/small-album-1.webp"
},
"title": "Eyes closed"
},
{
"artist": "Efe Kurnaz",
"id": "hymn-for-the-weekend",
"image": {
"alt": "Hymn for the weekend cover",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/heroui-pro-landing/small-album-2.webp"
},
"title": "Hymn for the weekend"
},
{
"artist": "Reinhart Julian",
"id": "something-like-this",
"image": {
"alt": "Something Just Like This cover",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/heroui-pro-landing/small-album-3.webp"
},
"title": "Something Just Like This"
}
]
} satisfies PlaylistComponent;
```
### Ride Status
```ts
import type {RideStatusComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Driver arrival and pickup location.",
"id": "ride-status",
"title": "Your ride is arriving",
"driver": {
"image": {
"alt": "Kate Wilson",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg"
},
"name": "Kate Wilson"
},
"eta": "1 min",
"kind": "ride-status",
"pickup": "455 Valencia St"
} satisfies RideStatusComponent;
```
### Purchase Items
```ts
import type {PurchaseItemsComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Review the items and final total.",
"id": "cafe-order",
"title": "Purchase items",
"actions": [
{
"id": "cart",
"label": "Add to cart",
"prompt": "Add these items to my cart",
"variant": "outline"
},
{
"id": "purchase",
"label": "Purchase",
"prompt": "Purchase these items",
"variant": "primary"
}
],
"currency": "USD",
"items": [
{
"description": "Toasted · Hot",
"id": "wrap",
"image": {
"alt": "Egg & bacon wrap",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/images/egg.webp"
},
"name": "Egg & bacon wrap",
"price": 7.25
},
{
"description": "Granola · Seasonal",
"id": "yogurt",
"image": {
"alt": "Cherry yogurt bowl",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/cherries.jpeg"
},
"name": "Cherry yogurt bowl",
"price": 5.9
},
{
"description": "16oz iced · Mint",
"id": "tonic",
"image": {
"alt": "Citrus tonic",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/oranges.jpeg"
},
"name": "Citrus tonic",
"price": 4.85
}
],
"kind": "purchase-items",
"totals": [
{
"amount": 18,
"label": "Subtotal"
},
{
"amount": 1.58,
"label": "Sales tax (8.75%)"
},
{
"amount": 19.58,
"emphasis": true,
"label": "Total with tax"
}
]
} satisfies PurchaseItemsComponent;
```
### Channel Message
```ts
import type {ChannelMessageComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "A project update with attachments.",
"id": "channel-update",
"title": "Project update",
"attachments": [
{
"id": "preview",
"image": {
"alt": "widget-preview.png",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/neo1.jpeg"
},
"name": "widget-preview.png"
},
{
"id": "spec",
"name": "agent-ui-spec.pdf"
},
{
"id": "roadmap",
"name": "q3-roadmap.md"
}
],
"author": {
"image": {
"alt": "John Doe",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"
},
"name": "John Doe"
},
"channel": "#proj-agent-ui",
"content": "End of week update for Agent UI:\n\n1. Designed new widget primitives with more flexibility.\n2. Made progress on streaming-ready examples.\n3. Prioritized remaining component requests.",
"kind": "channel-message",
"timestamp": "5:12 PM"
} satisfies ChannelMessageComponent;
```
### Purchase Complete
```ts
import type {PurchaseCompleteComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Your payment was successful.",
"id": "purchase-complete",
"title": "Purchase complete",
"action": {
"id": "details",
"label": "View details",
"prompt": "Show my purchase details"
},
"details": [
{
"label": "Estimated delivery",
"value": "Thursday, Oct 8"
},
{
"label": "Sold by",
"value": "HeroUI Pro Store"
}
],
"kind": "purchase-complete",
"paid": {
"amount": 1350,
"currency": "USD"
},
"product": {
"description": "Free delivery · 14-day returns",
"image": {
"alt": "Black knitted sneakers",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/heroui-pro-landing/sneakers.webp"
},
"name": "Knitted sneakers"
}
} satisfies PurchaseCompleteComponent;
```
### Player Card
```ts
import type {PlayerCardComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Match statistics for the selected player.",
"id": "player-foxy",
"title": "Foxy player card",
"backgroundImage": {
"alt": "Foxy on the pitch",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/docs/heroui-pro/agent-ui/fox-player.png"
},
"jerseyNumber": "9",
"kind": "player-card",
"playerName": "Foxy",
"stats": [
{
"label": "GLS",
"value": 18
},
{
"label": "AST",
"value": 11
},
{
"label": "TKL",
"value": 26
},
{
"label": "NUTMEGS",
"value": 17
}
]
} satisfies PlayerCardComponent;
```
### View Event
```ts
import type {ViewEventComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Upcoming calendar event.",
"id": "roadmap-event",
"title": "Q1 roadmap review",
"date": "Friday, Dec 28",
"kind": "view-event",
"time": "1:00 – 2:00 PM",
"tone": "accent"
} satisfies ViewEventComponent;
```
### Event Session
```ts
import type {EventSessionComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Design and deploy enterprise-grade agents with a composable UI system.",
"id": "orchestrating-agents",
"title": "Orchestrating Agents at Scale",
"action": {
"id": "view",
"label": "View",
"prompt": "Show this session"
},
"eyebrow": "Breakout session",
"kind": "event-session",
"location": "Cowell Theater",
"speakers": [
{
"image": {
"alt": "Emily Chen",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"
},
"name": "Emily Chen",
"role": "Design Engineer"
},
{
"image": {
"alt": "Michael Brown",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/orange.jpg"
},
"name": "Michael Brown",
"role": "Member of Technical Staff"
},
{
"image": {
"alt": "Olivia Davis",
"src": "https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/red.jpg"
},
"name": "Olivia Davis",
"role": "Developer Experience"
}
],
"time": "11:15 AM"
} satisfies EventSessionComponent;
```
### Enable Notification
```ts
import type {EnableNotificationComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Notify me when this item ships",
"id": "enable-notification",
"title": "Enable notification",
"actions": [
{
"id": "yes",
"label": "Yes",
"prompt": "Enable this notification",
"variant": "primary"
},
{
"id": "no",
"label": "No",
"prompt": "Do not enable this notification",
"variant": "outline"
}
],
"kind": "enable-notification"
} satisfies EnableNotificationComponent;
```
### Weather Forecast
```ts
import type {WeatherForecastComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Sun early, scattered showers and a breeze after lunch",
"id": "buenos-aires-forecast",
"title": "Five-day forecast",
"condition": "partly-cloudy",
"forecast": [
{
"condition": "clear",
"label": "Mon",
"temperature": 17
},
{
"condition": "rain",
"label": "Tue",
"temperature": 14
},
{
"condition": "partly-cloudy",
"label": "Wed",
"temperature": 16
},
{
"condition": "windy",
"label": "Thu",
"temperature": 13
},
{
"condition": "clear",
"label": "Fri",
"temperature": 18
}
],
"high": 20,
"kind": "weather-forecast",
"location": "Buenos Aires, AR",
"low": 11,
"unit": "celsius"
} satisfies WeatherForecastComponent;
```
### Weather Current
```ts
import type {WeatherCurrentComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Clear skies with a mild breeze off the river into the evening.",
"id": "buenos-aires-current-weather",
"title": "Current weather",
"condition": "clear",
"details": [
{
"label": "Feels like",
"value": "19°C"
},
{
"label": "Humidity",
"value": "62%"
}
],
"kind": "weather-current",
"location": "Buenos Aires",
"temperature": 20,
"unit": "celsius"
} satisfies WeatherCurrentComponent;
```
## Map
Explore ranked locations with an interactive map, place details, and contextual actions. Both Storybook entries are included, including the intentionally duplicated Real map fixture.
### Interactive Map
Interactive React example.
### Real map
Interactive React example.
## Row
Arrange generated nodes horizontally with alignment, distribution, spacing, and responsive wrapping.
```ts
import type {RowComponent} from "@heroui/agent-ui/contracts";
const contract = {
"align": "center",
"children": [
{
"id": "row-status",
"kind": "badge",
"label": "Shipped",
"tone": "success"
},
{
"id": "row-priority",
"kind": "badge",
"label": "Gift order",
"tone": "accent"
},
{
"id": "row-spacer",
"kind": "spacer",
"size": "auto"
},
{
"count": 231,
"id": "row-rating",
"kind": "rating",
"value": 4.8
}
],
"gap": "sm",
"id": "status-row",
"kind": "row"
} satisfies RowComponent;
```
## Column
Stack generated nodes vertically with controlled alignment, distribution, and spacing.
```ts
import type {ColComponent} from "@heroui/agent-ui/contracts";
const contract = {
"children": [
{
"icon": "route",
"id": "column-heading",
"kind": "heading",
"level": 3,
"text": "Delivery"
},
{
"content": "Left the regional hub this morning and is expected tomorrow.",
"id": "column-copy",
"kind": "text",
"title": "Delivery update",
"variant": "clear"
},
{
"id": "column-progress",
"kind": "progress",
"label": "Delivery progress",
"value": 72
}
],
"gap": "sm",
"id": "delivery-column",
"kind": "col"
} satisfies ColComponent;
```
## Grid
Arrange generated nodes in a responsive layout with one to four intended columns.
```ts
import type {GridComponent} from "@heroui/agent-ui/contracts";
const contract = {
"children": [
{
"content": "Ready for launch",
"id": "grid-design",
"kind": "text",
"title": "Design",
"variant": "card"
},
{
"content": "All checks passing",
"id": "grid-engineering",
"kind": "text",
"title": "Engineering",
"variant": "card"
},
{
"content": "Campaign scheduled",
"id": "grid-marketing",
"kind": "text",
"title": "Marketing",
"variant": "card"
}
],
"columns": 3,
"gap": "md",
"id": "launch-grid",
"kind": "grid"
} satisfies GridComponent;
```
## Spacer
Create fixed or flexible space between sibling nodes in a composed layout.
```ts
import type {SpacerComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "composition-spacer",
"kind": "spacer",
"size": "auto"
} satisfies SpacerComponent;
```
## Divider
Separate related groups of generated content with a subtle visual rule.
```ts
import type {DividerComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "composition-divider",
"kind": "divider"
} satisfies DividerComponent;
```
## Heading
Introduce a section with semantic hierarchy and an optional closed-vocabulary icon.
```ts
import type {HeadingComponent} from "@heroui/agent-ui/contracts";
const contract = {
"icon": "chart",
"id": "composition-heading",
"kind": "heading",
"level": 2,
"text": "Quarterly performance"
} satisfies HeadingComponent;
```
## Badge
Label status or compact metadata with a semantic tone and soft or solid treatment.
```ts
import type {BadgeComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "composition-badge",
"kind": "badge",
"label": "On track",
"tone": "success",
"variant": "soft"
} satisfies BadgeComponent;
```
## Icon
Display a theme-aware semantic icon from the safe Agent icon vocabulary.
```ts
import type {IconComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "composition-icon",
"kind": "icon",
"name": "rocket",
"size": "lg",
"tone": "accent"
} satisfies IconComponent;
```
## Rating
Show a score from zero to five with stars and an optional review count.
```ts
import type {RatingComponent} from "@heroui/agent-ui/contracts";
const contract = {
"count": 231,
"id": "composition-rating",
"kind": "rating",
"value": 4.8
} satisfies RatingComponent;
```
## Progress
Communicate completion, capacity, or readiness on a bounded percentage scale.
```ts
import type {ProgressComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "composition-progress",
"kind": "progress",
"label": "Launch readiness",
"value": 82
} satisfies ProgressComponent;
```
# Form Elements
**Category**: agents
**URL**: https://heroui.pro/docs/agents/components/form-elements
> Declarative forms and compact controls for collecting structured user input.
Browse every form component and meaningful Storybook variant available to HeroUI Agent. Each schema-backed preview exposes the exact form payload in its Contract tab.
## Form
Collect structured values with typed fields and declared submit actions.
Fields cover text, numbers, dates and times, sliders, switches, and every selection shape: `select` and `radio-group` for a handful of options, `checkbox-group` for a few independent ones, and `combobox` once the list grows past what someone would scan. A combobox filters as the user types, carries up to 200 options against a select's 30, and takes `selectionMode: "multiple"` to collect several as removable chips.
### Complete Form
```ts
import type {FormComponent} from "@heroui/agent-ui/contracts";
const contract = {
"description": "Native controls optimized for streamed GenUI snapshots.",
"id": "report-form",
"title": "Report preferences",
"actions": [
{
"id": "save",
"label": "Save preferences",
"variant": "primary"
},
{
"id": "cancel",
"label": "Cancel",
"variant": "tertiary"
}
],
"fields": [
{
"label": "Report name",
"name": "name",
"required": true,
"kind": "input",
"placeholder": "Weekly pipeline"
},
{
"label": "Instructions",
"name": "instructions",
"kind": "textarea",
"placeholder": "Focus on accounts at risk…"
},
{
"label": "Region",
"name": "region",
"kind": "select",
"options": [
{
"label": "Global",
"value": "global"
},
{
"label": "Americas",
"value": "americas"
},
{
"label": "Europe",
"value": "europe"
}
]
},
{
"label": "Cadence",
"name": "cadence",
"defaultValue": "weekly",
"kind": "radio-group",
"options": [
{
"label": "Daily",
"value": "daily"
},
{
"label": "Weekly",
"value": "weekly"
},
{
"label": "Monthly",
"value": "monthly"
}
]
},
{
"label": "Delivery",
"name": "delivery",
"defaultValue": [
"email"
],
"kind": "checkbox-group",
"options": [
{
"label": "Email",
"value": "email"
},
{
"label": "Dashboard",
"value": "dashboard"
},
{
"label": "Slack",
"value": "slack"
}
]
},
{
"label": "Maximum accounts",
"name": "limit",
"defaultValue": 25,
"kind": "number",
"max": 100,
"min": 1,
"step": 1
},
{
"description": "Only include accounts at or above this confidence score.",
"label": "Confidence threshold",
"name": "confidence",
"defaultValue": 70,
"kind": "slider",
"max": 100,
"min": 0,
"step": 5
},
{
"label": "Reporting window",
"name": "window",
"defaultValue": {
"end": "2026-08-31",
"start": "2026-08-01"
},
"kind": "date-range-picker",
"min": "2026-01-01"
},
{
"label": "Delivery time",
"name": "time",
"defaultValue": "09:00",
"kind": "time-field"
},
{
"description": "Send a notification when the report is ready.",
"label": "Notify me",
"name": "notify",
"defaultValue": true,
"kind": "switch"
}
],
"kind": "form"
} satisfies FormComponent;
```
### Compact Form
```ts
import type {FormComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "compact-form",
"title": "Schedule review",
"actions": [
{
"id": "confirm",
"label": "Confirm",
"variant": "primary"
}
],
"fields": [
{
"label": "Email",
"name": "email",
"required": true,
"inputType": "email",
"kind": "input"
},
{
"label": "Review date",
"name": "date",
"kind": "date-picker"
}
],
"kind": "form"
} satisfies FormComponent;
```
### Searchable option list
```ts
import type {FormComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "combobox-single",
"title": "Update billing",
"actions": [
{
"id": "apply",
"label": "Apply",
"variant": "primary"
}
],
"fields": [
{
"description": "Filters as you type, so a long list stays usable.",
"label": "Billing country",
"name": "country",
"kind": "combobox",
"options": [
{
"label": "Australia",
"value": "au"
},
{
"label": "Brazil",
"value": "br"
},
{
"label": "Canada",
"value": "ca"
},
{
"label": "France",
"value": "fr"
},
{
"label": "Germany",
"value": "de"
},
{
"label": "India",
"value": "in"
},
{
"label": "Japan",
"value": "jp"
},
{
"label": "Mexico",
"value": "mx"
},
{
"label": "Netherlands",
"value": "nl"
},
{
"label": "Portugal",
"value": "pt"
},
{
"label": "Spain",
"value": "es"
},
{
"label": "United Kingdom",
"value": "gb"
},
{
"label": "United States",
"value": "us"
}
],
"placeholder": "Search countries…"
}
],
"kind": "form"
} satisfies FormComponent;
```
### Searchable, multiple selection
```ts
import type {FormComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "combobox-multiple",
"title": "Build a segment",
"actions": [
{
"id": "save",
"label": "Save segment",
"variant": "primary"
}
],
"fields": [
{
"description": "Chosen tags appear below and can be removed individually.",
"label": "Account segments",
"name": "segments",
"defaultValue": [
"enterprise"
],
"kind": "combobox",
"options": [
{
"label": "Enterprise",
"value": "enterprise",
"description": "Above $100k ARR"
},
{
"label": "Mid-market",
"value": "mid-market"
},
{
"label": "Self-serve",
"value": "self-serve"
},
{
"label": "At risk",
"value": "at-risk"
},
{
"label": "Expansion ready",
"value": "expansion"
},
{
"label": "Renewing this quarter",
"value": "renewing"
}
],
"placeholder": "Search segments…",
"selectionMode": "multiple"
}
],
"kind": "form"
} satisfies FormComponent;
```
### Draft Email
```ts
import type {FormComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "widget-email",
"title": "Draft email",
"actions": [
{
"id": "discard",
"label": "Discard",
"variant": "outline"
},
{
"id": "send",
"label": "Send email",
"variant": "primary"
}
],
"fields": [
{
"label": "To",
"name": "to",
"defaultValue": "team@heroui.com",
"kind": "input"
},
{
"label": "Subject",
"name": "subject",
"defaultValue": "HeroUI Pro roadmap",
"kind": "input"
},
{
"label": "Message",
"name": "message",
"defaultValue": "Hey team,\n\nAny updates on the HeroUI Pro roadmap? We’re especially curious about Agent UI components.",
"kind": "textarea",
"rows": 5
}
],
"kind": "form"
} satisfies FormComponent;
```
### Create Task
```ts
import type {FormComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "widget-task",
"title": "Create task",
"actions": [
{
"id": "create",
"label": "Create task",
"variant": "primary"
}
],
"fields": [
{
"label": "Task title",
"name": "title",
"defaultValue": "Design resizable popup mode",
"kind": "input"
},
{
"label": "Description",
"name": "description",
"defaultValue": "Create a proposal for dynamic height and user resizing.",
"kind": "textarea",
"rows": 4
},
{
"label": "Due date",
"name": "due",
"defaultValue": "2026-10-16",
"kind": "date-picker"
}
],
"kind": "form"
} satisfies FormComponent;
```
### Software Purchase
```ts
import type {FormComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "widget-software",
"title": "Software purchase · $125/month",
"actions": [
{
"id": "discard",
"label": "Discard",
"variant": "outline"
},
{
"id": "confirm",
"label": "Confirm",
"variant": "primary"
}
],
"fields": [
{
"label": "What is it for?",
"name": "purpose",
"defaultValue": "HeroUI Pro",
"kind": "input"
},
{
"label": "Start date",
"name": "start",
"defaultValue": "2026-10-01",
"kind": "date-picker"
},
{
"label": "End date",
"name": "end",
"defaultValue": "2027-10-01",
"kind": "date-picker"
},
{
"label": "Volume",
"name": "volume",
"defaultValue": "5",
"kind": "select",
"options": [
{
"label": "5 seats",
"value": "5"
},
{
"label": "10 seats",
"value": "10"
}
]
}
],
"kind": "form"
} satisfies FormComponent;
```
## Switch Group
Collect a set of independent boolean preferences in a compact settings group.
### Switches
```ts
import type {SwitchGroupComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "switches",
"title": "Notifications",
"items": [
{
"defaultSelected": true,
"description": "Notify me when a high-value account changes",
"id": "alerts",
"label": "Risk alerts"
},
{
"description": "Include weekly executive summaries",
"id": "digest",
"label": "Weekly digest"
}
],
"kind": "switch-group"
} satisfies SwitchGroupComponent;
```
## Toggle Group
Offer single- or multi-select choices in a compact segmented control.
### Toggles
```ts
import type {ToggleGroupComponent} from "@heroui/agent-ui/contracts";
const contract = {
"id": "toggles",
"title": "Time range",
"defaultValue": [
"month"
],
"items": [
{
"id": "week",
"label": "Week"
},
{
"id": "month",
"label": "Month"
},
{
"id": "quarter",
"label": "Quarter"
}
],
"kind": "toggle-group",
"selectionMode": "single"
} satisfies ToggleGroupComponent;
```
# All Components
**Category**: agents
**URL**: https://heroui.pro/docs/agents/components
> Explore the hosted components HeroUI Agent can generate in a response.
Browse the four built-in groups of GenUI components the hosted Agent can emit in a response. Each
group includes validated component contracts and meaningful edge states. Because the conversation
runs in a cross-origin iframe, arbitrary customer React components and Markdown renderers do not
cross into the hosted UI.
## Agent UI Groups
## Product actions and custom components
Connect generated actions to client tools in your website today. Rendering your own React
components inside the hosted conversation is coming soon.
## How components are selected
1. A client tool returns structured data from the user's browser.
2. The hosted Agent selects a compatible component and prepares a typed payload.
3. The payload is validated against the Agent UI contract.
4. `agent-ui` renders the result with the embed's theme and interaction rules.
For direct control over the data available to generated UI, see [Client Tools](https://heroui.pro/docs/agents/api-reference/client-tools).
# Authentication
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/authentication
> Authenticate server-to-server requests with a scoped workspace API key.
Every request to the HeroUI Agents API requires a workspace API key. The request path selects one active Agent in that workspace.
Create and manage keys in the [workspace API keys dashboard](https://heroui.pro/dashboard/agents/api-keys).
## Authorization header
Use the HTTP Bearer scheme:
```http
Authorization: Bearer he_...
```
For example:
```bash
curl "https://api.heroui.pro/v1/agents/$HEROUI_AGENT_ID/conversations?limit=20" \
--header "Authorization: Bearer $HEROUI_AGENT_API_KEY" \
--header "Accept: application/json"
```
Workspace API keys are server-side secrets. Never expose one in JavaScript shipped to a browser, a
mobile application, logs, analytics, or support messages.
## Permissions
Each key has an explicit set of permissions:
| Permission | Dashboard label | Allows |
| -------------------- | ----------------------------- | ---------------------------------------------------------------------------- |
| `auth_tokens:create` | Connect the Agent to your app | Allow people to use this Agent in your app. |
| `users:read` | View users | See the people who have used this Agent. |
| `conversations:read` | View conversations | See conversations and messages between people and this Agent. |
| `runs:read` | View runs | View this Agent's run history, including status, latency, and tool activity. |
| `knowledge:read` | View knowledge | List knowledge documents and inspect their extracted markdown. |
| `knowledge:write` | Manage knowledge | Add, update, refresh, schedule, and delete knowledge documents. |
Choose the smallest set that satisfies the integration. A reporting service that reads runs, for example, does not need token creation or knowledge management access. A knowledge importer can use `knowledge:write` without permission to read conversations.
## Authentication errors
* A missing, malformed, revoked, or unknown key returns `401 unauthorized`.
* An archived, disabled, missing, or cross-workspace Agent returns the same generic `401 unauthorized` response.
* A valid key without the endpoint's required permission returns `403 insufficient_scope`.
The error message identifies the missing permission. Do not retry either response without changing the key or its permissions.
## Rotate a key
Create a replacement, deploy it to the server, verify traffic, and then revoke the old key. Revocation takes effect immediately. See [API keys](https://heroui.pro/docs/agents/configure/api-keys#rotating-a-key) for the full rotation workflow.
# Client Tools
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/client-tools
> Give HeroUI Agent typed access to browser data and actions with createToolHelper.
Client tools execute in the user's browser with the user's current session. Only each tool's name, description, JSON schema, and approval flag are sent to the hosted Agent; implementation code and shared context stay on the page.
They are one of three kinds of tool an agent can use. See [Tools](https://heroui.pro/docs/agents/configure/tools) for how client tools compare to built-in toolkits and [MCP servers](https://heroui.pro/docs/agents/configure/mcp-servers), and which to reach for.
## createToolHelper
`createToolHelper()` returns a tool factory bound to your application context type. Zod parameters provide typed arguments and runtime parsing.
Add `zod` to your own dependencies to use it in `parameters`. It ships inside `@heroui/agent`, but
strict package managers such as pnpm do not let application code import a package it did not
declare. Passing raw JSON Schema instead needs no extra dependency.
```tsx
import {HeroUIAgent, createToolHelper} from "@heroui/agent";
import {z} from "zod";
type AppContext = {
apiClient: ApiClient;
page: () => {route: string};
};
const tool = createToolHelper();
const tools = [
tool({
name: "search_users",
displayName: "Search users",
description: "Search users by name or email",
parameters: z.object({query: z.string()}),
execute: ({query}, context) => context.apiClient.searchUsers(query),
}),
];
({route: location.pathname})}}
agentId={process.env.HEROUI_AGENT_ID!}
tools={tools}
/>;
```
### Vanilla JavaScript
The CDN loader accepts the same tool shape with raw JSON Schema instead of Zod. Implementations and
context still run in the customer page:
```html
```
## ClientTool
| Property | Type | Required | Description |
| --------------- | ----------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `name` | `string` | Yes | Stable machine-readable tool name. |
| `displayName` | `string` | No | Human-readable name shown in the default tool card. |
| `description` | `string` | Yes | Clear instruction that helps the model decide when to call the tool. |
| `parameters` | `ZodType \| Record` | Yes | Zod schema or raw JSON Schema for tool arguments. |
| `execute` | `(args, context, execution) => unknown \| Promise` | Yes | Browser implementation. Returned data can feed generated UI and files. |
| `needsApproval` | `boolean` | No | Require user approval in the default `auto` permission mode. |
| `icon` | `ClientToolIcon` | No | Hosted icon identifier: `add`, `database`, `delete`, `edit`, `navigate`, `search`, `sparkles`, or `view`. |
| `iconColor` | `string` | No | Color for the hosted tool icon. |
The hosted iframe renders approval and execution states. Tool results must be JSON-serializable;
return `null` when the action has no data to return. Arbitrary React `render` callbacks cannot cross
the iframe boundary. A custom component API for tool states is coming soon; see
[Custom Components](https://heroui.pro/docs/agents/components/custom-components) for what is available today.
## Shared context
Every tool receives the same `context` object. It may contain API clients, authenticated user details, state setters, and other browser-only values. Those values never enter the iframe. The optional `page()` key is reserved: the bridge calls it on demand and sends its JSON-serializable return value as page context, limited to 16 KB.
Mark destructive or consequential actions with `needsApproval: true`. Permission modes only govern
declared client tools; they do not grant additional account, filesystem, or network access.
## Large datasets and generated files
Normal client-tool results are limited to 64 KiB. That is enough for requests such as “show the latest 100 conversations,” and the hosted **Generate files** toolkit can export those exact rows on a later turn.
For larger JSON, CSV, or TSV exports, use the third `execution` argument. `uploadDataSource` stores up to 10 MB in the current conversation and returns a small `AgentDataSource` handle:
```tsx
tool({
name: "export_conversations",
description: "Load conversations for analysis or export",
parameters: z.object({limit: z.number().int().max(10_000)}),
execute: async ({limit}, context, execution) => {
const conversations = await context.apiClient.listConversations({limit});
const dataSource = await execution.uploadDataSource({
data: conversations,
filename: "conversations.json",
format: "json",
});
return {count: conversations.length, dataSource};
},
});
```
Handles are private to the current agent and conversation. The hosted runtime validates ownership before streaming the source into its offline generator; returning a URL or forged handle does not bypass that check.
# Configuration
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/configuration
> Configure the Agent's appearance, composer, permissions, capabilities, Markdown, and start screen.
The SDK groups related options so the top-level `HeroUIAgent` API stays compact.
Prefer designing these visually? The [appearance editor](https://heroui.pro/docs/agents/configure/appearance) previews
every option below, and the embed applies what you save there without a deploy.
## Resolution order
Every option on this page can be set in two places, and the embed resolves them in this order:
1. **SDK defaults** — the values documented in the tables below.
2. **Your project's saved appearance** — whatever the [appearance editor](https://heroui.pro/docs/agents/configure/appearance) holds, fetched when the embed loads.
3. **Props on `HeroUIAgent`** — anything you write in code.
The merge runs field by field, not group by group. Overriding one color leaves the rest of the theme — and the launcher, greeting, and composer — exactly as the dashboard defines them:
```tsx
// Pins the accent. Everything else still comes from the dashboard.
```
Set [`remoteConfig`](https://heroui.pro/docs/agents/api-reference/hero-ui-agent) to `false` to skip step 2 entirely and configure the embed only from props.
Four groups are always code because the saved configuration does not carry them: `getAuthToken`
and `agentId` (identity), `tools` and `context` (functions), `onFeedback` (a callback), and
`appearance.zIndex` (the host application's layer relationship).
## Appearance
| Option | Type | Default | Description |
| ------------------------------ | ---------------------------------------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `viewMode` | `'floating' \| 'sidebar'` | `'floating'` | Open in a corner panel or a full-height right sidebar. |
| `zIndex` | `number` | `40` | Stacking level of the Agent host. The default stays below HeroUI overlays; override it when the application uses a different layer scale. |
| `shouldCloseOnInteractOutside` | `boolean` | Omitted (close) | Close the floating panel on outside interact. Set `false` to keep it open. |
| `launcher.position` | `'bottom-left' \| 'bottom-right'` | `'bottom-right'` | Corner used by the launcher and floating panel. |
| `launcher.offset` | `{x?: number; y?: number}` | Responsive | Pixel offset from the anchored side and bottom edges. Values are clamped from 0 to 200. |
| `launcher.icon` | `string` | Built in | Image URL rendered inside the launcher instead of the default spark mark. Use a square asset of at least 70x70 pixels. |
| `launcher.background` | `AgentThemeColor` | Accent | Fill of the launcher button. Useful when a custom icon needs a different backdrop; the mark inside gets a contrasting foreground automatically. |
| `launcher.style` | `CSSProperties` | None | Advanced: inline styles merged onto the launcher button, overriding the stylesheet. Use for anything the options above do not cover. |
| `panel.initialWidth` | `number \| string` | `440` | Floating panel width before expanding. A number is pixels; any CSS length also works. |
| `panel.initialHeight` | `number \| string` | `max(420px, 56dvh)` | Floating panel height before expanding. A number is pixels; any CSS length also works. |
| `panel.expanded` | `boolean` | `false` | Open the floating panel already expanded. |
| `panel.expandable` | `boolean` | `true` | Show the expand control in the panel header. Turn it off to pin the panel to one size. |
| `surfaceVariant` | `'surface' \| 'surface-secondary' \| 'outline' \| 'plain'` | `'surface-secondary'` | Surface treatment for charts, tables, maps, and other primary data surfaces. |
| `componentSurfaceVariant` | `'surface' \| 'surface-secondary' \| 'outline' \| 'plain'` | `'surface'` | Surface treatment for approval, calendar, commerce, and other response cards. |
| `theme.colorScheme` | `'light' \| 'dark' \| 'system'` | `'system'` | Color scheme selection. |
| `theme.radius` | `'sharp' \| 'soft' \| 'round' \| 'pill'` | `'round'` | Corner-radius preset across the embed. |
| `theme.colors` | `AgentThemeColors` | Built in | Override accent, background, foreground, surface, secondary surface, overlay, and tooltip colors. |
| `theme.typography` | `AgentTypography` | Built in | Set the font family and a base size clamped from 12 to 18 pixels. |
Each theme color accepts one CSS color for both schemes or `{light, dark}` values. Unset tokens retain their built-in scheme defaults. Use `overlay` for dropdown and popover surfaces; use `tooltip` for tooltip surfaces. Tooltip text contrast is derived automatically.
### Layer tokens
Use `appearance.zIndex` for the Agent's relationship with the host application. It writes the
`--ha-z-index` custom property on the Agent host. CSS-only integrations can set that property
directly. Internal surfaces expose their own tokens when an application needs to adjust the Agent's
layer order:
| Token | Default | Controls |
| ----------------------- | ------- | -------------------------------------- |
| `--ha-z-index` | `40` | Agent host relative to the application |
| `--ha-panel-z-index` | `0` | Open Agent panel inside the host |
| `--ha-launcher-z-index` | `1` | Launcher inside the host |
| `--ha-popover-z-index` | `2` | Conversation and action popovers |
| `--ha-modal-z-index` | `4` | Confirmation dialogs inside the Agent |
| `--ha-overlay-z-index` | `5` | Top-level Agent overlay portal |
The internal tokens cannot move the Agent above or below application content; use `--ha-z-index`
or `appearance.zIndex` for that boundary.
```css
:root {
--ha-z-index: 30;
--ha-modal-z-index: 6;
}
```
The `panel` options apply to `floating` mode only — a sidebar is docked full-height, and below 640 pixels the panel covers the screen. Both sizes are capped to the viewport, and expanding grows the panel in both directions, never narrower than `initialWidth`.
```tsx
```
Use `shouldCloseOnInteractOutside: false` when visitors need to interact with the host page while
the floating panel stays open—for example, selecting rows for a client tool. Escape still closes
the panel.
## Composer
| Option | Type | Default | Description |
| -------------- | --------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `attachments` | `AgentAttachmentContentType[] \| false` | All supported | Narrow accepted file types (MIME strings) or remove attachment controls. Types the hosted Agent does not support are ignored. |
| `defaultModel` | `AgentModelId` | Hosted default | Model used for new turns. Also seeds the optional picker. See [Models](https://heroui.pro/docs/agents/api-reference/models). |
| `dictation` | `boolean` | `true` | Show microphone recording and transcription controls. |
| `disclaimer` | `string \| false` | `false` | Verification notice below the composer. Supports inline Markdown links, normalizes bare domains to HTTPS, and is limited to 240 characters. |
| `modelPicker` | `boolean` | `false` | Let users choose from the hosted model allowlist. Usage is charged to the workspace's AI credits. |
| `placeholder` | `string` | `'Ask anything…'` | Composer placeholder, limited to 120 characters. |
`attachments` accepts the MIME types the hosted Agent can process: images (`image/png`, `image/jpeg`,
`image/gif`, `image/webp`), PDF, plain text, Markdown, CSV, TSV, JSON, HTML, RTF, EPUB, and the
Microsoft Office and OpenDocument formats. The list is owned by the hosted Agent and can grow
without a package update; unsupported entries are dropped rather than rejected.
## Permissions
| Option | Type | Default | Description |
| ------------- | --------------------------- | -------- | -------------------------------------------------------------------- |
| `defaultMode` | `'ask' \| 'auto' \| 'full'` | `'auto'` | Ask for every tool, only approval-gated tools, or no declared tools. |
| `showPicker` | `boolean` | `false` | Show the permission picker so the end user can change modes. |
The picker appears in the composer, beside the model picker, and offers three choices: **Ask for approval** (`ask`), **Approve for me** (`auto`), and **Full access** (`full`). A person's selection persists for the conversation.
Two things to know before enabling it:
* **It only renders when you declare `tools`.** With no client tools there is nothing to approve, so the picker stays hidden and `showPicker` has no visible effect.
* **It is a privilege boundary.** Turning it on lets a visitor select `full`, which bypasses every `needsApproval` flag on your declared tools and overrides your `defaultMode`. Leave it off when any tool can do something irreversible.
## Capabilities
| Option | Type | Default | Description |
| ------------- | --------- | ------- | ---------------------------------------------------------------------------- |
| `webSearch` | `boolean` | `false` | Allow the hosted Agent to search the public web for current information. |
| `imageSearch` | `boolean` | `true` | Allow image results when web search is enabled. |
| `newsSearch` | `boolean` | `false` | Allow recent news results with publication dates when web search is enabled. |
Web results are treated as untrusted external content and kept separate from client-tool datasets.
## Start screen
| Option | Type | Default | Description |
| ----------------- | ---------- | -------------------------------------------------- | -------------------------------------------------------------- |
| `greeting` | `string` | `'Ask about your data'` | Empty-conversation heading, limited to 120 characters. |
| `subtitle` | `string` | `'Live answers with charts, metrics, and tables.'` | Supporting text below the greeting, limited to 240 characters. |
| `prompts` | `string[]` | — | Up to five suggested prompts, each limited to 160 characters. |
| `promptShortcuts` | `boolean` | `false` | Enable `Ctrl+1` through `Ctrl+5` for visible prompts. |
## Streaming Markdown
| Option | Type | Default | Description |
| ---------- | ------------------------------------------ | ------------- | ------------------------------------------------------------ |
| `animated` | `boolean \| AgentMarkdownAnimationOptions` | Fade-in words | Animate newly streamed content or provide animation options. |
| `caret` | `'block' \| 'circle' \| false` | `'block'` | Streaming caret style. |
Markdown is rendered by the hosted iframe. React renderer callbacks are not supported across the
iframe boundary; add reusable visuals to the hosted declarative component catalog instead.
# Errors
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/errors
> Handle consistent JSON errors and HTTP status codes.
The API uses standard HTTP status codes. Error responses have a stable JSON shape:
```json
{
"name": "insufficient_scope",
"message": "This API key does not have the required conversations:read permission."
}
```
## Status codes
| Status | Name | Meaning |
| ------ | ------------------------ | ------------------------------------------------------------------ |
| `400` | `bad_request` | A path or query parameter is invalid. |
| `401` | `unauthorized` | The API key is missing, invalid, or revoked. |
| `403` | `insufficient_scope` | The key is valid but lacks the operation's permission. |
| `404` | `not_found` | The requested resource does not exist for this Agent. |
| `409` | `conflict` | The operation conflicts with the document's current state. |
| `413` | `payload_too_large` | An uploaded knowledge file exceeds 10 MB. |
| `415` | `unsupported_media_type` | The request or knowledge file uses an unsupported media type. |
| `429` | `rate_limited` | A key, Agent, or source IP exceeded its per-minute request limit. |
| `500` | `internal_server_error` | The request failed unexpectedly. |
| `503` | `service_unavailable` | The API is temporarily unavailable; the request was not processed. |
`401` responses include `WWW-Authenticate: Bearer`. A `403` response includes an
`insufficient_scope` Bearer challenge naming the permission the key needs.
## Retry behavior
Retry `500` and `503` failures with capped exponential backoff and jitter. For `429` and `503` responses, wait at least the number of seconds in the `Retry-After` header before sending another request. A fail-closed `503` caused by unavailable rate-limit storage returns `Retry-After: 60`.
Do not automatically retry `400`, `401`, `403`, `404`, `413`, or `415` responses. Correct the request, supply a valid key, or add the required permission first. For `409`, retrieve the document and retry only after its state changes.
A `404` response does not reveal whether a resource exists for a different Agent. Resources are
always resolved within the Agent associated with the API key.
# HeroUIAgent
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/hero-ui-agent
> Mount and configure the hosted HeroUI Agent from a React application.
`HeroUIAgent` creates a secure, cross-origin iframe on `document.body`. The hosted iframe renders the
launcher, conversation, and generated UI; the React component only manages the customer-page
bridge. Render it once near your application root. It does not wrap your component tree.
For websites without React, use `agent.heroui.pro/loader.js` and
`window.HeroUIAgent.mount()`. See the [vanilla quickstart](https://heroui.pro/docs/agents/quickstart#vanilla-javascript).
```tsx
import {HeroUIAgent} from "@heroui/agent";
const getAuthToken = async (context) => {
const response = await fetch("/api/heroui-agent/auth-token", {
body: JSON.stringify(context),
headers: {"Content-Type": "application/json"},
method: "POST",
});
if (!response.ok) throw new Error("Agent authentication failed");
return response.json();
};
export function AppAgent() {
return ;
}
```
Every visual option below has a counterpart in the [dashboard appearance editor](https://heroui.pro/docs/agents/configure/appearance), and the embed applies what you saved there. Props override it field by field, so you only write the ones you want pinned in code. See [Resolution order](https://heroui.pro/docs/agents/api-reference/configuration#resolution-order).
## Props
| Prop | Type | Default | Description |
| ---------------------------- | ------------------------------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `getAuthToken` | `(context: GetAuthTokenContext) => Promise` | Required | Calls your server endpoint for a short-lived browser token. |
| `agentId` | `string` | Required | Agent identifier. |
| `appearance` | `AgentAppearance` | — | View mode, launcher placement, and theme. |
| `capabilities` | `AgentCapabilities` | — | Optional hosted capabilities such as web search. |
| `componentExports` | `AgentComponentExportFormat[] \| false` | `['csv', 'svg', 'png']` | Formats available from generated charts and tables. Pass `false` to hide export controls. |
| `composer` | `AgentComposerOptions` | — | Attachments, dictation, disclaimer, and placeholder. |
| `context` | `AgentSharedContext` | — | Browser-only shared context passed to client tools. The reserved `page` function supplies turn context. |
| `markdown` | `AgentMarkdownOptions` | — | Streaming animation and caret preferences. |
| `onFeedback` | `(feedback: AgentResponseFeedback) => void \| Promise` | — | Receives end-user ratings and optional negative-feedback details. |
| `onReady` | `() => void` | — | Fires when project configuration and the complete composer are ready for the current conversation. |
| `permissions` | `AgentPermissionOptions` | — | Default client-tool permission mode and optional end-user picker. |
| `preload` | `boolean` | `true` | Warm authentication and the chat runtime while closed. Appearance still resolves on mount when `false`. |
| `reopenOnRefresh` | `boolean` | `false` | Reopen after a refresh at the start screen instead of resuming the previous conversation. |
| `remoteConfig` | `boolean` | `true` | Apply the appearance saved for this project in the dashboard. Props still win. Pass `false` to opt out. |
| `responseActions` | `AgentMessageAction[] \| false` | `['copy', 'feedback', 'retry']` | Actions shown below assistant responses. |
| `showLauncher` | `boolean` | `true` | Show the floating launcher after its presentation configuration resolves. |
| `showBetaBadge` | `boolean` | `false` | Show a compact Beta chip in the open panel header, next to the assistant title. |
| `startNewConversationOnOpen` | `boolean` | `false` | Open on the start screen instead of restoring the most recently active conversation. |
| `startScreen` | `AgentStartScreenOptions` | — | Greeting, subtitle, suggested prompts, and prompt shortcuts. |
| `tools` | `ClientTool[]` | `[]` | Browser client tools the hosted Agent may call. |
## Preloading
The Agent resolves its remote presentation configuration as soon as it mounts. The built-in launcher
and panel wait for that snapshot, so controls such as attachments, permissions, model selection, and
the disclaimer do not appear later and shift the layout. If the configuration is unavailable, the
embed falls back to its defaults after a short, bounded wait.
By default, authentication and the chat runtime warm up in parallel while the panel is closed. Calls
to `show()`, `toggle()`, or `newConversation()` made before presentation configuration resolves are
queued instead of painting a temporary panel. Calling `hide()` or toggling closed during that window
cancels the pending open.
This is on by default. You do not need to configure anything.
```tsx
// Warms up on its own — nothing to add.
```
Turn it off when most visitors never open the Agent, or when you would rather choose the moment yourself:
```tsx
```
With `preload={false}`, the small remote presentation request still runs on mount so the first visible
surface is layout-stable. Authentication and the chat runtime remain deferred until someone opens the
panel or calls `preload()`.
### Preloading on your own signal
`useAgent().preload()` runs the same warm-up on demand, without opening the panel. Pair it with `preload={false}` to warm up at the moment you think someone is about to ask something:
```tsx
"use client";
import {useAgent} from "@heroui/agent";
export function PricingTable() {
const agent = useAgent();
// Someone comparing plans is likely to have a question.
return {/* … */};
}
```
`preload()` is safe to call as often as you like — the work happens once.
## Readiness
Use `onReady` for a lifecycle event, or the reactive `useAgent().ready` status when a custom entry
point should wait for the complete authenticated runtime. Waiting is optional: controller actions are
safe to call earlier and queue while presentation configuration resolves.
```tsx
function AgentEntryPoint() {
const agent = useAgent();
return (
);
}
analytics.track("agent_ready")}
agentId={process.env.HEROUI_AGENT_ID!}
getAuthToken={getAuthToken}
/>;
```
## Reopening after refresh
Set `reopenOnRefresh` to preserve whether the panel is open for the current browser tab. When the
visitor refreshes the page, an open panel returns to the start screen as a new chat; it does not
resume the conversation that was active before the refresh.
```tsx
```
Set `startNewConversationOnOpen` when every launcher open should begin at the start screen. Existing
conversations remain available from the conversation picker.
```tsx
```
## Floating panel dismiss
In `appearance.viewMode: "floating"`, the desktop panel closes when the visitor clicks outside it
unless you opt out with `appearance.shouldCloseOnInteractOutside`. Omitted or `true` always closes;
`false` keeps it open. Escape, the header close button, and other dismiss paths are unchanged.
```tsx
```
See [Configuration — Appearance](https://heroui.pro/docs/agents/api-reference/configuration#appearance) for every
`appearance` option.
## Full configuration
```tsx
```
See [Configuration](https://heroui.pro/docs/agents/api-reference/configuration) for the nested option types and defaults.
# Introduction
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference
> Read Agent activity and manage the files and URLs in its knowledge base.
The HeroUI Agents API lets your backend query Agent activity and manage knowledge documents. Workspace API keys work across the workspace, while every request path selects exactly one Agent.
This API is for server-to-server requests. To embed an Agent in your product, use the [web
embed](#web-embed) instead.
## Base URL
Send all production requests to:
```text
https://api.heroui.pro/v1
```
Paths in this reference are relative to that URL. The API uses HTTPS and returns JSON, except for the extracted-content endpoint, which returns Markdown.
## Authentication
Pass a workspace API key as a Bearer token in the `Authorization` header and include the Agent ID in the path:
```bash
curl "https://api.heroui.pro/v1/agents/$HEROUI_AGENT_ID/users?limit=20" \
--header "Authorization: Bearer $HEROUI_AGENT_API_KEY" \
--header "Accept: application/json"
```
The key must include the permission required by the endpoint. Keep it in a secret manager and make requests from your backend only. See [Authentication](https://heroui.pro/docs/agents/api-reference/authentication) for the complete scope model.
Never put a workspace API key in your product's browser code. The documentation playground is for
deliberate testing only and keeps a pasted key in the current browser tab.
## Resources
All list endpoints use [cursor pagination](https://heroui.pro/docs/agents/api-reference/pagination). Failed requests use a consistent [error shape](https://heroui.pro/docs/agents/api-reference/errors), and requests are subject to [usage limits](https://heroui.pro/docs/agents/api-reference/usage-limits).
## Permissions
| Permission | Dashboard label | Allows |
| -------------------- | ------------------ | ---------------------------------------------------------------------------- |
| `users:read` | View users | See the people who have used this Agent. |
| `conversations:read` | View conversations | See conversations and messages between people and this Agent. |
| `runs:read` | View runs | View this Agent's run history, including status, latency, and tool activity. |
| `knowledge:read` | View knowledge | List knowledge documents and inspect their extracted markdown. |
| `knowledge:write` | Manage knowledge | Add, update, refresh, schedule, and delete knowledge documents. |
Only assign the permissions that a server integration needs. Permissions apply to the operations a key can perform across Agents in its workspace; they never grant Agent-definition administration.
## OpenAPI document
The OpenAPI 3.1 description is available at [`/.well-known/openapi/heroui-agents-api.json`](/.well-known/openapi/heroui-agents-api.json). It is also advertised in the site's [API catalog](/.well-known/api-catalog).
## Web embed
Use `agent.heroui.pro/loader.js` on any modern website. React applications can instead import the
typed host bridge from `@heroui/agent`, or its Next.js entry point from `@heroui/agent/next`. All
three integrations render the same hosted iframe and expose client-side tools and controls.
### Package entry points
| Entry point | Use |
| ---------------------------- | ---------------------------------------------------------------------- |
| `agent.heroui.pro/loader.js` | Framework-independent browser bridge. |
| `@heroui/agent` | React component, hook, tool helper, model constants, and public types. |
| `@heroui/agent/next` | Next.js-compatible mirror of the React bridge. |
| `@heroui/agent/server` | Server-only managed-key exchange helper. |
See the [Quickstart](https://heroui.pro/docs/agents/quickstart) for a complete embed integration.
# Models
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/models
> Choose the model HeroUI Agent runs and let end users switch between hosted models.
HeroUI Agent runs only models in its hosted catalog. Use `composer.defaultModel`
to choose the initial model and `composer.modelPicker` to let end users change it.
Each turn is charged to the workspace's AI credits based on the model's actual usage.
```tsx
```
## Model catalog
| ID | Label | Provider | Tier |
| --------------------------- | ---------------- | ----------- | ------- |
| `moonshotai/Kimi-K3` | Kimi K3 | Moonshot AI | Light |
| `openai/gpt-5.6-luna` | GPT-5.6 Luna | OpenAI | Light |
| `openai/gpt-5.6-terra` | GPT-5.6 Terra | OpenAI | Codegen |
| `openai/gpt-5.6-sol` | GPT-5.6 Sol | OpenAI | Complex |
| `google/gemini-3.8-flash` | Gemini 3.8 Flash | Google | Light |
| `anthropic/claude-sonnet-5` | Claude Sonnet 5 | Anthropic | Codegen |
| `anthropic/claude-opus-4.8` | Claude Opus 4.8 | Anthropic | Complex |
Retired Gemini 3.5 Flash, Gemini 3.6 Flash, and Gemini 3.7 Flash ids resolve to Gemini 3.8 Flash.
# Pagination
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/pagination
> Traverse list endpoints with opaque cursors.
List endpoints return one page at a time. Use `limit` to control the page size and `after` to continue from the cursor returned by the previous response.
## Query parameters
| Parameter | Type | Description |
| --------- | ------- | --------------------------------------------------------------------- |
| `limit` | integer | Number of objects to return. Defaults to `20` and is capped at `100`. |
| `after` | string | Opaque cursor from the previous page's `next_cursor`. |
The first request does not need an `after` value:
```bash
curl "https://api.heroui.pro/v1/agents/$HEROUI_AGENT_ID/conversations?limit=100" \
--header "Authorization: Bearer $HEROUI_AGENT_API_KEY"
```
## List response
Every list endpoint returns the same envelope:
```json
{
"object": "list",
"data": [],
"has_more": true,
"next_cursor": "opaque_cursor"
}
```
* `data` contains the resources in the current page.
* `has_more` indicates whether another page is available.
* `next_cursor` is the value to send as `after` on the next request. It is `null` on the final page.
## Request the next page
```bash
curl "https://api.heroui.pro/v1/agents/$HEROUI_AGENT_ID/conversations?limit=100&after=opaque_cursor" \
--header "Authorization: Bearer $HEROUI_AGENT_API_KEY"
```
Continue until `has_more` is `false`. Treat cursors as opaque values: do not parse them, modify them, or use a resource ID in their place.
# Usage limits
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/usage-limits
> Understand per-key and per-Agent request limits and retry safely.
The HeroUI Agents API enforces the following limits:
| Scope | Limit |
| ----------------------------------- | --------------------------- |
| Per API key | `120` requests per minute |
| Per Agent | `600` requests per minute |
| Per source IP during authentication | `1,200` requests per minute |
The per-key limit is workspace-wide, while the per-Agent limit is shared by every workspace key targeting that Agent. Adding Agents cannot multiply a key's allowance. The source-IP limit protects key verification before a key can be resolved. A rejected request returns `429 rate_limited` with a `Retry-After` header containing the number of seconds to wait.
## Handle rate limits
1. Read the `Retry-After` header.
2. Wait at least that many seconds.
3. Retry with exponential backoff and jitter if another `429` is returned.
4. Bound the number of retries so a downstream outage cannot create an infinite loop.
Avoid sending simultaneous retries from every worker. Centralized throttling or a queue makes it easier to stay within these limits.
## Reduce request volume
* Request up to `100` objects per page when processing complete datasets.
* Follow `next_cursor` sequentially instead of requesting the same page again.
* Cache immutable results when that fits your data freshness requirements.
* Poll a processing knowledge document with exponential backoff, then stop when `status` becomes `ready` or `failed`.
The limits apply across users, conversations, runs, and knowledge operations rather than separately to each resource.
# useAgent
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/use-agent
> Control a mounted HeroUI Agent from any component in your React tree.
`useAgent(agentId?)` returns imperative controls for the embedded Agent. It works without an ancestor provider because `HeroUIAgent` mounts independently from the host component tree.
```tsx
"use client";
import {useAgent} from "@heroui/agent";
export function AskAgentButton() {
const agent = useAgent();
return ;
}
```
Pass an agent ID when more than one Agent is mounted on the page:
```tsx
const agent = useAgent(process.env.HEROUI_AGENT_ID!);
```
## AgentController
`ready` is reactive: components using it re-render when project configuration and the complete
composer are ready for the current conversation. It reports full runtime readiness; you do not need
to wait for it before calling the controller methods.
| Member | Description |
| -------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `ready` | `true` when configuration and the complete composer are ready for the current conversation. |
| `show()` | Open the Agent panel. |
| `hide()` | Close the Agent panel. |
| `toggle()` | Toggle the panel between open and closed. |
| `newConversation(prompt?)` | Start a fresh conversation. A non-empty prompt is submitted immediately, bypassing the start screen. |
| `preload()` | Warm the Agent up without opening it, so the next open is immediate. Safe to call repeatedly. |
| `refreshAuth()` | Mint a new credential from `getAuthToken`. Use after login so the person is identified right away. |
| `shutdown()` | Clear the active credential, the local conversation state, and the browser's anonymous id. Use during logout. |
Calling a method before the matching `HeroUIAgent` has mounted is safe. The call is ignored and a warning is written to the browser console.
After `HeroUIAgent` mounts, calls to `show()`, `toggle()`, or `newConversation()` made while remote
presentation configuration is resolving are queued. They do not render a temporary panel with
default controls. Call `hide()` or toggle closed before resolution finishes to cancel the pending
open.
Pass a prompt when an entry point already captured the user's intent, such as a command palette or
contextual action:
```tsx
agent.newConversation("Compare this quarter's revenue with last quarter");
```
The panel opens directly on the new conversation with the user message submitted. Calling
`newConversation()` without a prompt keeps the normal start-screen behavior.
## Preloading on demand
The Agent already preloads itself by default, so most integrations never call `preload()`. It exists for embeds that opted out with [`preload={false}`](https://heroui.pro/docs/agents/api-reference/hero-ui-agent#preloading) and want to pick the moment instead — a visitor reaching your pricing page, scrolling to your FAQ, or pausing on a form.
```tsx
const agent = useAgent();
useEffect(() => {
const timer = setTimeout(agent.preload, 5000);
return () => clearTimeout(timer);
}, [agent]);
```
Unlike `show()`, this never opens the panel or takes focus.
## Login and logout
`refreshAuth()` and `shutdown()` are the two halves of [identifying users](https://heroui.pro/docs/agents/identifying-users): one picks up the session you just created, the other retires it.
```tsx
const agent = useAgent();
const handleLogin = async () => {
await signIn();
agent.refreshAuth();
};
const handleLogout = async () => {
await signOut();
agent.shutdown();
};
```
## Custom launcher
Hide the built-in launcher when your application owns the entry point:
```tsx
function AgentEntryPoint() {
const agent = useAgent();
return ;
}
;
```
Add `disabled={!agent.ready}` when you want to keep the custom launcher unavailable until the
authenticated runtime and complete composer are ready. It is not required to prevent a partial
layout: early opens are queued until presentation configuration resolves.
# Examples
**Category**: agents
**URL**: https://heroui.pro/docs/agents/examples
> Explore HeroUI Agent in complete product experiences with realistic data, tools, and actions.
Each example is a self-contained product surface with its own datasets, client tools, suggested prompts, and Agent configuration. Open the gallery to compare every experience, or jump directly to a demo.
# Introduction
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started
> Premium composable React Native components that extend HeroUI Native — built for teams who want to ship beautiful, production-ready mobile apps fast.
HeroUI Native Pro is a premium extension of [HeroUI Native OSS](https://heroui.com/docs/native/getting-started) — production-ready, fully composable React Native components that follow the same patterns you already know from HeroUI Native OSS. New components and fixes ship regularly as package updates.
## What's Included
* **React Native components** — Calendar, DatePicker, DateRangePicker, Stepper, SlideButton, ProgressButton, SocialAuthButton, NumberField, NumberStepper, RadioButtonGroup, and more — each with full documentation
* **Same design system** — Built on the same [Uniwind](https://uniwind.dev/) + [Tailwind CSS v4](https://tailwindcss.com/) theming, color tokens, and styling patterns as HeroUI Native OSS
* **Smooth animations** — All built on [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/) with a unified `animation` prop
* **Figma** — Design files for all Pro components
* **AI tooling** — MCP server, agent skills, and design taste for AI-assisted development
* **Priority support** — Fast support, prioritized issues, private Discord, and a VIP badge
## Next Steps
* [Installation](https://heroui.pro/docs/native/getting-started/installation) — Set up HeroUI Native Pro in your project
* [Browse Components](https://heroui.pro/docs/native/components) — See all available Pro components
* [Agent Skills](https://heroui.pro/docs/native/getting-started/agent-skills) — Set up AI tools for HeroUI Native Pro development
## Acknowledgements
Built on [HeroUI Native OSS](https://heroui.com/docs/native/getting-started), [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/), [React Native Gesture Handler](https://docs.swmansion.com/react-native-gesture-handler/), [Tailwind CSS v4](https://tailwindcss.com/), [Uniwind](https://uniwind.dev/), and [Expo](https://expo.dev/).
# All Components
**Category**: native
**URL**: https://heroui.pro/docs/native/components
> Explore the full list of components available in HeroUI Native. More are on the way.
## Buttons
## Charts
## Data Display
## Date and Time
## Feedback
## Forms
## Navigation
# 1.0.0-beta.1
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-1
> HeroUI Native Pro beta 1 introduced 17 premium mobile components, unified date and time controls, charts, and AI-ready tooling.
April 2026
The first public beta of **HeroUI Native Pro** is here. Seventeen premium React Native components, a unified date & time stack, new interaction patterns like slide-to-confirm and hold-to-confirm, a Skia-powered line chart, and a brand-new toolkit for AI-assisted native development — all built on the same foundations you already know from HeroUI Native OSS.
## Overview
HeroUI Native Pro (`heroui-native-pro`) is a premium extension of [HeroUI Native OSS](https://heroui.com/docs/native/getting-started). This beta ships production-ready components across 6 categories:
* **Date & Time** — Calendar, RangeCalendar, DateField, DatePicker, DateRangePicker
* **Charts** — LineChart
* **Buttons** — SlideButton, ProgressButton, SocialAuthButton
* **Forms** — NumberField, NumberStepper, RadioButtonGroup
* **Navigation** — Stepper, SplitView
* **Feedback** — Rating, NumberValue, TrendChip
Everything composes on top of `heroui-native`, uses the same theme tokens, the same compound pattern, the same `animation` prop, and the same Uniwind + Tailwind CSS v4 styling system. If you already build with HeroUI Native OSS, you already know how to use Pro.
## Try It on Your Device
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## Built on HeroUI Native OSS
HeroUI Native Pro inherits every core principle from HeroUI Native OSS. Same mental model, same API surface, same everything — just more components:
* **Compound components** — Every Pro component uses dot-notation primitives (`Calendar.Grid`, `Stepper.Step`, `DateField.Trigger`) so you can style, reorder, or replace any internal slot.
* **Shared theme tokens** — `accent`, `surface`, `foreground`, `muted`, `success`, `danger` — the same semantic tokens resolve identically in Pro components.
* **Uniwind + Tailwind CSS v4** — Style with familiar utility classes. No separate styling system, no new learning curve.
* **Unified `animation` prop** — Every animated Pro component exposes the same `animation` API. Disable, customize, or cascade per-component, per-subtree, or app-wide — same as OSS.
* **Reduce Motion out of the box** — Respects the system `Reduce Motion` setting automatically. No extra wiring.
* **Adaptive presentation modes** — DateField, DatePicker, and DateRangePicker switch between `popover`, `dialog`, and `bottom-sheet` with a single prop, matching the pattern established by `Select` and `Menu` in OSS.
* **Granular imports** — Import from the root or from per-component subpaths. Ship only what you use.
```tsx
import { Calendar, DatePicker, Stepper } from "heroui-native-pro";
```
The [Agent Skills](https://heroui.pro/docs/native/getting-started/agent-skills) and [MCP Server](https://heroui.pro/docs/native/getting-started/mcp-server) know about both libraries at once.
## Date & Time
A full, compound date & time stack. Five components that compose the same primitives — a shared `Calendar` / `RangeCalendar` engine, a masked `DateField` input, and trigger-based `DatePicker` and `DateRangePicker` variants. All five are built on [`@internationalized/date`](https://react-spectrum.adobe.com/internationalized/date/), so locale, time zone, and non-Gregorian calendars are first-class.
Every date component ships with:
* Controlled + uncontrolled modes with `value` / `defaultValue`
* `minValue` / `maxValue` and `isDateUnavailable` for constraints
* Optional year-picker overlay for fast long-range navigation
* BCP 47 locale support (Gregorian, Buddhist, Indian, Persian, and more)
* `isRequired`, `isInvalid`, and `FieldError` integration for forms
* `popover`, `dialog`, and `bottom-sheet` presentations on picker-based variants
### Calendar
A single-date calendar with month navigation, internationalization, year picker, and fully customizable day cells (including render-prop driven `CellIndicator` for event dots).
### RangeCalendar
Two-tap start/end date range selection with a highlight strip, `allowsNonContiguousRanges` for split bookings, and the same cell render props as `Calendar`.
### DateField
A masked `dd/mm/yyyy` input with an optional calendar popup in the trailing suffix slot. Supports `"masked"` and `"loose"` input modes, keyboard dismissal on trigger press, and full form-field integration with `Label`, `Description`, and `FieldError`.
`DatePicker` and `DateRangePicker` pair the same calendar engines with a trigger-first layout — a pressable `Trigger` that opens the overlay and a `Value` slot that displays the formatted selection. See the [component docs](https://heroui.pro/docs/native/components) for full API details.
## Charts
### LineChart
A Skia-powered line chart for visualizing trends over time. Built on `victory-native` and wrapped in a themed outer `View`, with compound primitives for static and draw-on animated lines, a crosshair that tracks chart presses, and a tooltip indicator dot. Multi-series, sparkline, and custom tooltip layouts all compose from the same building blocks.
* Compound parts — `LineChart.Line`, `LineChart.AnimatedLine`, `LineChart.Tooltip`, `LineChart.Crosshair`
* `curveType` — `natural` cubic spline or `linear` segments
* Replayable draw-on animation via `resetKey` with configurable timing/spring config
* Cascading `animation="disable-all"` to skip path interpolation across the entire subtree
* Render-prop children expose `points` and `chartBounds` for fully custom overlays
* Themed stroke colors via `colorClassName` (e.g. `accent-chart-1`, `accent-chart-3`)
## Navigation
### SplitView
A vertical split layout with a draggable divider between a top and bottom section. Snap points can be defined as ratios of the container height (`0`..`1`) or absolute pixel values (`> 1`). The top section animates between snaps with spring physics and supports:
* Controlled (`snapIndex` / `onSnapIndexChange`) and uncontrolled usage
* Render-prop children for reading animated layout values (`topSectionHeight`, `minPx`, `maxPx`)
* `useSplitView` hook for triggering `snapTo` programmatically from any descendant
* Extended hit target on `SplitView.DragArea` so the drag handle is easy to grab
* Cascading `animation="disable-all"` for the entire subtree
Useful for map-over-list screens, drawer-style inspectors, or any "pull up a panel" interaction.
### Stepper
A multi-step progress indicator for sequential workflows — onboarding, checkout, multi-step forms. Every step is a pressable `Stepper.Step` that automatically receives its index and one of three statuses: `inactive`, `active`, or `complete`. The default rail renders an animated indicator circle with a check-draw-in animation on completion and a separator fill that animates along the progress.
* Controlled (`currentStep` / `onStepChange`) and uncontrolled modes
* `orientation` — vertical or horizontal
* Fully customizable compound parts — `Indicator`, `IndicatorCheck`, `IndicatorNumber`, `Separator`, `SeparatorTrack`, `SeparatorFill`, `Title`, `Description`
* Per-step color overrides via `data-status` attributes
## Buttons
Two interaction patterns that are common in native apps but painful to build from scratch: **slide-to-confirm** and **hold-to-confirm**. Both guard against accidental taps for destructive or high-intent actions, both support `variant` (`accent`, `success`, `danger`, etc.), `autoReset`, and controlled completion state.
### SlideButton
A pan-gesture driven slider. Two content layers — an `UnderlayContent` that peels away as the thumb moves, and an `OverlayContent` that clip-reveals from left to right. Built for "slide to unlock", "slide to approve", or "slide to delete" flows.
### ProgressButton
A press-and-hold button. An absolutely positioned `Overlay` sweeps left-to-right on press, with an inverted-color `MaskLabel` that counter-translates to keep the label aligned — producing a clean color-wipe effect. Use `holdDuration` to tune how long the user must hold, and `autoReset` + `autoResetDelay` to return to idle.
### SocialAuthButton
A specialized button that renders a provider-specific icon and label for social sign-in flows. Pass a `provider` (`google`, `apple`, `github`, `facebook`, and more) and get the correct icon, label, and styling automatically.
## Forms
### NumberStepper
A compact numeric stepper with a decrement button, a current-value display with direction-aware flip animations, and an increment button. Auto-disables the relevant button at the `minValue` / `maxValue` boundary and supports custom `step` intervals (including decimals like `0.5`).
```tsx
```
Typical use cases: cart quantity selectors, item counters, compact numeric toggles in settings. The render-prop API (`{({ isAtMin }) => ...}`) lets you swap the decrement icon to a trash can when the value reaches the floor — a common pattern for "remove from cart" interactions.
### NumberField
A full numeric input with inline increment/decrement buttons anchored to the input edges. Supports long-press repeat on the buttons, `Intl` number formatting, min/max/step constraints, and full form-field integration (`Label`, `Description`, `FieldError`). Use this when you need a text-entry numeric field; use `NumberStepper` for purely button-driven counters.
### RadioButtonGroup
A compound radio group that wraps HeroUI Native's `RadioGroup` primitive with styled item rows. Supports group-level `variant` (`primary` / `secondary`) and flexible item content via `ItemContent`.
## Feedback
### Rating
A star rating input built on top of `heroui-native`'s `RadioGroup`. Auto-renders items from `1` to `maxValue` when children are omitted, supports fractional read-only display (e.g. `3.7` stars), fully swappable icons, and controlled/uncontrolled modes. Exposes a render-function `Item` for completely custom indicators.
```tsx
```
* `size` — `sm`, `md`, `lg`
* `isReadOnly` for display-only rendering with fractional fills
* Custom icons via the `icon` prop or `Rating.Item` render children
* Full form-field integration through the underlying `RadioGroup`
Pair it with `TrendChip` in a review summary, or drop it straight into a feedback form.
### NumberValue
A formatted number display with locale-aware rendering. Wraps the runtime's built-in `Intl.NumberFormat` to produce decimals, currency, percentages, and compact notation (`1.2K`, `3.4M`) with a consistent API. The root auto-renders the value when no children are provided, or composes with `Prefix`, `Value`, and `Suffix` primitives for richer layouts.
```tsx
approx users
```
Works out of the box on modern Hermes/JSC runtimes; for advanced options (`notation: "compact"`, `signDisplay`, non-default locales) the docs walk through the FormatJS polyfill setup so behavior matches the web exactly.
### TrendChip
A compact chip for financial and metrics dashboards. Built on top of `heroui-native`'s `Chip` — pass a `trend` prop (`up`, `neutral`, `down`) and the chip automatically derives its color (success, warning, danger), arrow direction, and icon. Digits render with tabular figures so percentages align vertically across stacked chips.
```tsx
+12.4%0.0%-3.2%
```
Supports `variant`, `size`, an optional `Prefix` and `Suffix` for `$`, `%`, or `vs last month` labels, and a render-prop `Indicator` for fully custom SVG arrows.
## AI Tooling
HeroUI Native Pro ships with a complete toolkit for AI-assisted mobile development. Three pieces that work together:
### MCP Server
A remote MCP server (`https://native-mcp.heroui.pro/mcp`) that gives AI assistants live access to every Pro component's docs, props, theme variables, and setup guides. Supported in Cursor, Claude Code, VS Code Copilot, Windsurf, and Zed.
```json title=".cursor/mcp.json"
{
"mcpServers": {
"heroui-native-pro": {
"url": "https://native-mcp.heroui.pro/mcp",
"headers": {
"x-heroui-personal-token": "HEROUI_PERSONAL_TOKEN"
}
}
}
}
```
### Native Pro Skill
An installable knowledge pack that teaches your agent `heroui-native-pro` conventions — compound component rules, MCP tool routing, Uniwind styling, Reanimated gotchas, and the common mistakes that produce broken native UIs.
```bash
curl -fsSL https://heroui.pro/docs/install | HEROUI_PERSONAL_TOKEN=your_token bash -s -- heroui-native-pro
```
### Design Taste Skill
A second skill focused purely on visual polish — spacing, typography, color token usage, card anatomy, form layout, button hierarchy, and mobile-native navigation patterns. 78 design principles learned from iterative human feedback, so your agent stops producing generic layouts and starts producing production-quality ones.
```bash
curl -fsSL https://heroui.pro/docs/install | HEROUI_PERSONAL_TOKEN=your_token bash -s -- heroui-pro-design-taste
```
**Skills teach, MCP does.** Install all three for the best results — your agent writes correct code, with live documentation access, in a polished design system. Learn more in the [UI for Agents overview](https://heroui.pro/docs/native/getting-started/overview).
## What's Next
This is `beta.1`. We're polishing APIs, adding more components, and squashing edge cases as they surface. Feedback during beta shapes the `1.0.0` stable release — please [file issues](https://github.com/heroui-inc/heroui-native-pro/issues) and share what you're building.
## Links
* [All Components](https://heroui.pro/docs/native/components)
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [MCP Server](https://heroui.pro/docs/native/getting-started/mcp-server)
* [Agent Skills](https://heroui.pro/docs/native/getting-started/agent-skills)
* [Design Taste](https://heroui.pro/docs/native/getting-started/design-taste)
* [Roadmap](https://herouinative.featurebase.app/roadmap)
# 1.0.0-beta.10
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-10
> Adds Table, Carousel, MorphButton, and PhoneNumberField, plus a theme-aware background layer for carousel navigation buttons.
August 21, 2026
The tenth beta of **HeroUI Native Pro** ships four new surfaces — a compound `Table` for selectable, sortable tabular data, a `Carousel` snap pager with navigation, interpolating dots, and thumbnails, a `MorphButton` that springs between collapsed and expanded content, and a `PhoneNumberField` with per-country as-you-type formatting. Carousel Previous/Next buttons also pick up a theme-aware background layer so glass nav chevrons frost correctly.
## Try It on Your Device
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## What's New
### New Components
This release introduces **4 new** components across data display, buttons, and forms:
* **Table**: Structured tabular data with row selection, controlled sorting, column-width alignment, and an opt-in virtualized body. ([Documentation](https://heroui.pro/docs/native/components/table))
* **Carousel**: Horizontal snap pager with navigation buttons, interpolating dots, a thumbnail strip, and `useCarousel()` for custom scroll-driven indicators. ([Documentation](https://heroui.pro/docs/native/components/carousel))
* **MorphButton**: Pressable that springs between auto-measured collapsed and expanded content toward one of eight RTL-aware directions. ([Documentation](https://heroui.pro/docs/native/components/morph-button))
* **PhoneNumberField**: International phone input with per-country as-you-type formatting, a searchable country picker, smart paste, and E.164 output. ([Documentation](https://heroui.pro/docs/native/components/phone-number-field))
#### Table
A compound data table for lists that need columns, selection, and sort without a web-style grid. Compose a header of columns and a body of rows, wrap wide content in a horizontal scroll container, and keep summaries or load-more actions in a footer outside that scroll. The table never reorders data itself — mark columns with `allowsSorting`, sort from `sortDescriptor`, and pass the result back in.
**Features:**
* Compound parts — `Table.ScrollContainer`, `Table.Content`, `Table.Header`, `Table.Column`, `Table.Body`, `Table.Row`, `Table.Cell`, `Table.SelectAllCell`, `Table.SelectionCell`, `Table.Footer`, and `Table.Background`
* `none` / `single` / `multiple` selection, with row press toggling selection when the mode is not `"none"`
* Controlled sorting via `sortDescriptor` / `onSortChange`; the table never mutates your items array
* Column widths (`width`, or `flex` + `minWidth`) seeded before first paint so header and body cells align without a layout flash
* `items` render-function body, `renderEmptyState`, and an opt-in `virtualized` `FlatList` body for large collections
* `primary` / `secondary` variants and a theme-aware `background` layer on the shell (glass renders a blur)
**Usage:**
```tsx
import { Chip } from "heroui-native";
import { Table } from "heroui-native-pro";
const MEMBERS = [
{
id: "1",
name: "Ava Thompson",
role: "Design",
status: "Active",
statusColor: "success" as const,
},
{
id: "2",
name: "Liam Nguyen",
role: "Engineering",
status: "Paused",
statusColor: "warning" as const,
},
{
id: "3",
name: "Maya Patel",
role: "Product",
status: "Active",
statusColor: "success" as const,
},
];
export function Example() {
return (
);
}
```
For complete documentation and examples, see the [Table component page](https://heroui.pro/docs/native/components/table).
#### Carousel
A horizontal snap pager for galleries, product images, and similar slide layouts. The root owns the snap engine — viewport measurement, slide width, snap offsets, and autoplay — and exposes `useCarousel()` for custom scroll-driven indicators. Navigation presses commit immediately; a swipe commits at the halfway point between two snap points.
**Features:**
* Compound parts — `Carousel.Content`, `Carousel.Item`, `Carousel.Previous` / `Carousel.Next`, `Carousel.Dots`, `Carousel.Thumbnails` / `Carousel.Thumbnail`, plus `useCarousel()`
* Layout controls: `itemsPerView` (fractional values peek the next slide), `gap`, engine-owned `sidePadding`, `align`, and `type` (`in-place` / `modal` / `miniatures`)
* Autoplay with wrap; `stopAutoPlayOnInteraction` stops permanently on the first gesture by default
* Dots interpolate against root `progress` on the UI thread; `renderDot` replaces the default pills
* RTL-aware chevrons and snap offsets; programmatic trips use a UI-thread spring so rapid nav presses chain without restarting
* Theme-aware `Carousel.NavButtonBackground` on Previous/Next — `undefined` renders the theme default, a custom node replaces it, `null` removes it
**Usage:**
```tsx
import { Carousel } from "heroui-native-pro";
import { Image } from "react-native";
const SLIDES = [
{ id: "1", uri: "https://example.com/photo-1.jpg" },
{ id: "2", uri: "https://example.com/photo-2.jpg" },
{ id: "3", uri: "https://example.com/photo-3.jpg" },
];
export function Example() {
return (
{SLIDES.map((slide) => (
))}
);
}
```
For complete documentation and examples, see the [Carousel component page](https://heroui.pro/docs/native/components/carousel).
#### MorphButton
A pressable surface that morphs between auto-measured collapsed and expanded content. The root's layout footprint always equals the collapsed size, so the expanding panel overflows without shifting siblings. Both content parts are measured up front — expanded content stays mounted while hidden — so the first open already knows its target size.
**Features:**
* Compound parts — `MorphButton.CollapsedContent` and `MorphButton.ExpandedContent`
* Eight logical `direction`s (`top`, `top-end`, `end`, `bottom-end`, `bottom`, `bottom-start`, `start`, `top-start`); `start` / `end` mirror in RTL
* `primary` / `secondary` variants — inverted floating surface vs. a card-friendly fill
* Controlled (`isOpen` / `onOpenChange`) and uncontrolled (`defaultOpen`) open state
* Spring morph via `animation.morphSpringConfig`, or `"disable-all"` to snap every transition
**Usage:**
```tsx
import { MorphButton } from "heroui-native-pro";
import { Text, View } from "react-native";
export function Example() {
return (
2 products in bagOrder summary
);
}
```
For complete documentation and examples, see the [MorphButton component page](https://heroui.pro/docs/native/components/morph-button).
#### PhoneNumberField
An international phone number field with per-country as-you-type formatting, a searchable country picker, smart paste, and E.164 output. Formatting uses `#`-template masks generated from `libphonenumber-js` metadata, so grouping stays stable as the user types — digits never rearrange mid-word. Validation, region detection, and length limits use `libphonenumber-js` when the optional peer is installed.
**Features:**
* Compound parts — `InputGroup`, `Prefix`, `Input`, `Select`, `Trigger`, `Portal`, `Overlay`, `Content`, `ContentBackground`, `ContentHandle`, `SearchInput`, `CountryList`, `CountryItem`, `Suffix`
* One stable layout per country: values are grouped like the placeholder mask; longer-than-mask numbers extend the last group
* Smart paste and dial-code typing detect the country from a leading `+` and keep the digits; choosing a country from the picker clears them
* Controlled and uncontrolled modes for number, country, and picker open state
* `onValueChange` reports digits, formatted value, E.164, country, and `isValid` / `isComplete` flags
* Virtualized country list; rows are measured before mount and the selected country is centred on open
**Usage:**
```tsx
import { Description, Label } from "heroui-native";
import { PhoneNumberField } from "heroui-native-pro";
export function Example() {
return (
We'll send a verification code to this number
);
}
```
For complete documentation and examples, see the [PhoneNumberField component page](https://heroui.pro/docs/native/components/phone-number-field).
## Dependencies
### libphonenumber-js (optional)
Declared as an optional peer dependency and loaded through an optional import helper. Required for metadata-driven validation, E.164 output, region detection from a pasted number, and per-prefix length limits. Without it, `isValid` degrades to a completeness check and `e164` is the dial code plus digits; formatting is unchanged because it comes from the built-in mask table.
```bash
npx expo install libphonenumber-js
```
Regenerate the fallback mask table with `node scripts/generate-phone-number-masks.js` after upgrading `libphonenumber-js`.
## Updated Documentation
The following documentation has been added or updated to reflect the changes in this release:
* [Table](https://heroui.pro/docs/native/components/table) — New component page covering selection, controlled sorting, column widths, empty state, and virtualized body
* [Carousel](https://heroui.pro/docs/native/components/carousel) — New component page covering snap layout, autoplay, dots, thumbnails, `useCarousel()`, and nav button backgrounds
* [MorphButton](https://heroui.pro/docs/native/components/morph-button) — New component page covering collapsed/expanded content, directions, variants, and the morph spring
* [PhoneNumberField](https://heroui.pro/docs/native/components/phone-number-field) — New component page covering as-you-type formatting, country picker, smart paste, validation, and the optional `libphonenumber-js` peer
## Links
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Component Documentation](https://heroui.pro/docs/native/components)
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview)
# 1.0.0-beta.2
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-2
> Adds Badge, ProgressBar, ProgressCircle, ToggleButton, ToggleButtonGroup, BarChart, and Widget components, fixes calendar year picker sync after nav-button paging, locks date picker trigger variants, and launches the HeroUI Native Pro Figma library.
April 2026
The second beta of **HeroUI Native Pro** lands seven new components — `Badge`, `ProgressBar`, `ProgressCircle`, `ToggleButton`, `ToggleButtonGroup`, `BarChart`, and `Widget` — alongside a calendar year-picker sync fix and a small API tightening on the date picker family. Everything composes on the same compound, theme-token, and animation primitives introduced in `beta.1`.
## Try It on Your Device
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## What's New
### New Components
This release introduces **7 new** components spanning four categories:
* **BarChart**: `victory-native`-powered bar chart with single, grouped, and stacked variants. ([Documentation](https://heroui.pro/docs/native/components/bar-chart))
* **Badge**: Notification dot or pill anchored to any element. ([Documentation](https://heroui.pro/docs/native/components/badge))
* **ProgressBar**: Horizontal determinate / indeterminate progress indicator. ([Documentation](https://heroui.pro/docs/native/components/progress-bar))
* **ProgressCircle**: Circular determinate / indeterminate progress indicator. ([Documentation](https://heroui.pro/docs/native/components/progress-circle))
* **ToggleButton**: Selectable button with selected / unselected styling. ([Documentation](https://heroui.pro/docs/native/components/toggle-button))
* **ToggleButtonGroup**: Single- or multi-select group of `ToggleButton`s with attached and detached layouts. ([Documentation](https://heroui.pro/docs/native/components/toggle-button-group))
* **Widget**: Dashboard surface that pairs an optional header / footer with an elevated content card for charts, tables, and KPIs. ([Documentation](https://heroui.pro/docs/native/components/widget))
#### BarChart
A `victory-native`-powered bar chart with the same theming, animation cascade, and Uniwind `colorClassName` API as `LineChart`. Compound primitives cover the three common bar layouts — single series, grouped (clustered) series, and stacked columns — and each renders as a Uniwind-wrapped Skia path.
**Features:**
* Compound parts — `BarChart.Bar`, `BarChart.BarGroup`, `BarChart.BarGroupItem`, `BarChart.StackedBar`
* Themed fills via `colorClassName` (e.g. `accent-chart-1`, `accent-chart-3`) with full `accent-chart-*` token support
* Built-in draw-on animation, gated by the cascading `animation="disable-all"` prop through `AnimationSettingsProvider`
* Configurable `barWidth`, `roundedCorners`, and `domainPadding`
* `useBarPath`, `useBarGroupPaths`, and `useStackedBarPaths` hooks re-exported for custom rendering (gradients, patterns, etc.)
* Reports resolved `barWidth`, `groupWidth`, and `gapWidth` through `BarGroup`'s `onBarSizeChange` for synchronized overlays
* `victory-native` stays an optional dependency — only loaded when `BarChart` or `LineChart` is imported
**Usage:**
```tsx
import { BarChart } from "heroui-native-pro";
const DATA = [
{ month: "Jan", sales: 120 },
{ month: "Feb", sales: 180 },
{ month: "Mar", sales: 150 },
];
export function Example() {
return (
{({ points, chartBounds }) => (
)}
);
}
```
For complete documentation and examples, see the [BarChart component page](https://heroui.pro/docs/native/components/bar-chart).
#### Badge
A small indicator positioned relative to another element. Use it for unread counters on `Avatar`s, status dots on icons, or standalone pills in lists. Renders as a dot when no children are passed and as a pill when string or number children are provided.
**Features:**
* Compound parts — `Badge.Anchor`, `Badge`, `Badge.Label`
* `color` — `default`, `accent`, `success`, `warning`, `danger`
* `variant` — `primary`, `secondary`, `soft`
* `size` — `sm`, `md`, `lg`
* `placement` — `top-right`, `top-left`, `bottom-right`, `bottom-left` when used inside `Badge.Anchor`
* Animated mount / unmount transitions with full `animation` cascade support
**Usage:**
```tsx
import { Avatar, Badge } from "heroui-native-pro";
export function Example() {
return (
...
5
);
}
```
For complete documentation and examples, see the [Badge component page](https://heroui.pro/docs/native/components/badge).
#### ProgressBar
A horizontal progress bar that supports both determinate and indeterminate modes. The root computes the percentage and formatted value text from `value`, `minValue`, `maxValue`, and `formatOptions`, while the `Track` and `Fill` primitives handle the visual styling. Plain string children auto-expand into the full label / track / fill layout.
**Features:**
* Compound parts — `ProgressBar.Track`, `ProgressBar.Fill`, `ProgressBar.Label`, `ProgressBar.ValueLabel`
* Determinate width animation and indeterminate `translateX` sweep, switched via `isIndeterminate`
* `color` — `default`, `accent`, `success`, `warning`, `danger`
* `size` — `sm`, `md`, `lg` track heights
* Locale-aware formatting through `formatOptions` (e.g. `style: "percent"`)
* Tabular figures on `ValueLabel` for stable digit alignment
* `accessibilityLabel` for screen-reader-only labels when no visual `Label` is rendered
**Usage:**
```tsx
import { ProgressBar } from "heroui-native-pro";
import { View } from "react-native";
export function Example() {
return (
Loading
);
}
```
For complete documentation and examples, see the [ProgressBar component page](https://heroui.pro/docs/native/components/progress-bar).
#### ProgressCircle
A circular progress indicator built on Skia / SVG with the same determinate / indeterminate API as `ProgressBar`. The `Indicator` animates `strokeDashoffset` for determinate progress and rotates continuously when `isIndeterminate` is set. Drop a `ValueLabel` inside to render the formatted value centered on the circle.
**Features:**
* Compound parts — `ProgressCircle.Indicator`, `ProgressCircle.ValueLabel`
* Determinate stroke animation and indeterminate spin, switched via `isIndeterminate`
* `color` — `default`, `accent`, `success`, `warning`, `danger`
* `size` — `sm`, `md`, `lg` presets, or a custom pixel number for full control
* `strokeWidth`, `trackColor`, and `fillColor` overrides on `Indicator`
* Locale-aware `formatOptions`, with a `ValueLabel` that supports custom child content
* Tabular figures and full `animation` cascade support
**Usage:**
```tsx
import { ProgressCircle } from "heroui-native-pro";
export function Example() {
return (
);
}
```
For complete documentation and examples, see the [ProgressCircle component page](https://heroui.pro/docs/native/components/progress-circle).
#### ToggleButton & ToggleButtonGroup
A pair of components for two-state and segmented selection. `ToggleButton` wraps `heroui-native`'s `Button` with selected / unselected background styling, and `ToggleButtonGroup` composes multiple toggles into a single segmented control with shared selection, size, and disabled state via context.
**Features:**
* `ToggleButton` compound parts — `ToggleButton.Label` with automatic selected / unselected text colors
* `variant` — `default`, `ghost` (selected appearance is shared)
* `size` — `sm`, `md`, `lg`
* `isIconOnly` for square icon-only toggles
* Controlled (`isSelected` / `onChange`) and uncontrolled (`defaultSelected`) modes
* `useToggleButton` hook for reading state from descendants
* `ToggleButtonGroup` `selectionMode` — `single` or `multiple`, with `selectedKeys` / `onSelectionChange` exposed as `Set`
* `ToggleButtonGroup` `orientation` — `horizontal` or `vertical`
* `isDetached` to render each toggle as a separate rounded button instead of an attached segment
* `fullWidth` to stretch the group across its container
**Usage:**
```tsx
import { ToggleButton, ToggleButtonGroup } from "heroui-native-pro";
export function Example() {
return (
LeftCenterRight
);
}
```
For complete documentation and examples, see the [ToggleButton](https://heroui.pro/docs/native/components/toggle-button) and [ToggleButtonGroup](https://heroui.pro/docs/native/components/toggle-button-group) component pages.
#### Widget
A dashboard container surface that pairs an optional header and footer with an elevated content card. Drop a `LineChart`, `BarChart`, table, or KPI block inside `Widget.Content` and you get the standard dashboard chrome — title, description, inline legend, and footer summary — without hand-rolling cards on every screen.
**Features:**
* Compound parts — `Widget.Header`, `Widget.Title`, `Widget.Description`, `Widget.Content`, `Widget.Footer`, `Widget.Legend`, `Widget.LegendItem`
* Two-layer surface — outer `bg-surface-secondary` shell with an inner elevated `bg-surface` content card
* Inline `Widget.Legend` with `Widget.LegendItem` entries that accept `colorClassName` (theme tokens, preferred) or `color` (inline color string)
* Slot-aware `Widget.LegendItem` with `classNames`, `styles`, and `textProps` overrides for the `wrapper`, `dot`, and `label` slots
* `animation="disable-all"` on the root cascades through `AnimationSettingsProvider` to every animated descendant (charts, progress bars, etc.)
* All sub-components are optional — drop `Widget.Footer` for compact panels, omit `Widget.Header` for chart-only widgets
**Usage:**
```tsx
import { LineChart, Widget } from "heroui-native-pro";
export function Example() {
return (
Tokens Over Time
Input
Output
{/* ... */}
);
}
```
For complete documentation and examples, see the [Widget component page](https://heroui.pro/docs/native/components/widget).
## Component Improvements
### CalendarYearPicker Sync Fix
Fixed a sync issue where the [Calendar](https://heroui.pro/docs/native/components/calendar) and [RangeCalendar](https://heroui.pro/docs/native/components/range-calendar) year picker stayed pinned to the page the calendar was opened on after paging via the next / previous month buttons. The year picker trigger heading and the highlighted / scrolled-to year now always reflect the page the user is currently viewing.
## ⚠️ Breaking Changes
### Removed `variant` from Date Picker triggers
The `variant` prop has been removed from the `Trigger` subcomponents of [DateField](https://heroui.pro/docs/native/components/date-field), [DatePicker](https://heroui.pro/docs/native/components/date-picker), and [DateRangePicker](https://heroui.pro/docs/native/components/date-range-picker). Each trigger now hardcodes its variant internally to enforce consistent styling across the date picker family:
* `DateField.Trigger` always renders with `variant="unstyled"`
* `DatePicker.Trigger` always renders with `variant="default"`
* `DateRangePicker.Trigger` always renders with `variant="default"`
Trigger prop types are now `Omit`, so passing `variant` will produce a TypeScript error.
**Migration:**
If you were passing the previous default `variant`, just drop the prop:
```tsx
// Before
// After
```
If you previously passed `variant="unstyled"` to `DatePicker.Trigger` or `DateRangePicker.Trigger` to render a fully custom trigger, you will need to recompose the trigger using the lower-level `Select.Trigger` from `heroui-native` directly, since these triggers now always render in the `default` variant.
There are no runtime behavior changes for consumers using the previous defaults.
## Updated Documentation
The following documentation pages have been updated to reflect the changes in this release:
* [BarChart](https://heroui.pro/docs/native/components/bar-chart) — New component page with basic, horizontal, gradient, grouped, stacked, and animated examples
* [LineChart](https://heroui.pro/docs/native/components/line-chart) — Now links out to the underlying [`victory-native`](https://nearform.com/open-source/victory-native/docs/) docs
* [Calendar](https://heroui.pro/docs/native/components/calendar) and [RangeCalendar](https://heroui.pro/docs/native/components/range-calendar) — Now reference [`@internationalized/date`](https://react-spectrum.adobe.com/internationalized/date/) for locale, time-zone, and non-Gregorian calendar context
* [DateField](https://heroui.pro/docs/native/components/date-field), [DatePicker](https://heroui.pro/docs/native/components/date-picker), [DateRangePicker](https://heroui.pro/docs/native/components/date-range-picker) — `variant` removed from `Trigger` prop tables
* [Badge](https://heroui.pro/docs/native/components/badge), [ProgressBar](https://heroui.pro/docs/native/components/progress-bar), [ProgressCircle](https://heroui.pro/docs/native/components/progress-circle), [ToggleButton](https://heroui.pro/docs/native/components/toggle-button), [ToggleButtonGroup](https://heroui.pro/docs/native/components/toggle-button-group), [Widget](https://heroui.pro/docs/native/components/widget) — New component pages
## HeroUI Native Pro Figma
The full **Figma design library** for HeroUI Native Pro is now available — including both open source and Pro components, ready to drop into your designs.
* Every component perfectly matched to the code
* Open source + Pro components included
* Start designing faster with production-ready building blocks
Get it from your [dashboard](https://heroui.pro/dashboard) or check the [Figma setup guide](/docs/react/getting-started/figma).
## Links
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Component Documentation](https://heroui.pro/docs/native/components)
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview)
* [Figma Setup Guide](/docs/react/getting-started/figma)
# 1.0.0-beta.3
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-3
> Adds AreaChart, ChartCrosshair, ChartIndicator, EmptyState, and Segment components, unifies press-driven overlays across cartesian charts, removes LineChart.Tooltip and LineChart.Crosshair, and cleans up ProgressButton press-out handling.
May 2026
The third beta of **HeroUI Native Pro** lands five new components — `AreaChart`, `ChartCrosshair`, `ChartIndicator`, `EmptyState`, and `Segment` — and unifies press-driven UI across every cartesian chart. `LineChart` and `BarChart` now share the same standalone crosshair and indicator primitives.
## Try It on Your Device
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## What's New
### New Components
This release introduces **5 new** components spanning charts, data display, and navigation:
* **AreaChart**: `victory-native`-powered area chart with `Area`, `AreaRange`, and `StackedArea` compound parts. ([Documentation](https://heroui.pro/docs/native/components/area-chart))
* **ChartCrosshair**: Skia vertical rule with an RN tooltip overlay (`Anchor` / `Value` / `ValueLabel`) shared across every cartesian chart. ([Documentation](https://heroui.pro/docs/native/components/chart-crosshair))
* **ChartIndicator**: Themed Skia double-dot marker (outer halo + inner dot) for press-driven point markers. ([Documentation](https://heroui.pro/docs/native/components/chart-indicator))
* **EmptyState**: Compound primitive for empty / zero-state messaging with `Header`, `Media`, `Title`, `Description`, and `Content` parts. ([Documentation](https://heroui.pro/docs/native/components/empty-state))
* **Segment**: Segmented control built on `Tabs` with `Group`, `ScrollView`, `Indicator`, `Item`, `Label`, and `Separator` compounds. ([Documentation](https://heroui.pro/docs/native/components/segment))
#### AreaChart
A Skia-accelerated area chart for visualizing trends, stacked contributions, and confidence bands. Built on `victory-native` and wrapped in a themed outer `View`, with the same compound, theming, and animation cascade conventions as `LineChart` and `BarChart`. Pair it with `ChartCrosshair` and `ChartIndicator` to add press-driven overlays.
**Features:**
* Compound parts — `AreaChart.Area`, `AreaChart.AreaRange`, `AreaChart.StackedArea`
* Themed fills via Uniwind `colorClassName` (e.g. `accent-chart-1`, `accent-chart-3`) with full `accent-chart-*` token support
* Built-in draw-on animation, gated by the cascading `animation="disable-all"` prop through `AnimationSettingsProvider`
* Configurable `curveType` (`natural`, `linear`, `monotoneX`, etc.) and per-area `animate` config for path-interpolated data transitions
* Skia `LinearGradient` composes as a child of `AreaChart.Area` for gradient fills
* `useAreaPath` and `useStackedAreaPaths` hooks re-exported for custom rendering
* `victory-native` stays an optional dependency — only loaded when a chart component is imported
**Usage:**
```tsx
import { AreaChart } from "heroui-native-pro";
const DATA = [
{ month: "Jan", revenue: 120 },
{ month: "Feb", revenue: 180 },
{ month: "Mar", revenue: 150 },
];
export function Example() {
return (
{({ points, chartBounds }) => (
)}
);
}
```
For complete documentation and examples, see the [AreaChart component page](https://heroui.pro/docs/native/components/area-chart).
#### ChartCrosshair
A vertical rule and tooltip overlay that highlight the pressed point on a chart. The Skia rule renders inside the chart canvas, while a sibling React Native overlay (`ChartCrosshair.Value`) hosts the tooltip pill — measured, centered, and clamped against `chartBounds`, with a label that updates on the UI thread via an internal `ReText` (read-only Reanimated `TextInput`) bridge.
**Features:**
* Compound parts — `ChartCrosshair`, `ChartCrosshair.Anchor`, `ChartCrosshair.Value`, `ChartCrosshair.ValueLabel`
* `variant` — `dashed` (themed `DashPathEffect`) or `solid` unbroken stroke
* Custom `color`, `strokeWidth`, and overrideable `DashPathEffect` via children
* Driven by `useChartPressState` shared values from `victory-native` — no React renders on press
* React Native value pill rendered outside the Skia canvas: auto-measures its own width, centers on `x`, and clamps to `chartBounds` via `onChartBoundsChange`
* `ChartCrosshair.ValueLabel` reads a `useDerivedValue` shared string from context, so label text updates entirely on the UI thread
* Works uniformly across `LineChart`, `BarChart`, and `AreaChart`
**Usage:**
```tsx
import { LineChart, ChartCrosshair, ChartIndicator } from "heroui-native-pro";
import { useChartPressState } from "victory-native";
import { useDerivedValue } from "react-native-reanimated";
export function Example() {
const { state, isActive } = useChartPressState({
x: "" as string,
y: { revenue: 0 },
});
const value = useDerivedValue(
() => `$${state.y.revenue.value.value.toFixed(0)}`
);
return (
{({ points, chartBounds }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
);
}
```
For complete documentation and examples, see the [ChartCrosshair component page](https://heroui.pro/docs/native/components/chart-crosshair).
#### ChartIndicator
A themed Skia double-dot marker — outer halo plus inner dot — that follows the pressed point on a chart. Drop-in primitive that pairs with `ChartCrosshair` (or stands alone) for line, area, and bar press interactions.
**Features:**
* Driven by `useChartPressState` shared values for `x` and `y`
* `innerRadius` (default `5`) and `outerRadius` (default `7`) for sizing
* `innerColor` and `outerColor` overrides on top of the themed `--color-background` halo and `--color-chart-3` dot
* Extra Skia `Circle` props are forwarded to the inner circle for stroke, opacity, and other effects
* Renders inside the chart canvas alongside `ChartCrosshair` for a complete press-overlay system
**Usage:**
```tsx
import { LineChart, ChartIndicator } from "heroui-native-pro";
import { useChartPressState } from "victory-native";
export function Example() {
const { state, isActive } = useChartPressState({
x: 0,
y: { value: 0 },
});
return (
{({ points }) => (
<>
{isActive ? (
) : null}
>
)}
);
}
```
For complete documentation and examples, see the [ChartIndicator component page](https://heroui.pro/docs/native/components/chart-indicator).
#### EmptyState
A placeholder for empty views with an optional icon, title, description, and call-to-action area. Use it for empty inboxes, no-search-results screens, first-run states, or any zero-data UI that previously had to be hand-rolled.
**Features:**
* Compound parts — `EmptyState.Header`, `EmptyState.Media`, `EmptyState.Title`, `EmptyState.Description`, `EmptyState.Content`
* `EmptyState.Media` ships `default` and `icon` variants (icon variant renders a circular muted surface)
* `EmptyState.Title` rendered with `accessibilityRole="header"` for screen readers
* All sub-components are optional — drop `Content` for header-only states, omit `Media` for text-only layouts
* Root `animation="disable-all"` cascades through `AnimationSettingsProvider` to every animated descendant
**Usage:**
```tsx
import { Button, EmptyState } from "heroui-native-pro";
import { BellIcon } from "lucide-react-native";
export function Example() {
return (
No notifications yet
New activity will show up here as it happens.
);
}
```
For complete documentation and examples, see the [EmptyState component page](https://heroui.pro/docs/native/components/empty-state).
#### Segment
A segmented control for toggling between a small set of mutually exclusive options. Built on top of HeroUI Native's `Tabs` (`variant="primary"`) so style overrides and animation hooks (`useTabsMeasurements`, `useTabsTrigger`) stay consistent with the underlying primitive.
**Features:**
* Compound parts — `Segment.Group`, `Segment.ScrollView`, `Segment.Indicator`, `Segment.Item`, `Segment.Label`, `Segment.Separator`
* Controlled (`value` / `onValueChange`) and uncontrolled (`defaultValue`) modes via `useControllableState`
* `size` — `sm`, `md`, `lg` cascades padding, indicator radius, and label typography across every part
* `isDisabled` cascades from the root through `SegmentContext` and merges per-item flags
* `Segment.ScrollView` wraps `Tabs.ScrollView` for horizontally scrollable rows when items overflow
* `Segment.Indicator` inherits Tabs animation props (`animation`, `isAnimatedStyleActive`) for full Reanimated control
* `Segment.Separator` accepts `betweenValues` so dividers auto-hide when one of their neighbors is selected
**Usage:**
```tsx
import { Segment } from "heroui-native-pro";
export function Example() {
return (
DashboardAnalytics
);
}
```
For complete documentation and examples, see the [Segment component page](https://heroui.pro/docs/native/components/segment).
### Unified Chart Press Overlays
`ChartCrosshair` and `ChartIndicator` are now **standalone primitives** that work uniformly across every cartesian chart. Previously, `LineChart` shipped its own bundled `LineChart.Tooltip` and `LineChart.Crosshair` Skia compounds; there was no equivalent for `BarChart` or `AreaChart`, and no React Native overlay label that could sit on top of the Skia canvas.
**Supported components:**
* [LineChart](https://heroui.pro/docs/native/components/line-chart)
* [BarChart](https://heroui.pro/docs/native/components/bar-chart)
* [AreaChart](https://heroui.pro/docs/native/components/area-chart)
The new pattern reads `chartBounds` from `onChartBoundsChange` and threads `useChartPressState` shared values through both Skia primitives and the RN value overlay — so press-driven UI is one mental model for every chart in the library.
## Component Improvements
### LineChart and BarChart Migration
The [LineChart](https://heroui.pro/docs/native/components/line-chart) and [BarChart](https://heroui.pro/docs/native/components/bar-chart) example screens have been migrated to the new `ChartCrosshair` + `ChartIndicator` pattern.
**Improvements:**
* Press-driven crosshair, indicator dot, and tooltip pill now share the same primitives across both charts
* `chartBounds` sourced from `onChartBoundsChange` for consistent overlay clamping
* React Native value labels render on top of the Skia canvas via the new `ReText` bridge — no more in-canvas-only tooltips
* Component docs updated in lockstep with the new APIs
### ProgressButton Press-Out Cleanup
The [ProgressButton](https://heroui.pro/docs/native/components/progress-button) `handlePressOut` flow has been simplified. The redundant `cancelAnimation(progress)` call has been removed — `resetProgress()` already supersedes any in-flight animation by assigning a new `withSpring` value to `progress`, making the explicit cancel unnecessary.
**Improvements:**
* `handlePressOut` now relies on `resetProgress()` alone to drive the value back to `0`
* Unused `cancelAnimation` import removed from `progress-button.animation.ts`
* No visual or behavioral change for consumers — reset still runs as a spring (or instantly when animations are disabled)
## ⚠️ Breaking Changes
### Removed `LineChart.Tooltip` and `LineChart.Crosshair`
`LineChart.Tooltip` and `LineChart.Crosshair` have been removed in favor of the new standalone `ChartCrosshair` and `ChartIndicator` exports from `heroui-native-pro`. The new primitives accept the same `x` / `y` / `top` / `bottom` props and the same `useChartPressState` wiring, so the migration is a like-for-like replacement.
**Migration:**
Replace `LineChart.Tooltip` with `ChartIndicator`, and `LineChart.Crosshair` with `ChartCrosshair`. For React Native value labels, wrap the chart in `ChartCrosshair.Anchor` and render `ChartCrosshair.Value` (with `ChartCrosshair.ValueLabel`) as a sibling.
```tsx
// Before
import { LineChart } from "heroui-native-pro";
import { useChartPressState } from "victory-native";
const { state, isActive } = useChartPressState({
x: 0,
y: { value: 0 },
});
{({ points, chartBounds }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
// After
import {
LineChart,
ChartCrosshair,
ChartIndicator,
} from "heroui-native-pro";
import { useChartPressState } from "victory-native";
import { useDerivedValue } from "react-native-reanimated";
const { state, isActive } = useChartPressState({
x: "" as string,
y: { value: 0 },
});
const label = useDerivedValue(
() => `${state.y.value.value.value.toFixed(0)}`
);
{({ points, chartBounds }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
;
```
**Available crosshair variants:**
* `"dashed"` — Themed `DashPathEffect` rule (default)
* `"solid"` — Unbroken stroke
When using `ChartCrosshair.Anchor`, the wrapped chart's `wrapperClassName` must not contain padding (e.g. `p-*`, `px-*`, `py-*`) — the anchor measures positions in the chart's native coordinate space, so any padding offsets the chart relative to the anchor and breaks centering / clamping of `ChartCrosshair.Value`. Apply spacing on a parent container instead.
## Updated Documentation
The following documentation pages have been updated to reflect the changes in this release:
* [AreaChart](https://heroui.pro/docs/native/components/area-chart) — New component page with basic, gradient, curve type, animated, stacked, and area-range examples
* [ChartCrosshair](https://heroui.pro/docs/native/components/chart-crosshair) — New component page covering the Skia rule, RN value overlay, dashed / solid variants, and `useDerivedValue` label patterns
* [ChartIndicator](https://heroui.pro/docs/native/components/chart-indicator) — New component page covering basic usage, custom radii, custom colors, and forwarded Skia props
* [EmptyState](https://heroui.pro/docs/native/components/empty-state) — New component page with header, icon media, action, and animation-cascade examples
* [Segment](https://heroui.pro/docs/native/components/segment) — New component page covering controlled / uncontrolled selection, sizes, scrollable layouts, separators, and disabled cascading
* [LineChart](https://heroui.pro/docs/native/components/line-chart) — `LineChart.Tooltip` and `LineChart.Crosshair` removed; press-overlay examples migrated to `ChartCrosshair` + `ChartIndicator`
## Links
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Component Documentation](https://heroui.pro/docs/native/components)
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview)
# 1.0.0-beta.4
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-4
> Adds PieChart, RadarChart, WheelPicker, WheelPickerGroup, TimePicker, WheelTimePicker, DateTimePicker, and WheelDateTimePicker, refactors component labels to soft-foreground tokens, and fixes NumberStepper digit jitter plus WheelPicker overscroll and re-render edge cases.
June 2026
The fourth beta of **HeroUI Native Pro** is a big one — eight new components spanning charts, wheel pickers, and a full time / date-time selection stack. `PieChart` and `RadarChart` round out the polar-chart family, a new `WheelPicker` / `WheelPickerGroup` foundation powers iOS-style wheel selection, and `TimePicker`, `WheelTimePicker`, `DateTimePicker`, and `WheelDateTimePicker` bring locale-aware time and date-time pickers to the library. This release also standardizes label and indicator colors on the new `*-soft-foreground` tokens and fixes a handful of `NumberStepper` and `WheelPicker` edge cases.
## Try It on Your Device
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## What's New
### New Components
This release introduces **8 new** components across charts, forms, and date & time:
* **PieChart**: `victory-native`-powered polar chart with pie, donut, segmented-donut, partial-arc, and labeled-slice layouts. ([Documentation](https://heroui.pro/docs/native/components/pie-chart))
* **RadarChart**: Skia-rendered multivariate radar with `Grid`, `AngleAxis`, `RadiusAxis`, and multi-series `Radar` compound parts. ([Documentation](https://heroui.pro/docs/native/components/radar-chart))
* **WheelPicker**: Vertical snap-to-row wheel with distance-based fade / scale and optional fade overlays. ([Documentation](https://heroui.pro/docs/native/components/wheel-picker))
* **WheelPickerGroup**: Coordinated row of wheels sharing layout, a composite values record, and a shared indicator / mask. ([Documentation](https://heroui.pro/docs/native/components/wheel-picker-group))
* **TimePicker**: Trigger field that opens a wheel-based hour / minute / period selector in a popover, dialog, or bottom sheet. ([Documentation](https://heroui.pro/docs/native/components/time-picker))
* **WheelTimePicker**: Standalone wheel time selector exchanging an `@internationalized/date` `Time` value. ([Documentation](https://heroui.pro/docs/native/components/wheel-time-picker))
* **DateTimePicker**: Field shell pairing a `Select` trigger with a combined date + time wheel surface. ([Documentation](https://heroui.pro/docs/native/components/date-time-picker))
* **WheelDateTimePicker**: Standalone wheel date-time selector exchanging a `CalendarDateTime` value. ([Documentation](https://heroui.pro/docs/native/components/wheel-date-time-picker))
#### PieChart
A polar chart for visualizing categorical proportions. Built on top of `victory-native`'s `PolarChart` and `Pie.Chart`, wrapped with HeroUI Native theming, generic-preserving typings, and a root-to-Canvas animation cascade. Compose pie, donut, segmented donut, partial-arc gauges, and Skia-font slice labels from the same building blocks.
**Features:**
* Compound parts — `PieChart`, `PieChart.Pie`, `PieChart.Slice`, `PieChart.SliceAngularInset`, `PieChart.Label`
* Donut layouts via `innerRadius`, segmented donuts via `SliceAngularInset`, and partial-arc gauges via `startAngle` / `circleSweepDegrees`
* Per-slice `animate` config for path-interpolated data transitions, gated by the cascading `animation="disable-all"` prop
* Slice fills sourced from each data row's `colorKey`; layer Skia shaders (`LinearGradient`, `RadialGradient`) as children for gradient fills
* Skia-font slice labels via `PieChart.Label`, with a render-function `children` for fully custom content
* New internal `BasePolarChart` helper plus re-exported `useSlicePath`, `useSliceAngularInsetPath`, and the `PieSliceData` type
* `victory-native` stays an optional peer dependency — only loaded when a chart component is imported
**Usage:**
```tsx
import { PieChart } from "heroui-native-pro";
const DATA = [
{ name: "Chrome", value: 62, color: "#6366f1" },
{ name: "Safari", value: 19, color: "#7c3aed" },
{ name: "Firefox", value: 10, color: "#8b5cf6" },
];
export function Example() {
return (
{() => }
);
}
```
For complete documentation and examples, see the [PieChart component page](https://heroui.pro/docs/native/components/pie-chart).
#### RadarChart
A radar chart for comparing multivariate data across categorical spokes. It reuses `victory-native`'s `PolarChart` for canvas measurement and `useAnimatedPath` for polygon interpolation, then renders every visual part — grid rings, spokes, axis labels, polygons, vertex dots — with `@shopify/react-native-skia` primitives.
**Features:**
* Compound parts — `RadarChart.Grid` (polygon or circle rings + spokes), `RadarChart.AngleAxis`, `RadarChart.RadiusAxis`, and `RadarChart.Radar`
* Multi-series support via sibling `Radar` components with per-series `dataKey` and `color`
* `RadarChart.RadiusAxis` with configurable `angle`, `orientation`, and `tickFormatter`
* `maxValue` to fix the radial scale across series; `showDots` / `dotRadius` to emphasize vertices
* Animated path interpolation through victory-native's `useAnimatedPath`, gated by `animation="disable-all"` on the root
* Public type exports (`RadarChartRootProps`, `RadarChartRadarProps`, etc.) and `radarChartClassNames` for styling overrides
* Relies on the existing optional `victory-native` and `@shopify/react-native-skia` peers — no new runtime dependencies
**Usage:**
```tsx
import { RadarChart } from "heroui-native-pro";
const DATA = [
{ category: "Design", score: 86 },
{ category: "Frontend", score: 92 },
{ category: "Backend", score: 74 },
];
export function Example() {
return (
);
}
```
For complete documentation and examples, see the [RadarChart component page](https://heroui.pro/docs/native/components/radar-chart).
#### WheelPicker
A vertical wheel picker with snap-to-row selection, distance-based fade and scale, and optional fade overlays. Built on `react-native-reanimated`, it powers iOS-style selectors for years, durations, reminders, and any single-column option list — and serves as the foundation for the new time and date-time pickers.
**Features:**
* Compound parts — `WheelPicker.Item`, `WheelPicker.ItemLabel`, `WheelPicker.Indicator`, `WheelPicker.Mask`
* Controlled (`value` / `onValueChange`) and uncontrolled (`defaultValue`) selection over a generic `items` array
* Configurable `itemHeight`, `visibleCount`, and a `renderItem` slot for fully custom rows
* Tap-to-focus rows, throttled value-change events, and distance-driven opacity / scale / label-color animations
* Imperative `scrollToIndex` / `scrollToValue` via the root ref
* `useWheelPicker` and `useWheelPickerItem` hooks for building custom row animations off the shared scroll offset
**Usage:**
```tsx
import { WheelPicker } from "heroui-native-pro";
import { useState } from "react";
const YEAR_ITEMS = Array.from({ length: 80 }, (_, i) => {
const year = 2026 - i;
return { value: year, label: String(year) };
});
export function Example() {
const [year, setYear] = useState(1995);
return (
);
}
```
For complete documentation and examples, see the [WheelPicker component page](https://heroui.pro/docs/native/components/wheel-picker).
#### WheelPickerGroup
A coordinated row of `WheelPicker` instances that share layout, emit a composite `values` record keyed by each wheel's `name`, and report when every wheel has come to rest. Use it for currency + amount selectors, time columns, or any multi-column wheel UI.
**Features:**
* Compound parts — `WheelPickerGroup.Indicator`, `WheelPickerGroup.Mask`
* Broadcasts `itemHeight` / `visibleCount` to every child wheel and distributes columns evenly with automatic `flex-1`
* Owns a shared `values` record (controlled `values` / uncontrolled `defaultValues`) keyed by each child wheel's `name`
* `onValuesChange` for live updates and `onValuesCommit` that fires once after every wheel comes to rest
* A single shared indicator band and mask spanning every column
* `animation="disable-all"` cascades the disabled state to all child wheels
**Usage:**
```tsx
import { WheelPicker, WheelPickerGroup } from "heroui-native-pro";
import { useState } from "react";
export function Example() {
const [values, setValues] = useState({ currency: "USD", amount: 500 });
return (
);
}
```
For complete documentation and examples, see the [WheelPickerGroup component page](https://heroui.pro/docs/native/components/wheel-picker-group).
#### WheelTimePicker
A standalone wheel time selector built on `WheelPickerGroup` that exchanges an `@internationalized/date` `Time` value. It renders hour, minute, and (in 12-hour mode) AM/PM period columns with a shared indicator and mask, committing live on scroll.
**Features:**
* Compound parts — `WheelTimePicker.Hour`, `WheelTimePicker.Minute`, `WheelTimePicker.Period`, `WheelTimePicker.Indicator`, `WheelTimePicker.Mask`
* 12 / 24-hour modes via `hourFormat`, with the period column auto-omitted in 24-hour mode
* `minuteInterval` for appointment-style stepping and `locale`-aware AM/PM labels (canonical `"AM"` / `"PM"` stored)
* `onValueChange` for live scroll updates and `onValueCommit` that fires once after every column rests
* Tabular numerals by default on hour / minute columns for stable digit width
**Usage:**
```tsx
import { Time } from "@internationalized/date";
import { WheelTimePicker } from "heroui-native-pro";
import { useState } from "react";
export function Example() {
const [time, setTime] = useState(new Time(9, 30));
return ;
}
```
For complete documentation and examples, see the [WheelTimePicker component page](https://heroui.pro/docs/native/components/wheel-time-picker).
#### TimePicker
A time picker that composes `WheelTimePicker` inside a trigger field with popover, dialog, and bottom-sheet presentations. It follows the same compound-component and adaptive-presentation pattern as `DatePicker`, and integrates with `Label`, `Description`, and `FieldError`.
**Features:**
* Compound parts — `TimePicker.Select`, `Trigger`, `Value`, `TriggerIndicator`, `Portal`, `Overlay`, `Content`, `Wheel`, and the `WheelHour` / `WheelMinute` / `WheelPeriod` / `WheelIndicator` / `WheelMask` columns
* Controlled and uncontrolled selection and open state
* `"popover"`, `"dialog"`, and `"bottom-sheet"` presentations from a single prop
* `hourFormat`, `minuteInterval`, `timeDisplayFormat` presets, and a `formatTime` override for the trigger label
* New internal `clock-icon` as the default trigger indicator
* Field states (`isRequired`, `isInvalid`, `isDisabled`) with danger border styling on invalid
**Usage:**
```tsx
import { Label } from "heroui-native";
import { TimePicker } from "heroui-native-pro";
export function Example() {
return (
);
}
```
For complete documentation and examples, see the [TimePicker component page](https://heroui.pro/docs/native/components/time-picker).
#### WheelDateTimePicker
A standalone wheel date-time selector built on `WheelPickerGroup` that exchanges a `CalendarDateTime` value. It adds a combined day column alongside hour, minute, and period columns, with bounded date ranges, localized labels, and custom `formatDate`.
**Features:**
* Compound parts — `WheelDateTimePicker.Date`, `Hour`, `Minute`, `Period`, `Indicator`, `Mask`
* Combined day column spanning `[minValue, maxValue]`, always widened to include the active value
* 12 / 24-hour modes, `minuteInterval` stepping, and localized date + AM/PM labels
* Custom date column labels via `formatDate` (receives an `isToday` flag)
* `onValueChange` for live updates and `onValueCommit` once every column rests
* Reuses the `WheelTimePicker` hour / minute / period item builders for consistency
**Usage:**
```tsx
import { CalendarDateTime } from "@internationalized/date";
import { WheelDateTimePicker } from "heroui-native-pro";
import { useState } from "react";
export function Example() {
const [value, setValue] = useState(
new CalendarDateTime(2026, 6, 1, 9, 30)
);
return ;
}
```
For complete documentation and examples, see the [WheelDateTimePicker component page](https://heroui.pro/docs/native/components/wheel-date-time-picker).
#### DateTimePicker
A field shell that pairs a `Select` trigger with a `WheelDateTimePicker` presentation surface. It mirrors `TimePicker`'s ergonomics for a combined date + time selection, forwarding range, format, and locale props down to the wheel and bridging context through portals.
**Features:**
* Compound parts — `DateTimePicker.Select`, `Trigger`, `Value`, `TriggerIndicator`, `Portal`, `Overlay`, `Content`, `Wheel`, and the aliased `WheelDate` / `WheelHour` / `WheelMinute` / `WheelPeriod` / `WheelIndicator` / `WheelMask` parts
* `"popover"`, `"dialog"`, and `"bottom-sheet"` presentations
* Controlled / uncontrolled selection and open state with automatic context bridging through portals
* `minValue` / `maxValue`, `hourFormat`, `minuteInterval`, `dateTimeDisplayFormat` presets, and a `formatDateTime` override
* Field states (`isRequired`, `isInvalid`, `isDisabled`) with a default trailing calendar icon
**Usage:**
```tsx
import { DateTimePicker } from "heroui-native-pro";
export function Example() {
return (
);
}
```
For complete documentation and examples, see the [DateTimePicker component page](https://heroui.pro/docs/native/components/date-time-picker).
## Component Improvements
### NumberStepper Digit Jitter Fix
Fixed a layout jitter in the [NumberStepper](https://heroui.pro/docs/native/components/number-stepper) value where the displayed number shifted horizontally as digits changed, due to proportional figure widths.
**Improvements:**
* `NumberStepper.Value` now applies `fontVariant: ['tabular-nums']` for a stable, monospaced digit width — no more horizontal shift between values like `1` and `8`.
* The consumer-provided `style` prop is now destructured and merged with the internal style, so overrides still take precedence via the merged style array.
## Style Fixes
### Soft-Foreground Token Adoption
Standardized label, icon, and indicator colors across multiple components to use the new `*-soft-foreground` semantic tokens instead of raw color tokens. These are internal styling refinements — the public component API is unchanged.
**Fixes:**
* [Badge](https://heroui.pro/docs/native/components/badge), [ProgressButton](https://heroui.pro/docs/native/components/progress-button), and [SlideButton](https://heroui.pro/docs/native/components/slide-button) labels now use `*-soft-foreground` tokens for the secondary / soft / accent / success / warning / danger variants.
* [TrendChip](https://heroui.pro/docs/native/components/trend-chip) indicator colors are resolved via explicit `Record` maps for the primary and soft variants.
* [Calendar](https://heroui.pro/docs/native/components/calendar) and [RangeCalendar](https://heroui.pro/docs/native/components/range-calendar) nav buttons, the year-picker indicator, and today cells use soft-foreground / soft-background tokens.
### Style Optimizations
* **Range calendar today highlight**: `RangeCalendar` adds a `data-today-not-in-range` attribute so today's highlight no longer overrides range endpoints or the continuous middle strip.
## Dependencies
### Other Dependency Upgrades
* `heroui-native` (peer): `1.0.2` / `1.0.3` → `1.0.4`
## Updated Documentation
The following documentation pages have been updated to reflect the changes in this release:
* [PieChart](https://heroui.pro/docs/native/components/pie-chart) — New component page with pie, donut, segmented donut, partial-arc, labeled-slice, gradient, and animated examples
* [RadarChart](https://heroui.pro/docs/native/components/radar-chart) — New component page covering basic, multi-series, dots-only, circle-grid, fixed-scale, custom radius-axis, custom font, and animated variants
* [WheelPicker](https://heroui.pro/docs/native/components/wheel-picker) — New component page with masked, uncontrolled, custom render, custom indicator / mask, animation, disabled, and programmatic-scroll examples
* [WheelPickerGroup](https://heroui.pro/docs/native/components/wheel-picker-group) — New component page covering controlled / uncontrolled values, shared layout, commit-on-rest, custom indicator / mask, and disabled cascading
* [TimePicker](https://heroui.pro/docs/native/components/time-picker) — New component page covering popover / dialog / bottom-sheet presentations, hour format, minute interval, custom format, and field states
* [WheelTimePicker](https://heroui.pro/docs/native/components/wheel-time-picker) — New component page covering 12 / 24-hour modes, minute interval, localized labels, commit-on-rest, custom composition, and custom item render
* [DateTimePicker](https://heroui.pro/docs/native/components/date-time-picker) — New component page covering managed state, bounded date range, 24-hour mode, custom label, and field states
* [WheelDateTimePicker](https://heroui.pro/docs/native/components/wheel-date-time-picker) — New component page covering bounded ranges, 24-hour mode, minute interval, localized labels, custom date label, and commit-on-rest
* [RangeCalendar](https://heroui.pro/docs/native/components/range-calendar) — Documents the new `data-today-not-in-range` render-prop attribute
## Links
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Component Documentation](https://heroui.pro/docs/native/components)
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview)
# 1.0.0-beta.5
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-5
> Adds ComposedChart, ChartTooltip, NumberPad, and RadialChart, improves SplitView mount behavior, and fixes NumberField runaway onChange on rapid taps.
June 16, 2026
The fifth beta of **HeroUI Native Pro** extends the charting stack with multi-series cartesian dashboards, press-driven floating tooltips, and gauge-style radial visualizations — plus a numeric keypad for PIN and code entry flows. `ComposedChart` and `ChartTooltip` bring bar, line, and area series together under one themed root with clamped tooltip positioning, while `RadialChart` adds Skia-rendered arc rings for progress and gauge UIs. This release also ships `NumberPad`, improves `SplitView` mount behavior, and fixes runaway `onChange` events when tapping `NumberField` increment/decrement buttons rapidly.
## Try It on Your Device
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## What's New
### New Components
This release introduces **4 new** components across charts and forms:
* **ComposedChart**: Multi-series cartesian root combining bar, line, and area series with dual Y-axis support. ([Documentation](https://heroui.pro/docs/native/components/composed-chart))
* **ChartTooltip**: Press-driven floating tooltip card with anchor positioning and multi-series rows. ([Documentation](https://heroui.pro/docs/native/components/chart-tooltip))
* **NumberPad**: Numeric keypad for PINs, codes, and amounts with a default 3×4 layout. ([Documentation](https://heroui.pro/docs/native/components/number-pad))
* **RadialChart**: Gauge and progress-ring visualizations with Skia-rendered arc rings. ([Documentation](https://heroui.pro/docs/native/components/radial-chart))
#### ComposedChart
A composed cartesian chart for multi-metric dashboards. `ComposedChart` wraps `victory-native`'s `CartesianChart` and reuses the themed Skia implementations from `BarChart`, `LineChart`, and `AreaChart` under one root — so you can mix bars, lines, and areas in a single chart with shared theming and animation cascading.
**Features:**
* Compound series parts — `Bar`, `BarGroup`, `StackedBar`, `Line`, `AnimatedLine`, `Area`, `StackedArea`, and `AreaRange`
* Dual Y-axis support via per-axis `yKeys`, `axisSide`, and `domain` configuration
* Bar-friendly default `domainPadding` and `animation="disable-all"` cascading to animated series parts
* Reuses existing `BarChart`, `LineChart`, and `AreaChart` Skia renderers — no new runtime dependencies
**Usage:**
```tsx
import { ComposedChart } from "heroui-native-pro";
const DATA = [
{ month: "Jan", revenue: 42000, orders: 180 },
{ month: "Feb", revenue: 51000, orders: 210 },
];
export function Example() {
return (
`$${(v / 1000).toFixed(0)}k` },
{ yKeys: ["orders"], axisSide: "right" },
]}
wrapperClassName="h-52"
>
{({ points, chartBounds }) => (
<>
>
)}
);
}
```
For complete documentation and examples, see the [ComposedChart component page](https://heroui.pro/docs/native/components/composed-chart).
#### ChartTooltip
A composable floating tooltip for cartesian chart press interactions. `ChartTooltip` follows press coordinates, clamps inside `chartBounds`, and renders a structured multi-series label card — replacing ad-hoc overlays built from `ChartCrosshair` and `ChartIndicator` alone.
**Features:**
* Compound parts — `Anchor`, `Header`, `Item`, `Indicator`, `Label`, and `Value`
* `useChartTooltipAnchor` hook for custom anchor wiring
* Follows press coordinates with placement, offset, visibility, and spring/timing animations
* Clamps tooltip position inside `chartBounds` on both axes
* Works with `LineChart`, `BarChart`, `AreaChart`, and `ComposedChart`
**Usage:**
```tsx
import { ChartTooltip, ComposedChart } from "heroui-native-pro";
import { useChartPressState } from "victory-native";
import { useState } from "react";
const { state, isActive } = useChartPressState({
x: "month",
y: { revenue: 0 },
});
const [chartBounds, setChartBounds] = useState(null);
{({ points, chartBounds: bounds }) => (
)}
Revenue
```
For complete documentation and examples, see the [ChartTooltip component page](https://heroui.pro/docs/native/components/chart-tooltip).
#### NumberPad
A numeric keypad for entering PINs, verification codes, and amounts. Built with HeroUI Native compound-component patterns, it ships a default 3×4 digit grid out of the box and supports full custom key composition when you need a different layout.
**Features:**
* Compound parts — `Row`, `Key`, `KeyLabel`, `Backspace`, and `Spacer`
* Auto-renders a default 3×4 digit grid when no children are provided
* Controlled and uncontrolled modes with `maxLength`, `onComplete`, and disabled state
* Press animations on keys; backspace deletes one character on press and clears on long-press
* Custom key content via render props; `Spacer` becomes an action key when given children
* Exported `useNumberPad` hook and full TypeScript types
**Usage:**
```tsx
import { NumberPad } from "heroui-native-pro";
import { useState } from "react";
export function Example() {
const [value, setValue] = useState("");
return (
console.log("PIN entered:", code)}
/>
);
}
```
For complete documentation and examples, see the [NumberPad component page](https://heroui.pro/docs/native/components/number-pad).
#### RadialChart
A radial chart for gauge, progress ring, and circular data visualizations. Built on `victory-native`'s `PolarChart` with custom `@shopify/react-native-skia` stroked arc rings, it supports configurable radii, angles, and per-datum colors with optional background tracks.
**Features:**
* Compound `RadialChart.Bar` API with rounded arc rings and optional full-domain background tracks
* Fixed gauge scales via `domain` (e.g. `[0, 100]`)
* Configurable `innerRadius`, `cornerRadius`, and per-datum colors through `colorKey`
* Exported types, styles, animation config, and constants
* Center labels and side legends composed by consumers via absolute overlays
**Usage:**
```tsx
import { RadialChart } from "heroui-native-pro";
const DATA = [{ name: "Score", value: 78, color: "#8b5cf6" }];
export function Example() {
return (
);
}
```
For complete documentation and examples, see the [RadialChart component page](https://heroui.pro/docs/native/components/radial-chart).
## Component Improvements
### NumberField Rapid-Tap Fix
Fixed a bug in the internal `useLongPressRepeat` hook where rapid successive taps on [NumberField](https://heroui.pro/docs/native/components/number-field) increment/decrement buttons could leave orphaned timers running, causing runaway `onChange` calls.
**Improvements:**
* `onPressIn` now calls `clear()` before scheduling a new timer, guaranteeing only one active repeat cycle
* Added `clear` to the `onPressIn` `useCallback` dependency array to keep the closure correct
## API Enhancements
### SplitView `skipInitialAnimation`
The [SplitView](https://heroui.pro/docs/native/components/split-view) component gains a `skipInitialAnimation` prop (default `true`) so the divider starts at its snap position without a spring on first render. Subsequent snaps and drags still use the spring animation as before.
**New Capability:**
```tsx
import { SplitView } from "heroui-native-pro";
// Animate the divider into place on first render
......
```
This is especially useful when you want the divider to visibly settle into place on first mount. The default (`true`) avoids the spring on mount and screen focus — the common case for split layouts that should appear already positioned.
### SplitView Constraint Reactivity
SplitView now recomputes constraints when `minHeight`, `maxHeight`, or `snapPoints` change, without requiring a container relayout. A stable `snapPointsKey` avoids unnecessary effect retriggers from inline array literals.
## Updated Documentation
The following documentation pages have been updated to reflect the changes in this release:
* [ComposedChart](https://heroui.pro/docs/native/components/composed-chart) — New component page with bar+line, stacked bar, and area+dashed-line examples
* [ChartTooltip](https://heroui.pro/docs/native/components/chart-tooltip) — New component page covering anchor positioning, multi-series rows, placement, and animations
* [NumberPad](https://heroui.pro/docs/native/components/number-pad) — New component page with basic, max length, custom styling, disabled, and custom composition variants
* [RadialChart](https://heroui.pro/docs/native/components/radial-chart) — New component page covering gauge, progress ring, and multi-ring layouts
* [SplitView](https://heroui.pro/docs/native/components/split-view) — Documents the new `skipInitialAnimation` prop and constraint reactivity behavior
## Links
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Component Documentation](https://heroui.pro/docs/native/components)
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview)
# 1.0.0-beta.6
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-6
> Adds FAB, FlipCard, and Timeline — a floating action button that expands into portal-rendered actions, a spring-driven 3D flip card, and a composable vertical event chronology.
July 9, 2026
The sixth beta of **HeroUI Native Pro** introduces three brand-new components: a portal-rendered `FAB` that expands into a list of actions with automatic screen-aware placement, a `FlipCard` that flips between two faces with a spring-driven 3D rotation, and a `Timeline` for event histories, activity logs, and milestone feeds. All three follow the same compound-component and animation-cascade conventions as the rest of the Pro library and slot into the existing `Buttons` and `Data Display` categories.
## Try It on Your Device
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## What's New
### New Components
This release introduces **3 new** components across buttons and data display:
* **FAB**: Floating action button that expands into a list of actions with automatic content placement, portal rendering, and a shared open/close animation. ([Documentation](https://heroui.pro/docs/native/components/fab))
* **FlipCard**: Pressable card that flips between a front and back face with a spring-driven 3D rotation. ([Documentation](https://heroui.pro/docs/native/components/flip-card))
* **Timeline**: Composable event-history component for feeds, activity logs, and vertical milestone chronologies. ([Documentation](https://heroui.pro/docs/native/components/timeline))
#### FAB
A floating action button that toggles a list of actions. The root resolves `placement` and `align` automatically from the trigger's screen position — a FAB in the bottom-right corner opens upwards with end alignment, a FAB in the top-left opens downwards with start alignment — and drives a shared `[idle, open, close]` progress that orchestrates the overlay, items, and trigger rotation. Content renders in a portal (backed by `FullWindowOverlay` on iOS) so it floats above everything else on screen.
**Features:**
* Compound parts — `FAB.Trigger`, `FAB.Portal`, `FAB.Overlay`, `FAB.Content`, `FAB.Item`, and `FAB.ItemLabel`
* Automatic `placement` and `align` resolution from the trigger's screen position, with manual overrides supported
* Staggered or simultaneous item appearance via `itemsAppearance`, with a tunable `animation.stagger.itemWindow` for stagger intensity
* Progress-driven animations on the trigger rotation, overlay opacity, and per-item `translate` / `scale`
* `useFAB` and `useFABAnimation` hooks for controlled state and custom backdrops (e.g. a blur backdrop built on `expo-blur`)
* iOS `FullWindowOverlay` portal support through the optional `react-native-screens` peer dependency
* Cascading `animation="disable-all"` to skip animation across the entire subtree
**Usage:**
```tsx
import { FAB } from "heroui-native-pro";
export function Example() {
return (
New messageNew label
);
}
```
For complete documentation and examples, see the [FAB component page](https://heroui.pro/docs/native/components/fab).
#### FlipCard
A pressable card that flips between two faces with a spring-driven 3D rotation. Tapping the root toggles the flip state (controllable via `isFlipped` + `onFlipChange` or uncontrolled via `defaultFlipped`), and the off-screen face is removed from hit testing and the accessibility tree so hidden interactive content can't intercept touches. Use it for reveal interactions on cards — think travel destinations, trading cards, or hidden statistics.
**Features:**
* Compound parts — `FlipCard.Front` and `FlipCard.Back`
* Controlled (`isFlipped` + `onFlipChange`) and uncontrolled (`defaultFlipped`) modes, with `isPressDisabled` to opt out of tap-to-flip
* Configurable `direction` (`"horizontal"` = rotateY, `"vertical"` = rotateX) and `rotation` (`"normal"` / `"reverse"`)
* True 3D feel with perspective and a mid-flip scale dip that de-emphasizes the flat edge
* Accessibility: off-screen face is hidden via `accessibilityElementsHidden` and `importantForAccessibility`; the root exposes `accessibilityRole="button"` and its selected state
* Customizable flip spring through `animation.progress.springConfig`, with `"disable-all"` and per-face `"disabled"` escape hatches
* `useFlipCard` and `useFlipCardAnimation` hooks for advanced consumers building custom progress-driven parts
**Usage:**
```tsx
import { FlipCard } from "heroui-native-pro";
export function Example() {
return (
{/* front face content */}
{/* back face content */}
);
}
```
For complete documentation and examples, see the [FlipCard component page](https://heroui.pro/docs/native/components/flip-card).
#### Timeline
A composable event-history component for feeds, activity logs, and vertical milestone chronologies. The root manages `size`, `density`, and default `itemAlign` context, and connectors use per-item layout measurements to bridge variable-height rows without hard-coding gaps.
**Features:**
* Compound parts — `Timeline.Item`, `Timeline.Leading`, `Timeline.Rail`, `Timeline.Marker`, `Timeline.Connector`, `Timeline.Content`, `Timeline.Title`, and `Timeline.Description`
* Per-item `status` tones — `"default"`, `"muted"`, `"current"`, `"success"`, `"warning"`, and `"danger"` — drive the marker color
* `size` (`"sm"` / `"md"` / `"lg"`) and `density` (`"compact"` / `"comfortable"`) scale markers, text, and vertical rhythm together
* Optional `Timeline.Leading` column for timestamps or short metadata to the left of the rail
* Connectors are positioned via layout measurements so they bridge variable-height items cleanly, and are automatically omitted on the first item
* Custom markers via icon children on `Timeline.Marker`; connector can be forced on item 0 with `force`
* Built on a lower-level primitives layer (`src/primitives/timeline`) with a styled Uniwind layer on top
**Usage:**
```tsx
import { Timeline } from "heroui-native-pro";
export function Example() {
return (
Order placedWe received your order.ProcessingPreparing your items.
);
}
```
For complete documentation and examples, see the [Timeline component page](https://heroui.pro/docs/native/components/timeline).
## Updated Documentation
The following documentation pages have been updated to reflect the changes in this release:
* [FAB](https://heroui.pro/docs/native/components/fab) — New component page covering auto placement, manual overrides, staggered vs. simultaneous item appearance, controlled state, custom blur backdrops, and animation configuration
* [FlipCard](https://heroui.pro/docs/native/components/flip-card) — New component page with horizontal / vertical directions, reverse rotation, controlled mode, and custom spring configuration
* [Timeline](https://heroui.pro/docs/native/components/timeline) — New component page covering statuses, leading columns, custom markers, sizes, and density variants
## Links
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Component Documentation](https://heroui.pro/docs/native/components)
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview)
# 1.0.0-beta.7
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-7
> Adds the Brutalism theme and extracts every component's styles into overridable BEM CSS, so a single import can restyle the entire library.
July 21, 2026
The seventh beta of **HeroUI Native Pro** introduces a proper theming system. Every component's styles have been extracted into overridable **BEM CSS** classes, which unlocks the first premium theme: **Brutalism** — bold borders, hard shadows, and display typography that restyle the entire library with a single import. Nothing changes in your component code; drop the theme stylesheet into your `global.css` and the whole app follows.
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## What's New
### Themeable Component Styles
Every HeroUI Native Pro component now renders through named **BEM CSS** classes instead of inline-only styles. Each component exposes a stable `block__element--modifier` class surface (e.g. `button`, `button__label`, `button--pressed`) that a theme stylesheet can target and override — without touching a single line of component markup.
This is the foundation the theme system builds on: a theme is just a CSS file that redefines those BEM classes and design tokens. Because the class surface is stable and component-scoped, themes stay decoupled from component internals, and you can override as little or as much as you like.
**What this enables:**
* **Single-import theming** — add one `@import` to `global.css` to restyle every component at once
* **Stable BEM class surface** — predictable `block__element--modifier` hooks per component for targeted overrides
* **Token + class layering** — themes redefine design tokens and BEM classes, so you can nudge one component or reskin the whole library
* **Zero code changes** — theming is purely CSS; component APIs and markup are untouched
### Brutalism Theme
The new **Brutalism** theme restyles every component with bold borders, hard shadows, and display typography for a raw, high-contrast look. It ships with HeroUI Native Pro and is applied entirely through CSS — no per-component wiring.
**Features:**
* Restyles the full component library through the new BEM CSS layer
* High-contrast surfaces with bold borders and hard (non-blurred) shadows
* Monospace body typography plus a dedicated `--brutalism-font-display` display font for labels
* Full light and dark mode support
* Applied with a single `@import`, layered on top of the default styles
**Usage:**
Import the theme in your `global.css`, right after the HeroUI Native Pro styles:
```css title="global.css"
@import 'tailwindcss';
@import 'uniwind';
@import 'heroui-native/styles';
@import 'heroui-native-pro/styles';
/* [!code highlight] */
@import 'heroui-native-pro/themes/brutalism';
@source './node_modules/heroui-native/lib';
@source './node_modules/heroui-native-pro/lib';
```
For complete setup instructions, font mapping, and a live preview, see the [Brutalism theme page](https://heroui.pro/docs/native/themes/brutalism).
## Updated Documentation
The following documentation has been added or updated to reflect the changes in this release:
* [Themes](https://heroui.pro/docs/native/themes) — New overview page showcasing every HeroUI Native Pro theme
* [Default](https://heroui.pro/docs/native/themes/default) — Documents the signature out-of-the-box look and its `@source` setup
* [Brutalism](https://heroui.pro/docs/native/themes/brutalism) — New theme page covering installation, the `heroui-native-pro/themes/brutalism` import, and font mapping for the monospace body and display label fonts
## Links
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Themes](https://heroui.pro/docs/native/themes)
* [Brutalism Theme](https://heroui.pro/docs/native/themes/brutalism)
* [Component Documentation](https://heroui.pro/docs/native/components)
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview)
# 1.0.0-beta.8
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-8
> Adds the Glass theme with real iOS blur, theme-aware background layers across 17 components, a blur variant for FAB.Overlay, themeable chart ramp tokens, and a new NumberPad highlightColor prop.
July 28, 2026
The eighth beta of **HeroUI Native Pro** ships the second premium theme: **Glass** — translucent surfaces backed by a real native blur layer on iOS. Making that possible required a new primitive across the library: components now mount a **theme-aware background layer** so semi-transparent tokens have something to frost against, exposed as a compound part you can retint, replace, or remove. `FAB.Overlay` also gains a built-in `blur` variant, and the chart palette moved onto `--chart-*` tokens so a theme can redefine the whole ramp. There is **one breaking change**: the `NumberPad` pressed highlight is now an inline style driven by a new `highlightColor` prop.
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## What's New
### Glass Theme
The new **Glass** theme restyles every component with frosted translucent surfaces. Unlike `brutalism`, it is not CSS-only: overlays, fields, and surfaces mount a real blur layer at runtime, powered by the optional [`expo-blur`](https://docs.expo.dev/versions/latest/sdk/blur-view/) package.
**Features:**
* Full light and dark token sets, including a dedicated chart ramp
* Real native backdrop blur on iOS, with an automatic opaque fallback everywhere else
* Flips the default `Overlay` variant to `blur` on `BottomSheet`, `Dialog`, and `FAB` — no code changes required
* Applied with a single `@import`, layered on top of the default styles
* Ships its own `INSTALL.md` covering setup and the Android/web fallback
**Usage:**
Install the blur provider, then import the theme in your `global.css` after the HeroUI Native styles:
```bash
npx expo install expo-blur
```
```css title="global.css"
@import 'tailwindcss';
@import 'uniwind';
@import 'heroui-native/styles';
@import 'heroui-native-pro/styles';
/* [!code highlight] */
@import 'heroui-native-pro/themes/glass';
@source './node_modules/heroui-native/lib';
@source './node_modules/heroui-native-pro/lib';
```
Real backdrop blur only exists on iOS. On Android, on web, and when `expo-blur` is not installed, every blur layer paints its `fallbackColor` token alpha-composited over `--background` instead — an intentionally opaque approximation, since a transparent layer would leave surfaces looking washed out. You never need to branch on the platform yourself.
For complete setup instructions, platform behavior, the `Input` structure change, and a live preview, see the [Glass theme page](https://heroui.pro/docs/native/themes/glass).
### Theme-Aware Background Layers
Translucent themes need a place to put the frosted layer, so **17 components** now render an absolute-fill background container behind their surface. The layer only mounts when the active theme registers default background content — under `default` and `brutalism` nothing is rendered, so existing apps are pixel-identical.
Each layer is a real compound part, and its owner takes a `background` prop with three behaviors:
* `undefined` — render the theme-aware default
* a custom node — replace the default layer
* `null` — remove the layer entirely
Each part is named after the surface it paints behind, so the layer is always easy to find from the part that owns it:
| Background part | Default layer scope |
| -------------------------------------- | ------------------------------------------------------------------- |
| `Badge.Background` | `secondary` variant, plus `primary` / `soft` with `color="default"` |
| `ChartCrosshair.ValueBackground` | `default` value-pill variant |
| `EmptyState.MediaBackground` | `icon` media variant |
| `FAB.ItemBackground` | Action items |
| `NumberPad.KeyBackground` | Keys — `Backspace` and `Spacer` default to `null` |
| `NumberStepper.RootBackground` | Root surface |
| `ProgressBar.TrackBackground` | Track surface |
| `ProgressButton.Background` | Button surface |
| `RadioButtonGroup.ItemBackground` | Unselected `secondary` variant items |
| `SlideButton.ContainerBackground` | Container surface |
| `WheelPicker.IndicatorBackground` | Highlight band |
| `WheelPickerGroup.IndicatorBackground` | Shared highlight band |
| `Widget.Background` | Widget root — `Widget.Content` keeps only its translucent tint |
`ChartTooltip`, `CalendarYearPicker`, and `FlipCard` gain the same layer through their own background parts, and `ToggleButton` forwards its `background` prop to the core `Button.Background` for the unselected `default` variant.
**Usage:**
```tsx
import { GlassView } from 'heroui-native';
import { Widget } from 'heroui-native-pro';
// Retint the frosted layer
}
>
{/* content */}
// Opt out entirely and paint your own surface
```
Blur layers are expensive, so avoid stacking them — a `GlassView` inside an already-frosted surface renders a second native blur pass for no visual gain.
### FAB Blur Backdrop
[`FAB.Overlay`](https://heroui.pro/docs/native/components/fab) gains a `variant` prop. The `default` variant paints the solid `--backdrop` fill it always has; the new `blur` variant renders an animated blur layer instead, driven by the same shared `[idle, open, close]` progress. Under the Glass theme it is the default.
```tsx
... {/* [!code highlight] */}
...
```
**Improvements:**
* `variant="blur"` animates **blur intensity** rather than overlay opacity, so `isAnimatedStyleActive` defaults to `false` for it — set it back to `true` if you drive a custom opacity animation through `animation`
* `blurViewProps` forwards to the underlying BlurView, where `intensity` acts as the maximum animated intensity (defaults to `50` in light mode and `75` in dark)
* A requested `blur` variant is automatically downgraded to `default` on Android, on web, and when `expo-blur` is missing
* Pin `variant="default"` to keep the solid backdrop even under Glass
This brings `FAB` in line with `BottomSheet.Overlay` and `Dialog.Overlay`, which expose the same `variant` and `blurViewProps` API.
## API Enhancements
### Themeable Chart Ramp
The chart palette is no longer derived from `--accent` at render time. It now reads five dedicated tokens — `--chart-1` through `--chart-5` — which still default to accent-derived values, so nothing changes visually unless a theme overrides them. Glass ships its own cool-toned ramp on top of these tokens, and your own themes can redefine as many steps as they like:
```css title="global.css"
@theme {
--chart-1: oklch(0.65 0.06 240);
--chart-2: oklch(0.55 0.05 240);
--chart-3: oklch(0.45 0.04 240);
--chart-4: oklch(0.35 0.03 240);
--chart-5: oklch(0.75 0.06 240);
}
```
Every chart component — `AreaChart`, `BarChart`, `LineChart`, `ComposedChart`, `PieChart`, `RadarChart`, and `RadialChart` — picks up the ramp automatically.
## Dependencies
### expo-blur (optional)
`expo-blur` is loaded through an optional wrapper, so it stays a **soft dependency**: the package is only required if you use the Glass theme or a `blur` overlay variant, and its absence degrades gracefully to the opaque fallback rather than throwing.
```bash
npx expo install expo-blur
```
Bare React Native projects can install it too — `expo-blur` works outside Expo apps as long as [`expo-modules-core`](https://docs.expo.dev/bare/installing-expo-modules/) is set up.
## ⚠️ Breaking Changes
### NumberPad Pressed Highlight Moved to an Inline Style
`NumberPad.Key` no longer carries `data-[pressed=true]:bg-default-hover` in its base class list. The pressed tint is now applied as an inline `{ backgroundColor: highlightColor }` appended last to the key's `containerStyle`, so a theme can drive the highlight without fighting the class layer.
Two consequences for existing code:
* A pressed background override passed through `className` (e.g. `data-[pressed=true]:bg-red-500`) is now silently beaten by the inline style, since inline styles always win in React Native
* `numberPadClassNames.key()` returns a different string, so anything composing it sees changed output
There is **no deprecation warning** — the class simply stops taking effect — so audit any `data-[pressed=true]:bg-*` classes on number pad keys when you upgrade.
**Migration:**
Move pressed-state background overrides off `className` and onto the new `highlightColor` prop:
```tsx
// Before
// After
```
Everything else in this release is additive. Background layers only mount when the active theme registers default background content, so `default` and `brutalism` render exactly as before, and the `--chart-*` tokens keep their previous accent-derived values as defaults.
## Updated Documentation
The following documentation has been added or updated to reflect the changes in this release:
* [Glass](https://heroui.pro/docs/native/themes/glass) — New theme page covering `expo-blur` setup, the `heroui-native-pro/themes/glass` import, platform behavior and fallbacks, the `Input` structure change, blur backdrops, and layer customization
* [Themes](https://heroui.pro/docs/native/themes) — Overview page now showcases the Glass theme alongside Default and Brutalism
* [FAB](https://heroui.pro/docs/native/components/fab) — Documents the `Overlay` `variant` / `blurViewProps` props and the new `FAB.ItemBackground` part
* [Badge](https://heroui.pro/docs/native/components/badge) — Documents `Badge.Background` and the variants its default layer covers
* [EmptyState](https://heroui.pro/docs/native/components/empty-state) — Documents `EmptyState.MediaBackground` for the `icon` media variant
* [ChartCrosshair](https://heroui.pro/docs/native/components/chart-crosshair) — Documents `ChartCrosshair.ValueBackground` for the value pill
* [ProgressBar](https://heroui.pro/docs/native/components/progress-bar) — Documents `ProgressBar.TrackBackground`
* [ProgressButton](https://heroui.pro/docs/native/components/progress-button) — Documents `ProgressButton.Background`
* [SlideButton](https://heroui.pro/docs/native/components/slide-button) — Documents `SlideButton.ContainerBackground`
* [ToggleButton](https://heroui.pro/docs/native/components/toggle-button) — Documents the `background` prop forwarded to the core `Button.Background`
* [NumberPad](https://heroui.pro/docs/native/components/number-pad) — Documents `NumberPad.KeyBackground` and the transparent `Backspace` / `Spacer` defaults
* [NumberStepper](https://heroui.pro/docs/native/components/number-stepper) — Documents `NumberStepper.RootBackground`
* [RadioButtonGroup](https://heroui.pro/docs/native/components/radio-button-group) — Documents `RadioButtonGroup.ItemBackground` for unselected `secondary` items
* [WheelPicker](https://heroui.pro/docs/native/components/wheel-picker) — Documents `WheelPicker.IndicatorBackground`
* [WheelPickerGroup](https://heroui.pro/docs/native/components/wheel-picker-group) — Documents `WheelPickerGroup.IndicatorBackground`
## Links
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Themes](https://heroui.pro/docs/native/themes)
* [Glass Theme](https://heroui.pro/docs/native/themes/glass)
* [Component Documentation](https://heroui.pro/docs/native/components)
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview)
# 1.0.0-beta.9
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/beta-9
> Adds Agenda, Autocomplete, and ComboBox, plus RTL layout support across components with example-app localization for English, Arabic, and Hebrew.
August 11, 2026
The ninth beta of **HeroUI Native Pro** ships three new surfaces — a full mobile `Agenda` calendar with day, week, and month views, plus `Autocomplete` and `ComboBox` for searchable selection — and brings RTL layout support across the library. Direction-aware styles, animations, and layout logic now flip correctly for RTL locales, with example-app localization covering English, Arabic, and Hebrew.
## Try It on Your Device
## Installation
Follow the [installation guide](https://heroui.pro/docs/native/getting-started/installation) to authenticate the private registry with your `HEROUI_PERSONAL_TOKEN`, install `heroui-native-pro`, and wire up `HeroUINativeProvider`.
## What's New
### New Components
This release introduces **3 new** components across date-and-time and forms:
* **Agenda**: Full mobile calendar surface with a collapsible month header, horizontally paged day/week/month body, and draggable, resizable events. ([Documentation](https://heroui.pro/docs/native/components/agenda))
* **Autocomplete**: Searchable select with in-overlay filtering, built on heroui-native `Select` and `SearchField`. ([Documentation](https://heroui.pro/docs/native/components/autocomplete))
* **ComboBox**: Inline input plus listbox popover aligned with the web ComboBox anatomy. ([Documentation](https://heroui.pro/docs/native/components/combo-box))
#### Agenda
A full calendar surface for mobile scheduling UIs. State stays external via `useAgenda`; the root spreads that state and ships a complete default composition when used childless — collapsible month header, horizontally paged day/week/month body, and event cards that drag and resize when the optional peer is installed.
**Features:**
* Compound parts — `Agenda.Header`, `Agenda.Calendar`, `Agenda.Body`, `Agenda.TimeGrid`, `Agenda.Event`, `Agenda.MonthGrid`, `Agenda.ViewSelector`, and related hooks
* Day, week, and month views with SplitView header collapse, pager sync, and content fade on view change
* Event drag and resize with drop time guides via optional peer `react-native-reanimated-dnd` — events still render without it
* Collapse chain, deferred event layers, and layout transitions on event chips
* Dates use `@internationalized/date` (`CalendarDate` / `CalendarDateTime`); the Agenda never mutates your events array — apply move/resize intents back into your state
**Usage:**
```tsx
import type { CalendarDateTime } from "@internationalized/date";
import { Agenda, useAgenda, type AgendaEvent } from "heroui-native-pro";
import { useState } from "react";
export function Example() {
const [events, setEvents] = useState(initialEvents);
const applyChange = (id: string, start: CalendarDateTime, end: CalendarDateTime) => {
setEvents((prev) =>
prev.map((event) => (event.id === id ? { ...event, start, end } : event)),
);
};
const agenda = useAgenda({
events,
onEventMove: applyChange,
onEventResize: applyChange,
});
return ;
}
```
For complete documentation and examples, see the [Agenda component page](https://heroui.pro/docs/native/components/agenda).
#### Autocomplete
A searchable select where filtering happens inside the overlay. Selection lives on the trigger while the search input renders in the portaled content — ideal when you want a familiar select field with type-ahead, not a free-text combo box.
**Features:**
* Built on heroui-native `Select` and in-content `SearchField`
* Single and multiple selection, controlled and uncontrolled modes
* Custom `filter`, clear button, empty state, and section labels
* Popover, dialog, and bottom-sheet presentations
* Case- and diacritic-insensitive "contains" match by default
**Usage:**
```tsx
import { Autocomplete } from "heroui-native-pro";
import { Label } from "heroui-native";
export function Example() {
return (
);
}
```
For complete documentation and examples, see the [Autocomplete component page](https://heroui.pro/docs/native/components/autocomplete).
#### ComboBox
An inline text input combined with a listbox popover, mirroring the HeroUI web ComboBox anatomy. Typing filters the collection; selecting an item commits its label into the input. Prefer `Autocomplete` when filtering should live inside the overlay instead.
**Features:**
* Inline `InputGroup` + popover listbox with filtering and clear button
* Single and multiple selection with controlled selection, open, and input APIs
* Sections via `ComboBox.ListLabel`, empty state, and custom `filter`
* Popover presentation only — place the field in the upper half of the screen (or use `placement="top"` on `ComboBox.Content`) to keep the list above the keyboard
**Usage:**
```tsx
import { ComboBox } from "heroui-native-pro";
import { Label } from "heroui-native";
export function Example() {
return (
);
}
```
For complete documentation and examples, see the [ComboBox component page](https://heroui.pro/docs/native/components/combo-box).
### RTL Support
Components and the example app now respect right-to-left locales. Direction-aware CSS, animations, and layout logic flip correctly when the app direction is RTL, including Android-specific fixes for agenda and range calendar.
**Supported surfaces:**
* Charts, pickers, FAB, number/progress, slider, rating, stepper, and timeline
* Wheel pickers, agenda, and range calendar
* Example app i18n with Lingui — `en` / `ar` / `he` catalogs and locale switching
Existing LTR usage is unchanged; RTL behavior activates with locale and layout direction. Validate key surfaces on iOS and Android in both LTR and RTL when shipping localized apps.
## Dependencies
### react-native-reanimated-dnd (optional)
Declared as an optional peer dependency (`>=2.0.0`) and loaded through an optional import helper. Required only for Agenda event drag and resize; without it, events still render but cannot be moved or resized.
```bash
npx expo install react-native-reanimated-dnd
```
### Other Dependency Upgrades
* `heroui-native`: peer dependency bumped to `^1.0.8`
## Updated Documentation
The following documentation has been added or updated to reflect the changes in this release:
* [Agenda](https://heroui.pro/docs/native/components/agenda) — New component page covering `useAgenda`, day/week/month views, event drag/resize, and composition
* [Autocomplete](https://heroui.pro/docs/native/components/autocomplete) — New component page covering searchable select, presentations, filtering, and selection modes
* [ComboBox](https://heroui.pro/docs/native/components/combo-box) — New component page covering inline input + listbox, filtering, and controlled APIs
## Links
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Component Documentation](https://heroui.pro/docs/native/components)
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview)
# All Releases
**Category**: native
**URL**: https://heroui.pro/docs/native/releases
> All updates and changes to HeroUI Native, including new features, fixes, and breaking changes.
## Releases
### 1.0.0-beta.10
**August 2026**
Introduces four new components — `Table`, `Carousel`, `MorphButton`, and `PhoneNumberField` — bringing selectable, sortable tabular data, a horizontal snap pager with navigation, interpolating dots, and thumbnails, a pressable that springs between collapsed and expanded content, and an international phone input with as-you-type formatting. Carousel navigation buttons also gain a theme-aware background layer so glass chevrons frost correctly.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-10)
### 1.0.0-beta.9
**August 2026**
Introduces three new components — `Agenda`, `Autocomplete`, and `ComboBox` — bringing a full mobile calendar surface with day/week/month views and draggable events, plus searchable select and inline combo-box primitives. Also adds RTL layout support across components, with example-app localization for English, Arabic, and Hebrew.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-9)
### 1.0.0-beta.8
**July 2026**
Adds the **Glass** theme — frosted translucent surfaces backed by a real native blur layer on iOS, with an automatic opaque fallback elsewhere. Introduces theme-aware background layers across 17 components (`Widget`, `Badge`, `SlideButton`, `NumberPad`, `WheelPicker`, and more), each replaceable or removable via a `background` prop, plus a `blur` variant for `FAB.Overlay` and themeable `--chart-*` ramp tokens. Also moves the `NumberPad` pressed highlight from a class to an inline style behind a new `highlightColor` prop.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-8)
### 1.0.0-beta.7
**July 2026**
Introduces a theming system for HeroUI Native Pro: every component's styles are extracted into overridable BEM CSS, unlocking the first premium theme — **Brutalism** — with bold borders, hard shadows, and display typography that restyle the entire library through a single import.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-7)
### 1.0.0-beta.6
**July 2026**
Adds three new components — `FAB`, `FlipCard`, and `Timeline` — bringing a portal-rendered floating action button with automatic screen-aware placement, a spring-driven 3D flip card with front/back faces, and a composable vertical event chronology with per-item status tones to HeroUI Native Pro.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-6)
### Style Guides Native
**June 2026**
Pro Style Guides now support HeroUI Native — live iOS/Android preview in-browser, QR scan to open your theme in the HeroUI Native app, and Native CSS import/export for Uniwind.
### Fitness App Template
**June 2026**
A new **HeroUI Native Pro** template: a mobile Fitness App with activity rings, featured workouts, daily stat widgets, and Today / Train / Profile navigation — built entirely from Pro components.
[Read full release notes →](https://heroui.pro/docs/native/releases/template-fitness-app-1.0.0)
### Theme Builder Native
**June 2026**
The Pro Theme Builder now supports HeroUI Native — live iOS/Android preview in-browser, QR scan to open your theme in the HeroUI Native app, and Native CSS import/export for Uniwind.
[Read full release notes →](https://heroui.pro/docs/native/releases/theme-builder-native)
### MCP Server v0.2.0
**May 2026**
The Pro Native MCP server now serves both `heroui-native-pro` and `heroui-native` from a single connection — consolidating four MCPs and skills across OSS and Pro into one unified setup.
[Read full release notes →](https://heroui.pro/docs/native/releases/mcp-0.2.0)
### Crypto Wallet Template
**May 2026**
The first **HeroUI Native Pro** template: a mobile Crypto Wallet with portfolio, assets, transactions, and send/receive flows — built entirely from Pro components.
[Read full release notes →](https://heroui.pro/docs/native/releases/template-crypto-wallet-1.0.0)
### 1.0.0-beta.5
**June 2026**
Adds four new components — `ComposedChart`, `ChartTooltip`, `NumberPad`, and `RadialChart` — extending multi-series cartesian dashboards with press-driven floating tooltips, numeric keypad entry, and gauge-style radial visualizations. Also improves `SplitView` mount behavior with `skipInitialAnimation` and fixes runaway `onChange` events in `NumberField` on rapid taps.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-5)
### 1.0.0-beta.4
**June 2026**
Adds eight new components — `PieChart`, `RadarChart`, `WheelPicker`, `WheelPickerGroup`, `TimePicker`, `WheelTimePicker`, `DateTimePicker`, and `WheelDateTimePicker` — completing the polar-chart family and bringing a wheel-picker foundation with a full time and date-time selection stack to HeroUI Native Pro. Also refactors component labels and indicators onto `*-soft-foreground` tokens and fixes `NumberStepper` digit jitter plus `WheelPicker` overscroll and re-render edge cases.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-4)
### 1.0.0-beta.3
**May 2026**
Adds five new components — `AreaChart`, `ChartCrosshair`, `ChartIndicator`, `EmptyState`, and `Segment` — unifying press-driven overlays across every cartesian chart and bringing zero-state messaging and segmented selection to HeroUI Native Pro. Also removes the old `LineChart.Tooltip` / `LineChart.Crosshair` compounds.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-3)
### 1.0.0-beta.2
**April 2026**
Adds seven new components — `Badge`, `ProgressBar`, `ProgressCircle`, `ToggleButton`, `ToggleButtonGroup`, `BarChart`, and `Widget` — bringing data display, feedback, segmented selection, bar charting, and dashboard surfaces to HeroUI Native Pro. Also fixes a `CalendarYearPicker` sync issue when paging via nav buttons and locks the date picker `Trigger` variants for consistent styling across `DateField`, `DatePicker`, and `DateRangePicker`.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-2)
### 1.0.0-beta.1
**April 2026**
The first public beta of **HeroUI Native Pro**. Premium React Native components across Date & Time (Calendar, RangeCalendar, DateField, DatePicker, DateRangePicker), Buttons (SlideButton, ProgressButton, SocialAuthButton), Forms (NumberField, NumberStepper, RadioButtonGroup), Navigation (Stepper, SplitView), and Feedback (TrendChip). Ships with a dedicated MCP server, agent skill, and design-taste skill for AI-assisted native development.
[Read full release notes →](https://heroui.pro/docs/native/releases/beta-1)
# MCP Server v0.2.0
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/mcp-0.2.0
> Unified Native MCP — one server covers both heroui-native-pro and heroui-native. No more juggling separate OSS and Pro MCPs.
May 2026
The Pro Native MCP server now serves **both** `heroui-native-pro` and `heroui-native` from a single connection. Pro customers no longer need a separate `@heroui/native-mcp` installed — everything is consolidated.
## What's New
* **`list_components`** returns Pro + OSS native components in a single sectioned response
* **`get_component_docs`** accepts any component name from either package and routes to the correct backend
* **`get_docs`** serves guides from both Pro (`/pro/docs/native/...`) and OSS (`/docs/native/...`) documentation
* **`get_theme_variables`** (new) — get default native theme tokens
## Why
Customer feedback was clear: 4 MCP servers + 4 skills across OSS and Pro felt fragmented and bloated. This release cuts the native setup from 2 MCPs to 1.
## Links
* [MCP Server Setup](https://heroui.pro/docs/native/getting-started/mcp-server)
* [Agent Skills](https://heroui.pro/docs/native/getting-started/agent-skills)
* [Component Documentation](https://heroui.pro/docs/native/components)
# Crypto Wallet v1.0.0
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/template-crypto-wallet-1.0.0
> First HeroUI Native Pro template — a fully-built mobile crypto wallet with portfolio, assets, and transaction views.
May 2026
The first **HeroUI Native Pro** template lands: a mobile **Crypto Wallet** built entirely from Pro components. Drop it into Expo Go to see the full UI, then download the source to start building from a real screen instead of a blank app.
## What's Inside
* **Portfolio overview** with balance, holdings, and trend visualization
* **Asset detail** screens powered by `LineChart` + `ChartCrosshair` press interactions
* **Transactions** list with filtering and status surfaces
* **Send / receive** flows wired up with `NumberField`, `SlideButton`, and `ProgressButton`
* Themed end-to-end with HeroUI Native Pro tokens — drop in your own brand color and the whole app follows
## Get the Template
Head to the [Templates page](https://heroui.pro/docs/native/templates) and hit **Download** on the Crypto Wallet card (requires an active HeroUI Native Pro license).
## Links
* [Templates](https://heroui.pro/docs/native/templates)
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Component Documentation](https://heroui.pro/docs/native/components)
# Fitness App v1.0.0
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/template-fitness-app-1.0.0
> A HeroUI Native Pro template — Stride, a workout & activity app with activity rings, a program library, full in-session logging, and a celebratory summary screen.
June 2026
Meet **Stride**, the second template in the HeroUI Native Pro library. Unlike a generic dashboard shell, Stride is a complete workout flow — from "what should I do today?" on the home screen all the way through logging individual sets and seeing a summary screen when you re-rack. Seven screens, real navigation, real state, and a `heroui-native-pro` component on practically every surface.
## App Map
Three tabs and three pushed screens, wired with Expo Router:
* **Today** (`(tabs)/(home)/index`) — activity rings hero, featured workout card, daily stats grid
* **Activity drill-down** (`(tabs)/(home)/activity`) — pushed from the Steps tile with a step `ProgressCircle`, animated `NumberFlow` count, hourly bar chart, and supplementary stat tiles
* **Train** (`(tabs)/(train)/index`) — featured program, category filter, and a program list
* **Profile** (`(tabs)/(profile)/index`) — avatar with Pro badge, lifetime stats, achievements row, a gradient membership card, and a real settings list
* **Program detail** (`program/[id]`) — parallax hero image, ratings, stat surfaces, and an accordion of exercises
* **Session** (`session`) — the active-workout screen with a live timer, header progress bar, set rows, and a bottom-sheet logger
* **Summary** (`summary`) — a modal screen rewarding the user with effort ring, splits, rating, and a "hold to share" action
Tabs use the platform-correct shell: `expo-router`'s `NativeTabs` on iOS, a custom `FloatingTabBar` (rounded `Surface` pill, haptic taps, `ScreenFade` gradient underneath) on Android.
## Today, At a Glance
The home tab opens with `RadialChart`-powered activity rings: Move (kcal), Steps, and Active (minutes), each color-tied to a semantic metric token. An animated `NumberFlow` shows today's step count inside the rings; the legend mirrors the rings as small color dots with `value / goal unit` lines.
Below the rings, a `Card` showcases the featured program — image, metadata (45 min · 420 kcal · Intermediate), and a primary `Button` with a play icon — which navigates to the program detail screen. Underneath, a 2×2 grid of `StatTile`s surfaces Steps, Calories, Active minutes, and Floors; tapping Steps pushes into the activity detail screen.
## Activity Detail (Apple Health-style)
A focused drill-down screen with:
* A `ProgressCircle` centered on the step goal
* A large animated `NumberFlow` step count with timeline labels (`12am → 6am → 12pm → 6pm → 12am`)
* A 24-bar hourly distribution chart driven by a custom `BarsChart` over mocked hourly steps
* Three editorial stat cards (Distance, Calories, Climbed) rendered with `NumberValue` for locale-aware formatting
## Train Tab
Built around content discovery:
* A full-bleed **`FeaturedProgram`** card with a gradient overlay and metadata chips
* A horizontally-scrolling **`CategoryFilter`** using `ToggleButtonGroup` / `ToggleButton` (All · Strength · Cardio · HIIT · Yoga · Mobility) with single-select, disallow-empty selection
* A **`ProgramCard`** list with thumbnail, category eyebrow, `New` / `Pro` chips, metadata line, and either a `ProgressBar` (if you've started it) or a read-only `Rating` (if you haven't)
The list filters reactively from the selected category, with five seeded programs spanning every category.
## Program Detail
Tapping a card pushes a detail screen built for sell-through:
* A **parallax hero image** that stretches and scales on overscroll using Reanimated `useScrollOffset` + `interpolate`
* Category / level / Pro `Chip`s and a `Rating` with `(ratingCount)`
* A three-stat `Surface` row: minutes · kcal · moves
* An optional resumption `ProgressBar` when `program.progress > 0`
* An **`Accordion`** of exercises showing sets × reps (or sets × duration for hold-based moves) with expandable instructions
* A pinned-to-bottom **`SlideButton`** ("Slide to start") with an accent overlay state, sitting on top of a background-to-transparent `LinearGradient` fade
## Live Session Screen
The most interactive screen in the template, and the showcase for several Pro components at once:
* A **live elapsed timer** in the header via `DurationFlow` (animated digit transitions)
* A **`ProgressBar` in the header** tracking completed sets across the entire session
* A `Chip` showing "current / total" exercise position
* One set row per planned set; tapping a row opens a **`BottomSheet`** log sheet
* Inside the sheet, two side-by-side \*\*`NumberStepper`\*\*s for weight (kg) and reps with `step={2.5}` / `step={1}`, or a centered duration display for hold-based moves
* A primary **Log set** / **Save set** / **Mark as not done** action set
* A **Next exercise** button between exercises, swapped for a **`SlideButton`** ("Slide to finish") on the final one
* Haptic feedback on every meaningful interaction via `fireHaptic`
## Workout Summary Modal
Presented as a modal route on completion:
* An "Effort" `ActivityRingStack` (single `RadialChart` ring) with a large animated kcal `NumberFlow` in the center
* A row of three stat tiles: minutes, duration (`mm:ss`), and moves
* A "How did it feel?" 5-star `Rating`
* A **`ProgressButton`** with `holdDuration={1100}` for a "Hold to share" celebratory action
* A `tertiary` Done button that pops back to the tabs and fires a notification haptic
## Profile
Personal-account UI patterns you'll lift into your own app:
* **`Avatar`** with `Avatar.Image` + fallback initials, anchored with a `Badge` for Pro status
* A **stats row** in a `Surface` showing total workouts, day streak, and weekly goal
* A horizontally-scrolling **achievements row** of locked / unlocked badges (icon + title)
* A gradient **Membership card** ("Stride Pro") with a hand-drawn SVG dot pattern, perks list, and "Manage plan" CTA — scoped to light theme via `ScopedTheme` so the artwork stays consistent in dark mode
* A real **`ListGroup`** settings block: notifications `Switch`, goals link with chevron, and a `ThemeSelect` for Light / Dark / System
## Stack Highlights
* **Expo Router** with grouped route segments per tab (`(home)`, `(train)`, `(profile)`) so each tab nests its own `Stack` and header
* **Reanimated 4** for parallax, ring animations, and number transitions
* **Uniwind** (Tailwind for React Native) with `accent-*`, `surface`, `foreground`, `muted` semantic tokens — re-skin the whole app by editing tokens
* **Bricolage Grotesque** display font + **Plus Jakarta Sans** body font, both loaded via `expo-font` with a splash hold
* **Haptic feedback** on every navigation and write action
* **All Pro components** that ship in `1.0.0-beta.5` are used somewhere — `RadialChart`, `ProgressCircle`, `ProgressBar`, `SlideButton`, `ProgressButton`, `NumberStepper`, `NumberValue`, `Rating`, `Badge`, `ToggleButton(Group)` — making this template double as a worked example for each one
## Heads-up
Stride uses native modules that aren't included in Expo Go (`expo-router/unstable-native-tabs` on iOS, plus Reanimated worklets and `react-native-svg`). You'll need an **Expo development build** — `npm run ios` or `npm run android` after the install — rather than scanning a QR with Expo Go.
## Get the Template
Open the [Templates page](https://heroui.pro/docs/native/templates), find the **Fitness App** card, and hit **Download** to grab the Expo source. A HeroUI Native Pro license is required.
## Links
* [Templates](https://heroui.pro/docs/native/templates)
* [Installation](https://heroui.pro/docs/native/getting-started/installation)
* [Component Documentation](https://heroui.pro/docs/native/components)
# Style Guides Native
**Category**: native
**URL**: https://heroui.pro/docs/native/releases/theme-builder-native
> Design themes in the Pro Style Guides, preview them live on iOS and Android, and scan a QR code to open your theme in the HeroUI Native app.
June 19, 2026
The [Pro Style Guides](https://heroui.pro/dashboard/pro/ds) now supports **HeroUI Native**. Switch the preview platform to Native, tune tokens in the browser, preview them inside an iOS/Android phone frame, then scan a QR code to open the same theme in the **HeroUI Native** app on a real device.
## What's New
* **Web + Native preview** — Toggle between Web and Native preview platforms without leaving the editor
* **Live phone frame** — iOS and Android segments render a scoped native overview with live theme tokens
* **Scan to preview** — QR card deep-links into the HeroUI Native app with your current theme payload
* **Native CSS export** — Export Uniwind-ready CSS alongside Web CSS and `DESIGN.md`
* **Native CSS import** — Import native theme CSS (including Expo Google Fonts family names) and round-trip without breaking the web preview
* **Scoped editor** — Left nav and command palette filter to native-relevant sections when Native is selected
## How it works
1. Open the [Style Guides](https://heroui.pro/dashboard/pro/ds) and switch the preview platform to **Native**
2. Adjust supported tokens — colors, radius, border width, fonts, letter spacing, and disabled opacity
3. Preview the result in the in-browser phone frame (iOS or Android)
4. Scan the QR code to open your theme in the [HeroUI Native app](https://apps.apple.com/app/id6757860059)
5. Export **Web CSS** or **Native CSS** from the code panel when you are ready to ship
## Supported on Native
These tokens sync between the editor preview, QR deep link, and native CSS export:
* Semantic colors — accent, surfaces, foregrounds, status colors, borders, and focus
* Radius and field radius
* Border width and field border width
* Font weights mapped to Expo Google Fonts families (15 suggested defaults)
* Letter spacing
* Disabled opacity
* Chart palette variables (`--chart-1` through `--chart-5`)
## Not supported yet on Native
When you switch to Native, unsupported web-only controls fall back to HeroUI defaults in both the phone preview and the editor:
* Line height, font size, spacing, and glass blur
* Cursor pointer and animations toggles
* Custom accent-hover and other unsupported color variables
* Custom CDN body fonts (the QR card shows a compact warning; suggested default fonts work without it)
* Pro design themes (Brutalism, Glass, Mouve) — the scan card is hidden; customize a base theme instead
The code panel follows the active platform. Open it from Native overview and **Native CSS** is
selected by default; from Web overview, **Web CSS** is selected.
## Try It on Your Device
## Links
* [Style Guides](https://heroui.pro/dashboard/pro/ds)
* [HeroUI Native on the App Store](https://apps.apple.com/app/id6757860059)
* [Native theming guide](https://heroui.pro/docs/native/getting-started/theming)
* [Native colors guide](https://heroui.pro/docs/native/getting-started/colors)
# Templates
**Category**: native
**URL**: https://heroui.pro/docs/native/templates
> Ready-to-use, full-page templates built with HeroUI Native Pro. Download and start building from a solid foundation.
# Brutalism
**Category**: native
**URL**: https://heroui.pro/docs/native/themes/brutalism
> Bold borders, hard shadows, and display typography — a raw, high-contrast take on every component.
The **Brutalism** theme restyles every HeroUI Native Pro component with bold borders, hard shadows, and display typography for a raw, high-contrast look.
## Preview
## Installation
The Brutalism theme is included with HeroUI Native Pro. If you haven't installed the Pro package yet, follow the [Installation guide](../getting-started/installation) first.
Import the theme in your `global.css` right after the HeroUI Native styles:
```css title="global.css"
@import 'tailwindcss';
@import 'uniwind';
@import 'heroui-native/styles';
@import 'heroui-native-pro/styles';
/* [!code highlight] */
@import 'heroui-native-pro/themes/brutalism';
@source './node_modules/heroui-native/lib';
@source './node_modules/heroui-native-pro/lib';
```
### Fonts
Brutalism is designed around a monospace body font and a display font for labels. Load the fonts in your app (e.g., with [`expo-font`](https://docs.expo.dev/develop/user-interface/fonts/)) and map them to the theme font variables:
```css title="global.css"
@layer theme {
:root {
@variant light {
--font-normal: 'JetBrainsMono-Regular';
--font-medium: 'JetBrainsMono-Medium';
--font-semibold: 'JetBrainsMono-SemiBold';
--font-bold: 'JetBrainsMono-Bold';
--brutalism-font-display: 'Anton-Regular';
}
@variant dark {
--font-normal: 'JetBrainsMono-Regular';
--font-medium: 'JetBrainsMono-Medium';
--font-semibold: 'JetBrainsMono-SemiBold';
--font-bold: 'JetBrainsMono-Bold';
--brutalism-font-display: 'Anton-Regular';
}
}
}
```
`--brutalism-font-display` is an exclusive Brutalism font variable used for labels.
# Default
**Category**: native
**URL**: https://heroui.pro/docs/native/themes/default
> The signature HeroUI Native look — clean surfaces, soft radii, and balanced typography that works out of the box.
The **Default** theme is the signature HeroUI Native look. Clean surfaces, soft radii, and balanced typography — every component ships styled with it out of the box, so there is nothing extra to import.
## Preview
## Try It on Your Device
## Installation
The Default theme is included with HeroUI Native. If you haven't installed the package yet, follow the [Installation guide](../getting-started/installation) first.
Add the HeroUI Native and HeroUI Native Pro source paths to your `global.css` file so Tailwind can scan component classes:
```css title="global.css"
@import 'tailwindcss';
@import 'uniwind';
/* [!code highlight] */
@import 'heroui-native/styles';
@import 'heroui-native-pro/styles';
@source './node_modules/heroui-native/lib';
/* [!code highlight] */
@source './node_modules/heroui-native-pro/lib';
```
The `@source` path is relative to your `global.css` file. Adjust accordingly if your CSS file is not at the project root (e.g., `../node_modules/heroui-native-pro/lib` if `global.css` is in `app/`).
# Glass
**Category**: native
**URL**: https://heroui.pro/docs/native/themes/glass
> Frosted translucent surfaces and real iOS blur — a soft, layered take on every component.
The **Glass** theme restyles every HeroUI Native Pro component with translucent surfaces and frosted blur layers. Unlike the other themes, it is not CSS-only: overlays, fields, and surfaces mount a real blur layer at runtime, which means there are a few things to know before shipping it.
## Preview
## Installation
The Glass theme is included with HeroUI Native Pro. If you haven't installed the Pro package yet, follow the [Installation guide](../getting-started/installation) first.
### 1. Install `expo-blur`
The blur layers are powered by [`expo-blur`](https://docs.expo.dev/versions/latest/sdk/blur-view/), the only supported blur provider:
```bash
npx expo install expo-blur
```
Bare React Native projects can install it too — `expo-blur` works outside Expo apps as long as
[`expo-modules-core`](https://docs.expo.dev/bare/installing-expo-modules/) is set up. Without it,
every platform renders the opaque fallback described below.
### 2. Import the theme
Import the theme in your `global.css` **after** the HeroUI Native styles:
```css title="global.css"
@import 'tailwindcss';
@import 'uniwind';
@import 'heroui-native/styles';
@import 'heroui-native-pro/styles';
/* [!code highlight] */
@import 'heroui-native-pro/themes/glass';
@source './node_modules/heroui-native/lib';
@source './node_modules/heroui-native-pro/lib';
```
Order matters. The Glass theme overrides the surface, field, and overlay tokens set by
`heroui-native/styles`, so importing it before the base styles silently disables the theme.
## Platform Behavior
Real backdrop blur only exists on iOS. Everything else falls back to an opaque approximation, so plan your designs around both outcomes:
| Platform | Result |
| ------------------------------- | ------------------------------------------------------------------------------------ |
| iOS with `expo-blur` | Native blur layer; translucent theme tokens frost through it |
| Android, web, or no `expo-blur` | Opaque color: the layer's `fallbackColor` token alpha-composited over `--background` |
The fallback is intentionally opaque rather than transparent — a transparent layer would leave surfaces looking washed out on Android. Every background part picks a matching token by default (`field` for inputs, `surface` / `surface-secondary` / `surface-tertiary` for surfaces, `overlay` for popups), so you rarely need to touch it. When you do, pass `fallbackColor` to `GlassView`:
```tsx
import { GlassView } from 'heroui-native';
;
```
## Text Input
This is the one component whose **structure changes** under the Glass theme, and it is the most common source of surprises.
On other themes, [`Input`](https://heroui.com/docs/native/components/input) renders a bare `TextInput` as its root. On Glass, it needs somewhere to put the frosted layer, so it wraps the text input in a container `View`:
```tsx
// Default theme
// Glass theme
{/* the frosted layer */}
```
Two consequences:
**Layout classes belong on `containerClassName`, not `className`.** With a background layer present, `className` lands on the text input and `containerClassName` lands on the wrapper. On themes without a layer both are merged onto the text input, so `containerClassName` is the portable place for sizing and positioning:
```tsx
```
**Don't paint an opaque background on the input itself.** A `bg-*` class on `className` sits above the frosted layer and hides it. Tint the layer instead, or remove it and style the input directly:
```tsx
// Hides the blur
// Customize the layer instead
}
/>
// Or opt out entirely — bare text input, style it however you like
```
The `ref` still points at the underlying `TextInput` on every theme, and the same rules apply to everything built on `Input` — `TextField`, `SearchField.Input`, and `TextArea`. `InputOTP` frosts each slot instead, through its own `InputOTP.SlotBackground` part.
## Blur Backdrops
[`BottomSheet`](https://heroui.com/docs/native/components/bottom-sheet), [`Dialog`](https://heroui.com/docs/native/components/dialog), and [`FAB`](../components/fab) all expose an `Overlay` part with a `variant` prop. Under the Glass theme its default flips from `default` (a solid `--backdrop` fill) to `blur`, so you get an animated blur backdrop without changing any code:
```tsx
{/* blur under Glass, solid elsewhere */}
...
```
Tune the maximum blur with `blurViewProps.intensity` (defaults to `50` in light mode and `75` in dark), or pin the variant explicitly to opt out:
```tsx
{/* keep the solid backdrop on Glass */}
```
The `blur` variant animates **blur intensity** instead of opacity, so `isAnimatedStyleActive`
defaults to `false` for it. If you were relying on a custom opacity animation via the `animation`
prop, set `isAnimatedStyleActive` back to `true`.
A requested `blur` variant is automatically downgraded to `default` on Android, on web, and when `expo-blur` is missing — you never need to branch on the platform yourself.
`BottomSheet` is the exception to the compound-part pattern for its **sheet** background (not its
overlay). Because the sheet is rendered by
[`@gorhom/bottom-sheet`](https://gorhom.dev/react-native-bottom-sheet/), customize its frosted
layer through a custom `backgroundComponent` that renders `BottomSheet.Background` or `GlassView`.
## Customizing a Layer
Every component that mounts a frosted layer exposes it as a compound part — `Dialog.ContentBackground`, `Popover.ContentBackground`, `Input.Background`, `Widget.Background`, `Surface.Background`, and so on. Pass it to the owning part's `background` prop to change intensity, tint, or the fallback color:
```tsx
}
>
...
```
Passing `background={null}` removes the layer entirely.
Blur layers are expensive. Avoid stacking them — a `GlassView` inside another already-frosted
surface renders a second native blur pass for no visual gain. This is why `Widget.Content` keeps
only its translucent tint and composites over the widget root's layer.
# All Themes
**Category**: native
**URL**: https://heroui.pro/docs/native/themes
> Premium design themes for HeroUI Native Pro. Restyle every component with a single import.
# React Components
**Category**: react
**URL**: https://heroui.pro/docs/react/components
> Browse HeroUI Pro components for charts, data display, forms, navigation, AI interfaces, and more.
## Charts
## Data Display
## AI
## Feedback
## Layout
## Forms
## Navigation
## Overlays
# Introduction
**Category**: react
**URL**: https://heroui.pro/docs/react/getting-started
> Premium composable components that extend HeroUI — built for teams who want to ship beautiful, production-ready apps fast.
HeroUI Pro is a premium extension of [HeroUI OSS](https://heroui.com) — production-ready, fully composable components that follow the same patterns you already know from HeroUI OSS. New components, themes, and fixes ship regularly as package updates.
## What's Included
* **+ React components** with **+ examples** — Command Palette, Data Grid, Sheet, Sidebar, Emoji Picker, Charts, File Tree, KPI cards, and more — each with full documentation
* **AI components** — A complete set of chat primitives for conversations, messages, prompt inputs, attachments, sources, tool calls, reasoning traces, markdown, and code blocks
* **React Native components** — The same design system with a similar API across iOS and Android
* **Templates** — Production-ready dashboards, mail, chat, and finance apps
* **Premium themes** — Brutalism, Glass, and more. Switch aesthetics in one import.
* **Design Systems** — Create custom themes visually, preview live in the docs, and export the CSS
* **Figma** — Design files for all Pro components + a theme variables sync plugin
* **AI tooling** — MCP server, agent skills, and design taste for AI-assisted development
* **Priority support** — Fast support, prioritized issues, private Discord, and a VIP badge
* **Teams & Enterprise** — Shared themes, centralized billing, SSO, and dedicated support
## Next Steps
* [Installation](https://heroui.pro/docs/react/getting-started/installation) — Set up HeroUI Pro in your project
* [Browse Components](https://heroui.pro/docs/react/components/area-chart) — See all available Pro components
* [Design Systems](https://heroui.pro/dashboard/pro/ds) — Create your own custom theme
## Acknowledgements
Built on [React Aria Components](https://react-spectrum.adobe.com/react-aria/), [Tailwind CSS v4](https://tailwindcss.com/), [Motion](https://motion.dev/), [Recharts](https://recharts.github.io/), [Number Flow](https://number-flow.barvian.me/), and [Gravity UI Icons](https://gravity-ui.com/icons).
# 1.0.0-beta.1
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/beta-1
> The first public beta of HeroUI Pro React. 47 premium components, 4 production templates, premium themes, AI tooling, and full documentation.
April 2026
The first public beta of **HeroUI Pro React**. 47 components, 4 templates, premium themes, Style Guides, and AI tooling — all built on [HeroUI v3](https://heroui.pro/docs/react/releases/v3-0-0).
## Overview
HeroUI Pro (`@heroui-pro/react`) extends [HeroUI OSS](https://heroui.com) with premium components, templates, and tooling. Same architecture, same theming, same accessibility foundation.
```tsx
import {Command, Sidebar, DataGrid} from "@heroui-pro/react";
import {Button, Card} from "@heroui/react";
```
## Templates
4 production-ready templates. Download the source with your license.
### Dashboard
Analytics dashboard with orders, tracker, settings, and help pages.
### Mail
Email client with folders, message threads, and compose views.
### Chat
AI chat interface with conversations, library, and exploration views.
### Finances
Finance dashboard with portfolio, spending, and transaction views.
Browse and download from the [Templates](https://heroui.pro/docs/react/templates) page.
## Built on HeroUI v3
Same principles as [HeroUI v3](https://heroui.pro/docs/react/releases/v3-0-0):
* **Compound components** — Dot-notation primitives for every slot
* **CSS-first theming** — Tailwind CSS v4 + OKLCH variables, `data-theme` switching
* **BEM class overrides** — Override globally via `.command`, `.sidebar`, `.sheet` in CSS
* **Headless-capable** — Remove the CSS import, keep behavior + accessibility
* **CSS animations** — No JS runtime, `data-reduce-motion` respected automatically
* **React Aria** — Keyboard, focus, screen readers built in
## Components
47 components across 7 categories. [Browse all →](https://heroui.pro/docs/react/components)
### Charts
9 components built on [Recharts](https://recharts.github.io/).
* [AreaChart](https://heroui.pro/docs/react/components/area-chart) — Gradient fills, stacking, curve interpolation
* [BarChart](https://heroui.pro/docs/react/components/bar-chart) — Grouped, stacked, waterfall layouts
* [LineChart](https://heroui.pro/docs/react/components/line-chart) — Multi-series, dashed comparisons, KPI overlays
* [PieChart](https://heroui.pro/docs/react/components/pie-chart) — Donut variants, nested rings, breakdowns
* [RadarChart](https://heroui.pro/docs/react/components/radar-chart) — Multi-axis comparison views
* [RadialChart](https://heroui.pro/docs/react/components/radial-chart) — Gauges and progress rings
* [ComposedChart](https://heroui.pro/docs/react/components/composed-chart) — Mix bar, line, and area in one chart
* [ChartTooltip](https://heroui.pro/docs/react/components/chart-tooltip) — Shared tooltip with custom content
* [Widget](https://heroui.pro/docs/react/components/widget) — Dashboard container with title, KPIs, and actions
### Data Display
13 components.
* [DataGrid](https://heroui.pro/docs/react/components/data-grid) — Sorting, filtering, pinning, drag-and-drop, editable cells, virtualization, bulk actions
* [ActionBar](https://heroui.pro/docs/react/components/action-bar) — Contextual toolbar on row selection
* [Carousel](https://heroui.pro/docs/react/components/carousel) — Touch/drag, loop, autoplay, modal preview
* [Kanban](https://heroui.pro/docs/react/components/kanban) — Drag-and-drop board with columns and swimlanes
* [ListView](https://heroui.pro/docs/react/components/list-view) — Selectable item list with action slots
* [FileTree](https://heroui.pro/docs/react/components/file-tree) — Hierarchical browser with drag-and-drop and guide lines
* [FloatingToc](https://heroui.pro/docs/react/components/floating-toc) — Scroll-tracking table of contents
* [HoverCard](https://heroui.pro/docs/react/components/hover-card) — Rich hover card with delays and placements
* [ItemCard](https://heroui.pro/docs/react/components/item-card) / [ItemCardGroup](https://heroui.pro/docs/react/components/item-card-group) — Content cards for grids and galleries
* [EmptyState](https://heroui.pro/docs/react/components/empty-state) — Placeholder for empty views
* [KPI](https://heroui.pro/docs/react/components/kpi) / [KPIGroup](https://heroui.pro/docs/react/components/kpi-group) — Metric cards with trends and sparklines
### Navigation
6 components.
* [AppLayout](https://heroui.pro/docs/react/components/app-layout) — Sidebar + navbar + main + aside scaffold
* [Sidebar](https://heroui.pro/docs/react/components/sidebar) — Collapsible groups, rail mode, mobile sheet
* [Navbar](https://heroui.pro/docs/react/components/navbar) — Sticky nav with dropdowns, search, hide-on-scroll
* [Command](https://heroui.pro/docs/react/components/command) — Command palette with search and split-view
* [ContextMenu](https://heroui.pro/docs/react/components/context-menu) — Right-click menu with submenus and selection
* [Segment](https://heroui.pro/docs/react/components/segment) — Segmented control for view switching
### Forms
9 components.
* [RadioButtonGroup](https://heroui.pro/docs/react/components/radio-button-group) — Card layouts, icon cards, grid, custom indicators
* [CheckboxButtonGroup](https://heroui.pro/docs/react/components/checkbox-button-group) — Same patterns, multi-select
* [NumberStepper](https://heroui.pro/docs/react/components/number-stepper) — Increment/decrement with min/max and formatting
* [InlineSelect](https://heroui.pro/docs/react/components/inline-select) — Compact inline dropdown
* [NativeSelect](https://heroui.pro/docs/react/components/native-select) — Browser-native `
# 1.0.0-beta.2
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/beta-2
> Vite/SSR compatibility fix, CLI improvements, peer dependency bump, and docs reorganization.
April 2026
A patch release focused on compatibility, CLI improvements, and documentation. Fixes a module resolution issue that broke default icons in Vite SSR and TanStack Start environments, improves the CLI install and update workflow, bumps peer dependency minimums, and reorganizes component docs into more intuitive categories.
## Bug Fixes
### Fix: Icon rendering in Vite SSR / TanStack Start
Components that render a default icon when no `children` are passed — such as `InlineSelect.Indicator` and `CellSelect.Indicator` — threw a runtime error in Vite SSR and TanStack Start:
```
Element type is invalid: expected a string (for built-in components)
or a class/function (for composite components) but got: object.
```
**Root cause:** The `@gravity-ui/icons` package ships CJS files at the root and ESM files under `esm/`, but has no `exports` map. Subpath default imports (e.g. `@gravity-ui/icons/ChevronsExpandVertical`) resolved to the CJS build, and Node.js native ESM does not honor the `__esModule` convention — so the default import returned the module namespace object instead of the component function.
**Fix:** Replaced all subpath default imports with named barrel imports (`import { ChevronsExpandVertical } from "@gravity-ui/icons"`), which resolve to ESM via the package's `module` field.
Affected components:
* `InlineSelect.Indicator`
* `CellSelect.Indicator`
* `Carousel` (prev/next chevrons)
* `DropZone` (upload/trash icons)
* `FileTree` (expand chevron)
* `NumberStepper` (plus/minus icons)
## CLI Improvements
### Fix: CLI now respects pinned versions on install
Previously, running `npx heroui-pro install` would resolve to `@latest` and overwrite the version declared in your `package.json`. The CLI now runs a bare `pm install` for packages already present, preserving your pinned or range version.
Other CLI changes:
* **Version drift detection** — After install, the CLI compares the installed `node_modules` version against your `package.json` version. In interactive mode it prompts you to keep the installed version, install the `package.json` version, or update to latest.
* **Separate Install and Update menu options** — The interactive menu now always shows "Install \{name} (\{version})" for the current version and a separate "Update \{name} to \{latest}" option when outdated.
* **New `heroui-pro update` command** — Non-interactive counterpart to the menu's update option. Run `npx heroui-pro update` to upgrade directly.
## Breaking Changes
### Peer dependency minimums bumped
`@heroui/react` and `@heroui/styles` peer dependencies now require `>=3.0.3` (previously `>=3.0.0`).
```diff
- "@heroui/react": ">=3.0.0"
- "@heroui/styles": ">=3.0.0"
+ "@heroui/react": ">=3.0.3"
+ "@heroui/styles": ">=3.0.3"
```
If you're on an older version, update both packages:
```bash
npm install @heroui/react@latest @heroui/styles@latest
```
## Docs
### Component categories reorganized
Several components moved to categories that better reflect their purpose:
| Component | From | To |
| ------------------------------------------------------------------------------------- | -------- | ------------ |
| [Widget](https://heroui.pro/docs/react/components/widget) | Charts | Data Display |
| [EmojiReactionButton](https://heroui.pro/docs/react/components/emoji-reaction-button) | Overlays | Feedback |
| [DropZone](https://heroui.pro/docs/react/components/drop-zone) | Overlays | Forms |
| [Stepper](https://heroui.pro/docs/react/components/stepper) | Overlays | Navigation |
No API changes — only the documentation navigation URLs changed.
## Upgrade
Run the HeroUI Pro CLI and select the update option:
```bash
npx heroui-pro
```
Then choose **Update HeroUI Pro React to x.x.x (latest)** from the interactive menu.
# 1.0.0-beta.3
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/beta-3
> Segment ghost variant + dynamic width, AppLayout content-scroll mode, FileTree checkboxes, EmojiReactionButton read-only, and keyboard support for PressableFeedback.
May 2026
This release adds a ghost variant and dynamic-width items to [Segment](https://heroui.pro/docs/react/components/segment), a content-scroll mode to [AppLayout](https://heroui.pro/docs/react/components/app-layout), read-only support for [EmojiReactionButton](https://heroui.pro/docs/react/components/emoji-reaction-button), and automatic checkbox rendering in [FileTree](https://heroui.pro/docs/react/components/file-tree).
⚠️ **Breaking changes**: `@gravity-ui/icons` removed as a runtime dependency (now bundled); NumberStepper sizes decreased one step across all variants.
## What's New
### Segment: Ghost Variant
Transparent container with an accent-colored indicator — ideal for inline navigation embedded in toolbars or cards.
### Segment: Dynamic Width
Render-prop Segment items that expand from icon-only to icon + label on selection — dynamic width driven by CSS grid transitions.
### AppLayout: Content Scroll Mode
New `scrollMode="content"` prop keeps the shell fixed to the viewport while only the main column scrolls. The main region becomes focusable so browsers provide native PageUp/PageDown/Arrow scrolling without custom handlers.
### EmojiReactionButton: Read-Only Mode
New `isReadOnly` prop displays reactions without responding to interaction — no press handlers fire, and the button is excluded from tab order.
### FileTree: Selection Checkboxes
Checkboxes auto-render when `selectionBehavior="toggle"`. The chevron also gained a hover state for better affordance.
## New Demos
### Sidebar: Meeting Notes
Full-featured sidebar demo with command palette, offcanvas mode, and nested navigation.
### Sidebar: Complex
Updated complex sidebar demo with teamspaces, agents, favorites, recents, and ghost segment navigation.
## Bug Fixes
* **ContextMenu** — Re-opens correctly on repeated right-click while the popover overlay is active
* **PressableFeedback (HoldConfirm)** — Space/Enter now trigger hold-to-confirm (keyboard parity with pointer)
* **PressableFeedback (ProgressFeedback)** — Keyboard sweep activation added
* **FileTree** — Row hover suppressed when hovering the chevron button
* **Glass theme (Carousel)** — Nav buttons use `mix-blend-mode: difference` for visibility on arbitrary slide backgrounds
## Style Changes
* **NumberStepper** — All size variants decreased one step (lg → h-10, md → h-9, sm → h-8) for tighter proportions
* **RadioButtonGroup** — Demos updated with responsive layout patterns
## Build
* **"use client" directives preserved** — Added `rollup-plugin-preserve-directives` so directives survive terser minification
* **Icons bundled internally** — `@gravity-ui/icons` moved from `dependencies` to `devDependencies`; icons are compiled into the output, consumers no longer need it installed
## ⚠️ Breaking Changes
### `@gravity-ui/icons` no longer a runtime dependency
Icons used by HeroUI Pro components are now bundled into the package output. If you were importing from `@gravity-ui/icons` via HeroUI Pro's `node_modules`, install it directly:
```bash
npm install @gravity-ui/icons
```
No action needed if you only use HeroUI Pro components — they continue to work as before.
### NumberStepper size reduction
All button sizes are one step smaller:
| Variant | Before | After |
| ------- | -------------- | ------------- |
| sm | h-9 / md:h-8 | h-8 / md:h-7 |
| md | h-10 / md:h-9 | h-9 / md:h-8 |
| lg | h-11 / md:h-10 | h-10 / md:h-9 |
Layouts with exact height constraints on the stepper may need adjustment.
## Upgrade
```bash
npx heroui-pro
```
Select **Update HeroUI Pro React to x.x.x (latest)** from the interactive menu.
# 1.0.0-beta.4
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/beta-4
> New Agenda component with day, week, and month views, a Mouve theme, and a DataGrid column-toggle fix.
May 2026
This release adds [Agenda](https://heroui.pro/docs/react/components/agenda) — a composable calendar with day, week, and month views, drag-to-create/move/resize, and mobile responsive layout — and a new [Mouve](https://heroui.pro/docs/react/getting-started/theming#mouve) premium theme. Also fixes a `DataGrid` crash when toggling column visibility.
## What's New
### Mouve Theme
A refined mauve/warm-purple theme. Saturated-accent surfaces (filled buttons, selected calendar cells, switch/slider/progress fills, segment ghost indicator, stepper completed steps) pick up a layered raised shadow with inset specular highlight and accent border; neutral pill buttons get an inset pressed shadow on press for a tactile feel.
```css title="globals.css"
@import "tailwindcss";
@import "@heroui/styles";
@import "@heroui-pro/react/css";
@import "@heroui-pro/react/themes/mouve";
```
Activate with `data-theme="mouve-light"` or `data-theme="mouve-dark"`. Three reusable shadow tokens — `--mouve-raised-shadow`, `--mouve-pressed-shadow`, and `--mouve-accent-fill-shadow` — let you extend the look to custom components.
### Agenda
Composable calendar inspired by Notion Calendar. Day, week, and month views, all-day section, current time indicator, and full drag CRUD.
Key features:
* **Three views**: day, week, month — switch via `Agenda.ViewSelector` or the `view` prop
* **Drag CRUD**: drag-to-create on empty slots, drag-to-move (cross-day), drag-to-resize, delete via Backspace/Delete
* **All-day section**: collapsible row above the time grid, multi-day spanning events, per-day event counts when collapsed
* **Month view**: spanning event bars, per-cell `maxEvents` overflow ("N more"), date click → day view
* **Current time indicator**: locale-aware time label, faded line across columns with active highlight on today
* **Mobile responsive**: `weekDays` prop centers a smaller window around today (e.g. 3-day view on phones); pass `undefined` drag callbacks to disable drag
* **Event states**: `status: "unconfirmed"` (dashed border), `isReadOnly: true` (no move/resize), `[data-selected]`, `[data-weekend]`, `[data-today]`
* **i18n**: built on `@internationalized/date` and React Aria primitives
```tsx
import {Agenda, useAgenda} from "@heroui-pro/react";
const agenda = useAgenda({events, defaultView: "week"});
{/* week or month layout */};
```
See the [Agenda docs](https://heroui.pro/docs/react/components/agenda) for the full anatomy and API reference.
## Bug Fixes
### DataGrid: Column toggle crash
Toggling column visibility threw `Cell count must match column count` because dependencies on `Table.Body` had no effect on row caching — `Table.Body`'s direct children are static JSX. Dependencies are now on `Table.Collection`, which properly invalidates the WeakMap row cache when columns change.
### Sidebar: Right-side offcanvas overflow
Right-side offcanvas content overflowed the sidebar bounds. CSS now clips overflow and corrects element ordering for right placement.
## Dependencies
* **`@internationalized/date`** — Added as a dependency (used by Agenda for date math and locale-aware formatting)
## Upgrade
```bash
npx heroui-pro
```
Select **Update HeroUI Pro React to x.x.x (latest)** from the interactive menu.
# 1.0.0-beta.5
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/beta-5
> New AI components for chat and agent interfaces, an upgraded Chat template, HeroUI 3.1.0 peer dependency support, and focused polish.
May 2026
This release adds a new AI component family to `@heroui-pro/react`: composable primitives for chat conversations, prompt composers, attachments, markdown, code blocks, citations, tool calls, and reasoning traces. It also refreshes the Chat template around those primitives and updates HeroUI OSS peer dependencies to 3.1.0.
⚠️ **Breaking change**: `@heroui/react` and `@heroui/styles` peer dependency minimums are now `>=3.1.0`.
## What's New
### AI Components
14 new components are available in the new [AI](https://heroui.pro/docs/react/components#ai) category:
* [PromptInput](https://heroui.pro/docs/react/components/prompt-input) - Composable prompt composer with attachments, toolbar slots, send/stop states, inline layout, and queued prompts
* [PromptSuggestion](https://heroui.pro/docs/react/components/prompt-suggestion) - Suggested prompts and starter actions for empty states
* [ChatConversation](https://heroui.pro/docs/react/components/chat-conversation) - Stick-to-bottom conversation viewport with optional jump-to-bottom control
* [ChatMessage](https://heroui.pro/docs/react/components/chat-message) - Assistant and user message layouts with avatars, bubbles, markdown, actions, and attachments
* [ChatMessageActions](https://heroui.pro/docs/react/components/chat-message-actions) - Copy, retry, rating, share, or custom message actions
* [ChatAttachment](https://heroui.pro/docs/react/components/chat-attachment) - File previews, attachment groups, and composer file input helpers
* [ChatListView](https://heroui.pro/docs/react/components/chat-list-view) - Thread list rows for sidebars and conversation pickers
* [ChatLoader](https://heroui.pro/docs/react/components/chat-loader) - Dots, pulse, spinner, and skeleton loaders for assistant responses
* [Markdown](https://heroui.pro/docs/react/components/markdown) - AI response markdown with memoized streaming blocks and `StreamMarkdown`
* [CodeBlock](https://heroui.pro/docs/react/components/code-block) - Shiki-highlighted code blocks with language labels and copy actions
* [ChainOfThought](https://heroui.pro/docs/react/components/chain-of-thought) - Collapsible reasoning timeline for progress, traces, and assistant thinking
* [ChatSource](https://heroui.pro/docs/react/components/chat-source) - URL/document citation chips and grouped source lists
* [ChatTool](https://heroui.pro/docs/react/components/chat-tool) - Tool-call cards for inputs, outputs, errors, approvals, and grouped tool activity
* [TextShimmer](https://heroui.pro/docs/react/components/text-shimmer) - Shimmering status text for streaming and background work
The components are intentionally primitive and composable. You can wire them to any AI runtime, including the AI SDK, a custom backend, or a static chat template.
```tsx
import {ChatConversation, ChatMessage, Markdown, PromptInput} from "@heroui-pro/react";
<>
{assistantResponse}{/* actions */}
>;
```
### PromptInput Queue
`PromptInput.Queue` adds a compact queued-prompt surface above the composer. It supports Motion-powered reordering, hover actions, attachment indicators, and clamped multi-line prompt previews.
### Markdown, Code, Sources, and Tools
AI answers can now be built from focused response primitives:
* `Markdown` renders response markdown with block-level memoization so streaming updates only re-render changed blocks
* `StreamMarkdown` wraps Streamdown for incomplete markdown repair, animation, and caret rendering
* `CodeBlock` uses Shiki for highlighted code and includes a copy button with copied-state feedback
* `ChatSource` and `ChatSources` support URL citations, document citations, grouped sources, hover previews, and stacked favicons
* `ChatTool` and `ChatToolGroup` support streaming, running, complete, error, and approval states
### Chat Template Refresh
The Chat template now uses the new AI components directly. The composer was rebuilt with `PromptInput`, attachments use `ChatAttachment`, assistant messages use `Markdown`, `ChainOfThought`, `ChatSource`, and `ChatTool`, and the template includes richer demo threads for markdown, tools, sources, loading states, and message actions.
The template also gained shared strings, safer attachment URL cleanup, refreshed library/explore pages, and updated package versions for the latest HeroUI stack.
### AI Docs
Every AI component has full docs, Storybook coverage, demos, CSS class references, and API tables. The All Components page now includes the AI category so the new primitives are discoverable alongside charts, forms, navigation, and data-display components.
## Improvements
* **HeroUI OSS 3.1.0** - `@heroui/react` and `@heroui/styles` dev and peer dependencies now target 3.1.0
* **Rating** - Read-only ratings no longer expose hidden radio inputs to pointer interaction or focus outlines
* **Navbar** - Static mobile menus now position against the navbar and avoid horizontal overflow
* **Widget** - Header height now supports taller or wrapping header content without clipping
* **Mouve theme** - Success, warning, and danger tones were tuned for better light/dark contrast
* **Build output** - Removed terser pure annotation preservation from the React package build output
## Dependencies
Added runtime dependencies for the AI response renderer and code highlighting:
* `marked`
* `react-markdown`
* `remark-breaks`
* `remark-gfm`
* `shiki`
* `streamdown`
## Breaking Changes
### Peer dependency minimums bumped
`@heroui/react` and `@heroui/styles` now require `>=3.1.0`:
```diff
- "@heroui/react": ">=3.0.5"
- "@heroui/styles": ">=3.0.5"
+ "@heroui/react": ">=3.1.0"
+ "@heroui/styles": ">=3.1.0"
```
Update both packages together:
```bash
npm install @heroui/react@latest @heroui/styles@latest
```
## Upgrade
```bash
npx heroui-pro
```
Select **Update HeroUI Pro React to x.x.x (latest)** from the interactive menu.
# 1.0.0-beta.6
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/beta-6
> Rich Text Editor and Timeline components, PromptInput layout API, Resizable pixel sizing, HeroUI 3.2.0 peer dependency support and focused polish across Agenda, Rating, and scroll surfaces.
June 2026
This release adds two new components: [Rich Text Editor](https://heroui.pro/docs/react/components/rich-text-editor) for Tiptap-based document editing and [Timeline](https://heroui.pro/docs/react/components/timeline) for read-only event chronology. It also splits `PromptInput` surface styling from layout, adds pixel-based `Resizable` panel sizing, and ships focused fixes for Agenda, Rating, and shared scrollbar styling. Moreover, it updates HeroUI OSS peer dependencies to 3.2.0.
⚠️ **Breaking change**:
* `@heroui/react` and `@heroui/styles` peer dependency minimums are now `>=3.2.0`.
* `PromptInput` `variant="inline"` is now `layout="inline"`. `variant` is limited to `primary` and `secondary`.
## What's New
### New Components
* **[Rich Text Editor](#rich-text-editor)**: JSON-first Tiptap editor with toolbar, bubble menu, floating menu, and character count. ([Docs](https://heroui.pro/docs/react/components/rich-text-editor))
* **[Timeline](#timeline)**: Composable event-history layout for feeds, audit trails, and milestone roadmaps. ([Docs](https://heroui.pro/docs/react/components/timeline))
### Rich Text Editor
Tiptap-based editor with default toolbar controls, selection bubble menu, empty-line floating menu, link popover, and JSON document values.
Key features:
* **JSON-first value API**: `defaultValue`, `value`, and `onValueChange`
* **Toolbar + floating menus**: default toolbar, bubble menu, floating menu, and suggestion menu slots
* **Character count**: optional footer with word and character limits
* **Extensible**: custom toolbar groups, commands, and composition patterns
**Controlled mode:**
```tsx
import {RichTextEditor} from "@heroui-pro/react";
;
```
### Timeline
Read-only chronology for activity feeds, incident history, release logs, and centered milestone roadmaps. Use [Stepper](https://heroui.pro/docs/react/components/stepper) when the user is moving through an interactive sequence.
Key features:
* **Compound layout**: `Timeline.Item`, `Timeline.Rail`, `Timeline.Marker`, `Timeline.Connector`, and `Timeline.Content`
* **Start + center axes**: `axis="start"` or `axis="center"` with alternate placement
* **Status markers**: `default`, `current`, `success`, `warning`, `danger`, and `muted`
* **Responsive centered layout**: centered milestones fall back to a start-axis layout on small screens ([#557](https://github.com/heroui-inc/heroui-pro/pull/557))
* **Accessible**: semantic `ol`/`li`, `aria-current` on `status="current"`, decorative rails hidden from assistive technology
**Centered milestones:**
**Incident response:**
```tsx
import {Timeline} from "@heroui-pro/react";
Canary rollout started
Enabled for 5% of workspaces.
;
```
### PromptInput Layout
`PromptInput` now separates surface styling from layout:
* `variant`: `primary` | `secondary`
* `layout`: `stacked` | `compact` | `inline`
New demos cover compact composers and review-style layouts with attachments and queued prompts. Queue reorder handles disable when there is only one item.
**Review composer:**
### Resizable and AppLayout
[Resizable](https://heroui.pro/docs/react/components/resizable) and [AppLayout](https://heroui.pro/docs/react/components/app-layout) now document and support pixel/CSS panel sizes with `preserve-pixel-size` resize behavior. Panel open/collapse sync is safer when using fixed-width aside and sidebar regions.
### Chat Attachments
`ChatAttachment.Input` now accepts pasted images, filtered by the existing `accept` prop.
## Improvements
* **HeroUI OSS 3.2.0** - `@heroui/react` and `@heroui/styles` dev and peer dependencies now target 3.2.0
* **Agenda** - Mobile event alignment uses measured/CSS slot height for labels, scroll, drag, and the current-time indicator
* **Rating** - Overlay spacing uses `gap` instead of per-star padding
* **Scroll surfaces** - Shared `scrollbar` utilities across sheet, command, navbar, file tree, chat conversation, and related Pro components
* **Mouve theme** - Success, warning, and danger tones tuned for better light/dark contrast
## Dependencies
Added Tiptap peer dependencies for Rich Text Editor:
* `@tiptap/core`
* `@tiptap/pm`
* `@tiptap/react`
* `@tiptap/starter-kit`
* `@tiptap/extensions`
* `@tiptap/extension-link`
* `@tiptap/extension-underline`
* `@tiptap/suggestion`
Install the packages when using `RichTextEditor`:
```bash
npm install @tiptap/core @tiptap/pm @tiptap/react @tiptap/starter-kit @tiptap/extensions @tiptap/extension-link @tiptap/extension-underline @tiptap/suggestion
```
We're currently working on making feature-specific peer dependencies optional. That improvement is
planned for the next release, so apps that do not use components like `RichTextEditor` will not need
to install their supporting packages.
## Breaking Changes
### Peer dependency minimums bumped
`@heroui/react` and `@heroui/styles` now require `>=3.2.0`:
```diff
- "@heroui/react": ">=3.1.0"
- "@heroui/styles": ">=3.1.0"
+ "@heroui/react": ">=3.2.0"
+ "@heroui/styles": ">=3.2.0"
```
Update both packages together:
```bash
npm install @heroui/react@latest @heroui/styles@latest
```
### PromptInput inline layout rename
`variant="inline"` is now `layout="inline"`. Surface styling stays on `variant`.
```diff
-
+
```
### Rich Text Editor peer dependencies
Tiptap packages are required when importing `RichTextEditor`. Install the packages listed in [Dependencies](#dependencies) alongside `@heroui-pro/react`.
## Upgrade
```bash
npx heroui-pro
```
Select **Update HeroUI Pro React to x.x.x (latest)** from the interactive menu.
# 1.0.0-beta.7
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/beta-7
> MapLibre-based Map component, RTL support through logical CSS properties, Navbar RouterProvider routing, Rich Text Editor link popover polish, and HeroUI 3.2.2 peer dependency support.
July 2026
This release adds [Map](https://heroui.pro/docs/react/components/map), a MapLibre-based map surface with markers, popups, routes, arcs, clusters, and themed controls. It also adopts logical CSS properties across Pro component styles for right-to-left layouts, lets `Navbar` pick up a global React Aria `RouterProvider` for client-side routing, and polishes the Rich Text Editor link popover.
⚠️ **Breaking change**:
* `@heroui/react` and `@heroui/styles` peer dependency minimums are now `>=3.2.2`, and `react-aria-components` is now `>=1.19.0`.
* `maplibre-gl` is a new peer dependency, required when using `Map`.
## What's New
### New Components
* **[Map](#map)**: MapLibre map surface with markers, popups, routes, arcs, clusters, and controls. ([Docs](https://heroui.pro/docs/react/components/map))
### Map
MapLibre-powered map with theme-aware styles, React-rendered markers, popups, route and arc layers, point clustering, and HeroUI-styled controls ([#513](https://github.com/heroui-inc/heroui-pro/pull/513)).
Key features:
* **Compound API**: `Map.Marker`, `Map.Popup`, `Map.Route`, `Map.Arc`, `Map.ClusterLayer`, `Map.Controls`
* **Theme-aware styles**: light + dark style URLs via `styles`, or a single `mapStyle`
* **Custom markers**: `Map.MarkerContent` with dots, labels, tooltips, popups, and drag support
* **Controls**: zoom, compass, locate, fullscreen + custom control buttons and groups
* **Bring your own tiles**: works with any MapLibre-compatible style provider
Demos cover live visitor dashboards, property listings, live tracking, store locators, fleet dispatch, coverage zones, heatmaps, flight paths, and incident monitoring.
**Live visitors:**
**Flight paths:**
**Store locator:**
```tsx
import {Map} from "@heroui-pro/react";
const styles = {
dark: "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json",
light: "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json",
};
;
```
`Map` does not ship a tile provider — pass any MapLibre-compatible style URL or object. The demos use [CARTO basemaps](https://docs.carto.com/carto-for-developers/carto-for-react/guides/basemaps).
### Navbar Routing
`Navbar.Item` and `Navbar.MenuItem` links now defer to a global React Aria [`RouterProvider`](https://react-spectrum.adobe.com/react-aria/routing.html) when no `navigate` prop is set, falling back to native browser navigation otherwise ([#656](https://github.com/heroui-inc/heroui-pro/pull/656)). Apps that already configure `RouterProvider` for HeroUI OSS components get client-side Navbar navigation without extra wiring.
## Improvements
* **RTL support** - Pro component styles use logical CSS properties (`margin-inline`, `inset-inline`, and friends) so components mirror correctly in right-to-left layouts ([#729](https://github.com/heroui-inc/heroui-pro/pull/729)), with dedicated fixes for the Sidebar current-item indicator ([#709](https://github.com/heroui-inc/heroui-pro/pull/709)) and PromptInput ([#710](https://github.com/heroui-inc/heroui-pro/pull/710))
* **Rich Text Editor** - Link popover trigger defaults to the `ghost` variant and accepts an optional `tooltip` ([#646](https://github.com/heroui-inc/heroui-pro/pull/646))
* **Segment** - Demos drop `Segment.Separator` from the default markup; separators are shown in a dedicated demo. Demo-only change, no API change
## HeroUI Pro Platform
Shipping alongside the package in this release:
* **Template gallery** - Publish and remix generated apps as templates, with curated previews hosted on `*.template.heroui.app` ([#696](https://github.com/heroui-inc/heroui-pro/pull/696))
* **Theme Builder Native** - In-browser native preview, QR scan to open on device, and native CSS import/export ([#493](https://github.com/heroui-inc/heroui-pro/pull/493))
* **License gating** - AI Chat and Themes now require an active Pro license ([#707](https://github.com/heroui-inc/heroui-pro/pull/707))
## Dependencies
* `@heroui/react` and `@heroui/styles` peer minimums bumped to `>=3.2.2`
* `react-aria-components` peer minimum bumped to `>=1.19.0`
* `maplibre-gl` (`>=5.15.0`) added as a peer dependency for `Map`
* `@internationalized/number` bumped to `3.6.7`
Install `maplibre-gl` when using `Map`:
```bash
npm install maplibre-gl
```
## Breaking Changes
### Peer dependency minimums bumped
`@heroui/react` and `@heroui/styles` now require `>=3.2.2`, and `react-aria-components` requires `>=1.19.0`:
```diff
- "@heroui/react": ">=3.2.0"
- "@heroui/styles": ">=3.2.0"
- "react-aria-components": ">=1.18.0"
+ "@heroui/react": ">=3.2.2"
+ "@heroui/styles": ">=3.2.2"
+ "react-aria-components": ">=1.19.0"
```
Update the packages together:
```bash
npm install @heroui/react@latest @heroui/styles@latest react-aria-components@latest
```
### Map peer dependency
`maplibre-gl` is required when importing `Map`:
```bash
npm install maplibre-gl
```
## Upgrade
```bash
npx heroui-pro
```
Select **Update HeroUI Pro React to x.x.x (latest)** from the interactive menu.
# 1.0.0-beta.8
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/beta-8
> Updated HeroUI Pro React and Native Figma files, a Sidebar page switcher, SSR-safe subpath imports for peer-heavy components, KPI chart tooltips, broader RTL support across Pro components, DataGrid type fixes, a MapLibre GL v6 upgrade, and dependency bumps to HeroUI 3.2.3 and tailwind-variants 3.3.0.
July 2026
This release updates the **HeroUI Pro React** and **HeroUI Pro Native** Figma files, adds an animated `Sidebar.Pages` switcher, and makes the package barrel SSR-safe by moving components that pull heavy, opt-in peer dependencies to their own subpath imports. It also adds a chart tooltip to `KPI.Chart`, extends right-to-left support to the Carousel and more Pro components, corrects `DataGrid` expansion prop types, upgrades `maplibre-gl` to v6, and bumps the `@heroui/react` peer to `3.2.3` and `tailwind-variants` to `3.3.0`.
⚠️ **Breaking change**:
* **Subpath imports** — charts, `KPI`, `Carousel`, `Map`, `Markdown`, `RichTextEditor`, `CodeBlock`, and other peer-heavy components are no longer exported from the root `@heroui-pro/react` barrel. Import each from its subpath instead (for example `@heroui-pro/react/kpi`).
* `maplibre-gl` is now a `>=6.0.0` peer dependency (required when using `Map`).
* `@react-aria/utils` moved to a peer dependency (`>=3.34.1`) — install it alongside `@heroui-pro/react` if it is not already present.
## What's New
No new top-level package components ship in this release, but the Sidebar gains an animated page switcher and KPI charts gain hover tooltips.
### Sidebar Pages
`Sidebar.Pages` swaps between multiple navigation panels with an animated horizontal slide — for example a main menu and a nested settings page ([#847](https://github.com/heroui-inc/heroui-pro/pull/847)). Each `Sidebar.Page` is identified by a `value`; the active page slides into view while the others slide off to the side. Switch pages from any descendant with the new `useSidebarPages` hook, or control the active page with `value` / `onValueChange`.
```tsx
import {Sidebar, useSidebarPages} from "@heroui-pro/react";
function MainMenu() {
const {setActiveValue} = useSidebarPages();
return (
setActiveValue("settings")}>
Settings
);
}
{/* settings menu */};
```
Slide animations respect `Sidebar.Provider`'s `reduceMotion` prop and the user's `prefers-reduced-motion` setting. Inactive pages are marked `inert` and `aria-hidden`, so keyboard and screen readers skip them.
### KPI Chart Tooltip
`KPI.Chart` now accepts a `tooltip` prop, letting you render a fully styled `ChartTooltip` on hover inside the sparkline without leaving the KPI surface ([#801](https://github.com/heroui-inc/heroui-pro/pull/801)).
```tsx
import {ChartTooltip} from "@heroui-pro/react";
import {AreaChart} from "@heroui-pro/react/area-chart";
import {KPI} from "@heroui-pro/react/kpi";
{
const point = payload?.[0];
if (!active || !point) return null;
return (
{point.payload?.month}Revenue
${Number(point.value).toLocaleString()}
);
}}
/>
}
/>;
```
## Improvements
* **ChatAttachment** - Reworked display for attachments of different types: media (image/video) previews render inline, while other files show a compact file card with a type-aware icon, truncated name, and equal-height layout ([#838](https://github.com/heroui-inc/heroui-pro/pull/838))
* **Carousel RTL** - Embla now syncs its scroll direction with the inherited `dir`, so carousels mirror correctly in right-to-left layouts ([#781](https://github.com/heroui-inc/heroui-pro/pull/781))
* **RTL support** - More Pro components and demos adopt logical CSS properties, continuing the right-to-left work started in beta.7
* **DataGrid** - Corrected types for `expandedKeys`, `defaultExpandedKeys`, and `onExpandedChange`, and the grid keeps a row-header column when a column is hidden ([#783](https://github.com/heroui-inc/heroui-pro/pull/783))
* **Map** - `setData` is now guarded against style-teardown races, avoiding errors when a source updates while the map style is being replaced
## HeroUI Pro Platform
Shipping alongside the package in this release:
* **Design Systems** - Import your website or start from scratch, customize a complete HeroUI design system with live previews, keep brand guidance and assets together, use it in AI Chat, and export Web CSS, Native CSS, `DESIGN.md`, and `PRODUCT.md` ([Read the release notes](https://heroui.pro/docs/react/releases/design-systems))
* **AI Chat** - Upgraded to AI SDK v7 with resumable streams, chat generation now runs on the trigger.dev agent, new models in the selector, SVG brand-logo generation, browser-based preview QA, and a remixable template gallery
* **Docs** - A **Copy prompt** button across component, installation, and overview pages makes it easy to hand a page's context to your AI assistant
## Dependencies
* `maplibre-gl` upgraded to `v6`; peer minimum bumped to `>=6.0.0` ([#832](https://github.com/heroui-inc/heroui-pro/pull/832))
* `@react-aria/utils` moved from a direct dependency to a peer dependency (`>=3.34.1`) ([#833](https://github.com/heroui-inc/heroui-pro/pull/833))
* `@heroui/react` and `@heroui/styles` bumped to `3.2.3` ([#839](https://github.com/heroui-inc/heroui-pro/pull/839)); peer minimum stays `>=3.2.2`
* `tailwind-variants` bumped to `3.3.0`; peer minimum bumped to `>=3.3.0`
## Breaking Changes
### SSR-safe subpath imports
Several components statically import heavy, opt-in peer dependencies (recharts, maplibre-gl, tiptap, embla, shiki, and more). Re-exporting them from the root barrel meant those peers were evaluated on every import, which crashes SSR module evaluation for consumers that do not install them ([#785](https://github.com/heroui-inc/heroui-pro/pull/785)).
These components are no longer exported from `@heroui-pro/react`. Import each from its own subpath:
| Component(s) | Import from |
| ---------------------------------------------------------------------------------------------- | ------------------------------------ |
| `AreaChart`, `BarChart`, `LineChart`, `PieChart`, `ComposedChart`, `RadarChart`, `RadialChart` | `@heroui-pro/react/` |
| `KPI` | `@heroui-pro/react/kpi` |
| `Carousel` | `@heroui-pro/react/carousel` |
| `Map` | `@heroui-pro/react/map` |
| `Markdown` | `@heroui-pro/react/markdown` |
| `RichTextEditor` | `@heroui-pro/react/rich-text-editor` |
| `CodeBlock` | `@heroui-pro/react/code-block` |
| `ChatTool`, `ChatToolGroup` | `@heroui-pro/react/chat-tool` |
| `NumberStepper` | `@heroui-pro/react/number-stepper` |
| `Resizable` | `@heroui-pro/react/resizable` |
Update the imports for any of these components:
```diff
- import {AreaChart, KPI} from "@heroui-pro/react";
+ import {AreaChart} from "@heroui-pro/react/area-chart";
+ import {KPI} from "@heroui-pro/react/kpi";
```
Every other component continues to import from the root `@heroui-pro/react`, and each subpath only pulls in its own peer dependency, so apps that never import these components no longer evaluate the heavy peers at all.
### MapLibre GL v6
`maplibre-gl` is upgraded to v6 and its peer minimum is now `>=6.0.0`. Update it when using `Map`:
```bash
npm install maplibre-gl@latest
```
### `@react-aria/utils` is now a peer dependency
`@react-aria/utils` is no longer bundled as a direct dependency of `@heroui-pro/react`; it is a peer dependency at `>=3.34.1`. Most apps already have it via `react-aria-components`, but install it explicitly if you hit an unresolved import:
```bash
npm install @react-aria/utils@latest
```
## Figma Updates
### HeroUI Pro React Figma
The **HeroUI Pro React** Figma files are updated to **v1.0.5** (Jul 31, 2026). Download the latest files from your [dashboard](https://heroui.pro/dashboard) or see the [Figma setup guide](https://heroui.pro/docs/react/getting-started/figma).
* Changed typography styles to sync with Typography component values, and added Heading 5 and Heading 6
* Added states support to Input, InputGroup, and DateFieldInput components
* New Sidebar, Navbar, AppLayout, EmojiPicker, RichTextEditor, Agenda, Map, Timeline, and AI components
### HeroUI Pro Native Figma
The **HeroUI Pro Native** Figma files are updated to **v1.0.3** (Jul 31, 2026). Download the latest files from your [dashboard](https://heroui.pro/dashboard) or see the [Native Figma setup guide](https://heroui.pro/docs/native/getting-started/figma).
* Changed typography styles to sync with Typography component values, and added Heading 5 and Heading 6
* Added states support to Input, InputGroup, and DateFieldInput components
* New 30 Pro components to sync with the code library
## Upgrade
```bash
npx heroui-pro
```
Select **Update HeroUI Pro React to x.x.x (latest)** from the interactive menu.
# Design Systems
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/design-systems
> Import your brand, customize a complete HeroUI design system, use it in AI Chat, and export it to code.
July 23, 2026
HeroUI Pro Design Systems turns your brand into a working UI foundation. Import an existing website or start from scratch, refine tokens and components with a live preview, keep brand guidance and assets together, then use the result in AI Chat or export it to your project.
## What's New
* **Import your website** — Start with a URL to extract your logo, colors, typography, brand voice, and visual assets, then generate a HeroUI design system from them
* **Build from scratch** — Define your identity and choose a visual direction through a guided setup
* **Design with a live preview** — Customize colors, fonts, radius, borders, shadows, spacing, and component styles while seeing the system update in real time
* **Keep the brand together** — Manage identity, guidelines, and assets from the new General, Guides, and Assets workspace
* **Use it in AI Chat** — Select a saved design system when starting a chat, apply one later, and sync updates from the builder
* **Export for code and AI** — Generate Web CSS, Native CSS, `DESIGN.md`, and `PRODUCT.md`, or let an AI agent import the system through the HeroUI Pro MCP server
## Start With Your Brand
Create a design system in one of two ways:
1. **Import a website** — Paste a URL and HeroUI extracts the brand foundation, including colors, fonts, logos, imagery, and written identity
2. **Start from scratch** — Add your name and logo, choose your colors and typography, then select a UI direction
The generated system is only a starting point. Review the imported identity, choose the direction that fits your product, and adjust every detail in the editor.
## Brand and UI in One Workspace
Design Systems combines the theme editor with a dedicated brand workspace:
* **General** — Brand identity, logo, website, description, audience, and tone
* **Guides** — Color, typography, spacing, radius, shadow, and iconography guidance
* **Assets** — Logos, product imagery, illustrations, and other reusable brand files
Your tokens, component styles, brand guidance, fonts, and assets stay attached to the same saved system.
## Bring Design Systems Into AI Chat
Choose a design system before starting a new AI Chat, or apply one from the design system panel later. The builder receives the saved colors, fonts, component styling, brand guidance, and assets so generated interfaces begin on-brand.
When the source design system changes, use **Sync** to bring the latest version into the chat. Editing the theme directly inside a chat detaches it from the saved system, keeping the original design system as the source of truth.
## Export to Any Project
The code panel now provides four outputs:
* **Web CSS** — HeroUI variables, fonts, and component overrides for React projects
* **Native CSS** — Uniwind-ready variables for HeroUI Native
* **`DESIGN.md`** — Visual language and implementation guidance for designers and AI agents
* **`PRODUCT.md`** — Product context, audience, voice, and brand direction
You can also copy the MCP prompt from the editor. With the [HeroUI Pro MCP server](https://heroui.pro/docs/react/getting-started/mcp-server) connected, your AI agent uses `get_design_system_manifest` and `get_design_system_export` to inspect the saved design system and adapt the recommended outputs to the current project.
## Availability
Everyone can create one Design System for free. An active HeroUI Pro license or team workspace unlocks unlimited Design Systems.
Existing Style Guides are now called Design Systems. Previous `/styles` and `/themes` links redirect to the new `/ds` experience.
## Get Started
* [Open Design Systems](https://heroui.pro/dashboard/pro/ds)
* [Create a Design System](https://heroui.pro/ds/new)
* [Set up the HeroUI Pro MCP server](https://heroui.pro/docs/react/getting-started/mcp-server)
* [Read the theming guide](https://heroui.pro/docs/react/getting-started/theming)
# Figma Plugin Sync
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/figma-plugin-sync
> Sync custom HeroUI themes from the Pro Style Guides into your Figma files — keeping supported design tokens aligned between code and design.
May 2026
**HeroUI Theme Sync for Figma** is now available. Sync custom HeroUI themes from the [Pro Style Guides](https://heroui.pro/dashboard/pro/ds) directly into your HeroUI Figma files, keeping supported design tokens aligned between code and design.
## What's New
* **Style Guides → Figma** — Create or customize your theme in the [Style Guides](https://heroui.pro/dashboard/pro/ds), then paste the generated CSS into the Figma plugin
* **Supported token sync** — Sync supported variables like colors, radius, and spacing into your Figma file
* **OSS and Pro files** — Works with supported OSS and Pro Figma files
## How it works
1. Create or customize a theme in the [Style Guides](https://heroui.pro/dashboard/pro/ds) and copy the CSS
2. Open the [HeroUI Theme Sync](https://www.figma.com/community/plugin/1628472563022614828/heroui-theme-sync) plugin inside your Figma file
3. Paste the CSS output from the Style Guides
4. Sync — supported Figma variables update to match your theme
## Before you sync
* **Use the right Figma file version** — Community files require HeroUI Figma Kit **v3.0.3 or newer**. Pro files support the plugin from their initial versions, but we recommend using the latest files for minor updates and fixes.
* **Duplicate first** — Try the plugin in a duplicated file first, check that everything looks good, then sync your main file.
* **Manual updates still required** — Font family and shadow styles need to be updated manually in Figma.
* **Premium themes** — Brutalism, Glass, and Mouve Pro themes are not supported yet.
This is the first version of the Figma plugin, so feedback is very welcome.
## Links
* [HeroUI Theme Sync on Figma Community](https://www.figma.com/community/plugin/1628472563022614828/heroui-theme-sync)
* [Figma setup guide](https://heroui.pro/docs/react/getting-started/figma)
* [Style Guides](https://heroui.pro/dashboard/pro/ds)
# HeroUI Chat on the edge
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/heroui-chat-edge
> Faster generation, instant static previews, resumable sessions, in-chat theme editing, and separate HeroUI Pro and HeroUI Chat workspaces.
August 10, 2026
HeroUI Chat now starts generation on the edge, streams earlier, and serves previews without a sandbox boot. Reloads and temporary connection drops no longer interrupt an active generation.
The dashboard now has separate HeroUI Pro and HeroUI Chat workspaces. Each product has its own navigation.
## AI Chat, rebuilt on the edge
New chats run on a Cloudflare edge runtime powered by [Durable Objects](https://developers.cloudflare.com/durable-objects/). One Durable Object owns each chat's active stream, recovery state, and usage.
The model and tool loop runs on [Pi](https://pi.dev/). Pi handles each turn's file reads, edits, and tool calls while HeroUI controls routing, prompts, previews, and billing.
Existing chats stay on the runtime that created them. They keep working without a migration.
### Faster starts
Generation opens the stream while the project is prepared. Reconnecting resumes the active stream and its original turn.
### Instant previews
Previews now come from immutable static builds. The current preview loads without starting a development server or restoring sandbox dependencies.
Sandboxes still run bounded work such as builds and commands, but they no longer host the preview. When a new build succeeds, the stable preview URL updates immediately. When a build fails, the last working preview remains available.
### Generation that recovers
* **Resumable streams**: Reload the page or reconnect without losing the active generation
* **Stalled-turn recovery**: Interrupted work resumes from durable state or returns an actionable error
* **Bounded tool execution**: Commands stop on abort or timeout, with no detached work left running
* **Safer billing**: Retries reuse the same settlement record, preventing duplicate charges
## Edit the theme inside a chat
Open the theme customizer from a chat to adjust colors, typography, radius, borders, shadows, spacing, and component styles. Changes apply to the preview as you work.
Theme editing works for new edge chats and existing chats. When a chat uses a saved [Design System](https://heroui.pro/docs/react/releases/design-systems), you can sync changes from the builder or edit the chat's copy independently.
## Better results while you build
* **Preview QA**: Chat can inspect the running interface, capture screenshots, and check multi-section pages before finishing
* **Version guidance**: Generated apps detect HeroUI version drift and offer a guided upgrade path
* **Brand details**: Generated apps use the project's name and logo for the browser title and favicon
* **Image attachments**: Pasted and uploaded images use object storage instead of large inline payloads
* **Queued prompts**: Add a follow-up while generation is running, then send it next or move it to the front
* **Completion notifications**: Receive a browser notification when a longer generation finishes
## HeroUI Pro and HeroUI Chat, separated
A product switcher below the dashboard logo moves between **HeroUI Pro** and **HeroUI Chat**.
* **HeroUI Pro**: Home, usage, Design Systems, docs, and roadmap
* **HeroUI Chat**: Home, usage, new chat, and recent conversations
Each product opens its own sidebar, keeping chat history separate from package and account navigation.
## Platform improvements
* **Usage and billing**: The usage dashboard separates HeroUI Pro license downloads from HeroUI Chat requests, tokens, credits, and estimated spend. The billing page also shows Stripe credit grants alongside the current plan
* **License ownership**: Team owners can transfer license ownership to another member from that member's options in the dashboard
* **Package delivery**: Protected downloads use challenge-response validation, account ban checks, and separate rate limits for interactive CLI use and CI
# All Releases
**Category**: react
**URL**: https://heroui.pro/docs/react/releases
> All updates and changes to HeroUI Pro for React, including new features, components, and improvements.
**Using AI assistants?** Simply prompt "Hey Cursor, update HeroUI Pro to the latest version" and
your AI assistant will automatically compare versions and apply the necessary changes. Learn more
about the [HeroUI Pro MCP Server](https://heroui.pro/docs/react/getting-started/mcp-server).
## Latest Release
### 1.0.0-beta.8
**July 2026**
Updated HeroUI Pro React (v1.0.5) and Native (v1.0.3) Figma files, a new Sidebar page switcher, SSR-safe subpath imports for peer-heavy components, KPI chart tooltips, broader RTL support across the Carousel and more Pro components, DataGrid expansion type fixes, a MapLibre GL v6 upgrade, and dependency bumps to HeroUI 3.2.3 and tailwind-variants 3.3.0.
[Read the full release notes →](https://heroui.pro/docs/react/releases/beta-8)
### 1.0.0-beta.7
**July 2026**
New MapLibre-based Map component with markers, popups, routes, arcs, and clusters, RTL support through logical CSS properties, Navbar client-side routing via React Aria RouterProvider, Rich Text Editor link popover polish, and HeroUI 3.2.2 peer dependency support.
[Read the full release notes →](https://heroui.pro/docs/react/releases/beta-7)
### 1.0.0-beta.6
**June 2026**
New Rich Text Editor and Timeline components, PromptInput layout API, Resizable pixel sizing, pasted image attachments, HeroUI 3.2.0 peer dependency support, and focused polish for Agenda, Rating, and scroll surfaces.
[Read the full release notes →](https://heroui.pro/docs/react/releases/beta-6)
### 1.0.0-beta.5
**May 2026**
New AI component family for chat and agent interfaces, upgraded Chat template, markdown/code/source/tool-call primitives, `PromptInput.Queue`, HeroUI OSS 3.1.0 peer minimums, and focused polish for Rating, Navbar, Widget, and Mouve.
[Read the full release notes →](https://heroui.pro/docs/react/releases/beta-5)
### 1.0.0-beta.4
**May 2026**
New Agenda component (day, week, month views with drag-to-create/move/resize and mobile responsive layout), new Mouve premium theme, DataGrid column-toggle crash fix, and Sidebar right-side offcanvas overflow fix.
[Read the full release notes →](https://heroui.pro/docs/react/releases/beta-4)
### 1.0.0-beta.3
**May 2026**
Segment ghost variant, AppLayout content-scroll mode, FileTree checkboxes, EmojiReactionButton read-only, keyboard support for PressableFeedback, and icons bundled internally.
[Read the full release notes →](https://heroui.pro/docs/react/releases/beta-3)
### 1.0.0-beta.2
**April 2026**
Vite/SSR compatibility fix for default icon rendering, peer dependency bump to `@heroui/react@>=3.0.3`, and docs reorganization.
[Read the full release notes →](https://heroui.pro/docs/react/releases/beta-2)
### 1.0.0-beta.1
**April 2026**
The first public beta of **HeroUI Pro React**. 47 premium components across 7 categories, 4 production-ready templates, premium themes, Style Guides, Figma kit, and a complete AI development toolkit — all built on top of [HeroUI v3](https://heroui.pro/docs/react/releases/v3-0-0).
[Read the full announcement →](https://heroui.pro/docs/react/releases/beta-1)
## Platform
### Design Systems
**July 2026**
Import your website or start from scratch, customize a complete HeroUI design system with live previews, keep brand guidance and assets together, use it in AI Chat, and export Web CSS, Native CSS, `DESIGN.md`, and `PRODUCT.md`.
[Read the full release notes →](https://heroui.pro/docs/react/releases/design-systems)
## Tooling
### Figma Plugin Sync
**May 2026**
Sync custom HeroUI themes from Pro Design Systems into your Figma files — colors, radius, spacing, and other supported tokens stay aligned between code and design.
[Read the full release notes →](https://heroui.pro/docs/react/releases/figma-plugin-sync)
### MCP Server v0.2.0
**May 2026**
One MCP server now covers both `@heroui-pro/react` and `@heroui/react` — list components, docs, CSS, and theme variables from a single connection instead of juggling separate OSS and Pro MCPs.
[Read the full release notes →](https://heroui.pro/docs/react/releases/mcp-0.2.0)
## Release Schedule
HeroUI Pro ships regular updates as `@heroui-pro/react` package releases:
* **Patch releases**: Bug fixes and polish as needed
* **Minor releases**: New components and features, typically monthly
* **Major releases**: Architectural changes with migration guides
## Feedback
Found an issue or have a feature request? [File an issue](https://github.com/heroui-inc/heroui-pro/issues) or reach out through the private Discord channel included with your license.
# MCP Server v0.2.0
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/mcp-0.2.0
> Unified MCP — one server covers both @heroui-pro/react and @heroui/react. No more juggling separate OSS and Pro MCPs.
May 2026
The Pro React MCP server now serves **both** `@heroui-pro/react` and `@heroui/react` from a single connection. Pro customers no longer need a separate `@heroui/react-mcp` installed — everything is consolidated.
## What's New
* **`list_components`** returns Pro + OSS components in a single sectioned response
* **`get_component_docs`** accepts any component name from either package and routes to the correct backend
* **`get_css`** returns BEM CSS for both Pro and OSS components
* **`get_docs`** serves guides from both Pro (`/pro/docs/react/...`) and OSS (`/docs/react/...`) documentation
* **`get_component_source_code`** (new) — view OSS component source code for learning and debugging
* **`get_theme_variables`** (new) — get default theme tokens or Pro theme variants (brutalism, glass, mouve)
## Why
Customer feedback was clear: 4 MCP servers + 4 skills across OSS and Pro felt fragmented and bloated. This release cuts the web setup from 2 MCPs to 1.
## Links
* [MCP Server Setup](https://heroui.pro/docs/react/getting-started/mcp-server)
* [Agent Skills](https://heroui.pro/docs/react/getting-started/agent-skills)
* [Component Documentation](https://heroui.pro/docs/react/components)
# Introducing HeroUI v3
**Category**: react
**URL**: https://heroui.pro/docs/react/releases/v3-0-0
> A ground-up rewrite for React and React Native. 75+ web components, 37 native components, Tailwind CSS v4, React Aria, compound architecture, and built for AI-assisted development.
March 2026
Every component rewritten. Every animation moved to CSS. Styles decoupled from implementation. A brand-new React Native library. And tooling that treats AI assistants as a primary development interface.
## Overview
### React (Web)
75+ components. [React Aria Components](https://react-aria.adobe.com/) for accessibility. Tailwind CSS v4 + CSS variables for theming. Styles in a standalone package you can use with any framework.
[Jump to details](#compound-components)
### React Native
37 components with shared design tokens, compound pattern, unified animation API, and adaptive presentation modes. Built native on each platform with [Uniwind](https://uniwind.dev/) for Tailwind CSS v4 support.
[Jump to details](#heroui-native)
### HeroUI Pro
Premium components, templates, and AI tooling for React and React Native. Command palette, Kanban, DataGrid, Dashboard templates, and more. Pre-sale pricing at [heroui.pro](https://heroui.pro).
[Jump to details](#heroui-pro)
## Design Principles
**Composition over configuration:** v2 components were black boxes. v3 adopts compound components: every internal piece is a real element you can style, move, swap, or remove.
**Styles separated from implementation:** `@heroui/styles` is standalone CSS. `@heroui/react` handles behavior. Use the styles with React, plain HTML + Tailwind, or any framework. BEM class names make every slot customizable globally. Swap themes to change not just variables, but how components look and feel.
**Headless when you want it:** Remove the `@heroui/styles` import and you have a headless library. We maintain functionality and accessibility. You focus on your product.
**Performance by default:** v2 used Framer Motion for every animation. v3 replaced it with native CSS transitions and keyframes. Lighter bundles, GPU-accelerated, no JS animation runtime.
**Accessible from the start:** Migrated from React Aria hooks to [React Aria Components](https://react-aria.adobe.com/). Keyboard navigation, focus management, screen readers, and ARIA attributes are built in.
## Compound Components
Here's what the compound pattern looks like in practice:
```tsx
ProductDetails about this product.
Card content goes here.
```
More lines of code. But every piece is a real element you can style, move, or replace. The pattern runs through the entire library, from Accordion to Toast.
Each compound component shares state through React context. The root component creates a style context, and every child consumes it. You never pass classNames down manually:
```tsx
Profile updatedYour changes have been saved.
```
### Progressive Disclosure
Components support both simple and compound usage. Start with the one-liner. Add structure when you need it:
```tsx
// One line
// With icon
// Full control
```
## Tailwind CSS v4 + CSS Variables
Theming runs on Tailwind CSS v4's native CSS variable layer with OKLCH colors. Every design token is a CSS variable:
```css
:root {
--background: oklch(0.9702 0 0);
--foreground: oklch(0.2103 0.0059 285.89);
--accent: oklch(0.6204 0.195 253.83);
--surface: oklch(100% 0 0);
--danger: oklch(0.6532 0.2328 25.74);
--radius: 0.5rem;
}
```
Tailwind's `@theme` directive maps these tokens to utility classes. `bg-accent`, `text-foreground`, `rounded-lg` all resolve to CSS variables. Light and dark mode switch by swapping the values:
```css
.dark,
[data-theme="dark"] {
--background: oklch(12% 0.005 285.823);
--foreground: oklch(0.9911 0 0);
--surface: oklch(0.2103 0.0059 285.89);
}
```
No provider component. No JavaScript theme object. One CSS import, two lines:
```css
@import "tailwindcss";
@import "@heroui/styles";
```
### BEM Classes
Override any component globally through standard CSS:
```css
@layer components {
.button {
@apply font-semibold tracking-wide;
}
.button--primary {
@apply bg-blue-600 hover:bg-blue-700;
}
}
```
No className threading. No style prop gymnastics. Your design system overrides happen in CSS, where they belong.
### Custom Themes
Create a theme by defining your own token set. Everything cascades from there:
```css
@layer base {
[data-theme="ocean"] {
--accent: oklch(0.45 0.15 230);
--background: oklch(0.985 0.015 225);
--radius: 0.75rem;
--border: oklch(0.5 0.06 230 / 22%);
}
}
```
Apply it with a single data attribute:
```html
```
[Style Guides](https://heroui.pro/dashboard/pro/ds) generates these variables visually. Pick colors, adjust radius and spacing, export the CSS.
### Selective Imports
Import the full library or pick individual component styles:
```css
@import "tailwindcss";
@import "@heroui/styles/base" layer(base);
@import "@heroui/styles/themes/default" layer(theme);
@import "@heroui/styles/components/button.css" layer(components);
@import "@heroui/styles/components/card.css" layer(components);
```
Ship only the CSS you use. No unused component styles in production.
## Animation That Respects Users
All component animations use CSS transitions and keyframes tied to data attributes. Popovers fade in with `[data-entering]`. Buttons scale on `[data-pressed]`. Accordions expand with `[aria-hidden="false"]`.
```css
.popover[data-entering] {
@apply animate-in zoom-in-90 fade-in-0 duration-200;
}
.button:active,
.button[data-pressed="true"] {
transform: scale(0.97);
}
```
### Reduce Motion
Some users need animations disabled. HeroUI extends Tailwind's `motion-reduce:` variant to support both the system preference and a data attribute:
```css
.button {
@apply transition-colors motion-reduce:transition-none;
}
```
This responds to the native `prefers-reduced-motion: reduce` media query. It also responds to `data-reduce-motion="true"` on the HTML element, for app-level control:
```html
```
The data attribute overrides the system setting. Set `data-reduce-motion="false"` to force animations on, or remove the attribute to defer to the OS. Every animated component respects this. No opt-in required.
### Bring Your Own Animation Library
Framer Motion, Motion One, or any CSS animation library works alongside HeroUI's built-in transitions:
```tsx
import {motion} from "framer-motion";
import {Button} from "@heroui/react";
const MotionButton = motion(Button);
Animated
;
```
## 75+ Components for React
### Date & Time
Six components: Calendar, RangeCalendar, DateField, DatePicker, DateRangePicker, and TimeField. Built on React Aria's internationalized date library with Gregorian, Buddhist, Persian, and other calendar systems by default. Keyboard navigation, screen reader labels, and locale-aware formatting come free.
### Color
Six color components: ColorPicker, ColorArea, ColorSlider, ColorField, ColorSwatch, and ColorSwatchPicker. Pick from a 2D area, adjust hue and alpha sliders, enter hex values, or select from a swatch palette.
### Data
Need a table with sorting, row selection, column resizing, async loading, and custom cells? Table does all of that. Large datasets get virtualization via React Aria's `Virtualizer`. ListBox shares the same virtualization support.
### Forms
Thirteen form components: TextField, Select, Autocomplete, ComboBox, Checkbox, CheckboxGroup, RadioGroup, Switch, InputOTP, NumberField, SearchField, Slider, and Fieldset. All integrate with React Aria's form validation. `isRequired`, `isInvalid`, and custom error messages through FieldError work across every one.
### Overlays
Seven overlay components. Drawer supports four placements with drag-to-dismiss gestures. Toast stacks notifications with auto-dismiss and promise support. Menu composes with submenus and sections. Plus Modal, AlertDialog, Popover, and Tooltip.
### Navigation
Tabs, Accordion, Breadcrumbs, Pagination, and Link. Tabs support horizontal and vertical orientation. Accordion supports single or multiple expanded panels.
### Feedback
ProgressBar and ProgressCircle handle determinate + indeterminate states. Meter maps values to semantic colors: green for safe, yellow for cautious, red for critical. Skeleton and Spinner round out the set.
### Buttons & Toggles
Button, ButtonGroup, ToggleButton, ToggleButtonGroup, CloseButton, and Toolbar. ButtonGroup connects buttons with shared borders and supports vertical orientation. Toolbar groups buttons, toggles, and separators into an accessible `role="toolbar"` container.
### Granular Imports
Import from the root or from per-component subpaths. Both work:
```tsx
// Root import
import {Button, Card, Table} from "@heroui/react";
// Subpath import
import {Button} from "@heroui/react/button";
import {Card} from "@heroui/react/card";
import {Table} from "@heroui/react/table";
```
## UI for Agents
More developers build by prompting than by reading API docs. HeroUI v3 accounts for that.
### MCP Server
The HeroUI MCP Server connects AI coding assistants (Cursor, Claude Code, VS Code Copilot, Windsurf, Zed) to component docs, props, source code, CSS styles, and theme variables. The AI reads the source of truth directly instead of guessing from training data.
```json
{
"mcpServers": {
"heroui-react": {
"command": "npx",
"args": ["-y", "@heroui/react-mcp@latest"]
}
}
}
```
Ask your AI assistant "update HeroUI to the latest version" and it will compare versions, review the changelog for breaking changes, and apply the necessary code updates automatically.
### Agent Skills
Installable knowledge packs for Cursor and Claude Code. Component patterns, variant usage, theming instructions, and upgrade guides. Preloaded context so the AI writes correct HeroUI code on the first attempt.
### LLMs.txt
Structured documentation files optimized for AI context windows. Published at `/llms.txt` and `/llms-components.txt`, these give any LLM-powered tool a machine-readable summary of HeroUI's API surface.
MCP server, agent skills, LLMs.txt. Three layers that give AI assistants the same access to HeroUI that human developers get from docs.
## HeroUI Native
HeroUI Native is a brand-new library shipping alongside v3 for the web. Different rendering engine, same mental model. Where the platforms diverge, the APIs adapt to feel native on each.
### Try It on Your Device
Scan the QR code with your device's camera or [Expo Go](https://expo.dev/go) to explore all 37 components live:
**[📱 Open Demo App in Expo Go](https://link.heroui.com/native-demo)**
**Android users:** If scanning the QR code redirects to a browser and shows a 404 error, open Expo
Go first and use its built-in QR scanner instead.
### 37 Components
Forms, navigation, overlays, feedback, layout. From Button, Input, and Checkbox to Dialog, BottomSheet, Select, Toast, and InputOTP. Components follow the compound pattern:
```tsx
import {Dialog, Button} from "heroui-native";
;
```
### Familiar Across Platforms
If you know HeroUI on the web, most of that knowledge carries over. Familiar component names, dot notation, and prop patterns wherever possible. Where the platforms diverge (layout primitives, gestures, navigation), the APIs adapt to feel native. The mental model stays the same:
```tsx
// React (web)
Profile updatedYour changes have been saved.
// React Native — similar API, native behavior
Profile updatedYour changes have been saved.
```
Teams working on both web and mobile share knowledge and patterns. The learning curve between platforms is minimal, even where the components differ.
### Shared Design Tokens
Both platforms read from the same token set. Colors like `accent`, `surface`, `danger`, and `success` resolve identically across web and native. Your brand stays consistent without maintaining two separate systems.
```tsx
import {View, Text} from "react-native";
Card TitleConsistent on web and mobile.;
```
Tailwind CSS v4 on both platforms. [Uniwind](https://uniwind.dev/) on native, standard Tailwind on web.
### Unified Animation API
Every animated native component exposes a single `animation` prop. Values, timing, spring configs, enter/exit transitions controlled from one place. Reanimated powers the math, but you never touch it directly:
```tsx
import {Switch} from "heroui-native";
;
```
Disable animations at any level. Per-component, per-tree, or globally:
```tsx
// Single component
// Entire subtree
...
// App-wide
```
Reduce Motion is automatic. When a user enables it in system settings, all animations stop. No extra code.
### Adaptive Presentation Modes
Popover, Select, and Menu switch between popover, bottom-sheet, and dialog with a single prop. Same component, different presentation depending on context:
```tsx
```
No other React Native component library does this.
### Granular Imports
Each native component has its own entry point. Import only what you use:
```tsx
import {HeroUINativeProvider} from "heroui-native/provider";
import {Button} from "heroui-native/button";
import {Card} from "heroui-native/card";
```
### AI Tooling for Native
HeroUI Native ships with its own MCP Server, agent skills, and LLMs.txt. Same tooling structure as the web library:
```json
{
"mcpServers": {
"heroui-native": {
"command": "npx",
"args": ["-y", "@heroui/native-mcp@latest"]
}
}
}
```
## HeroUI Pro
[HeroUI Pro](https://heroui.pro) is now available. 47 premium components, 4 production templates, premium themes, and AI tooling for React — built on the same v3 foundation.
### Pro Components
Components beyond the core library: Command palette, Sidebar, Kanban board, DataGrid, Charts, Emoji Picker, File Tree, and more. 47 components across 7 categories, each with full documentation and live demos.
### Templates
Full-page, responsive starter templates: Dashboard, Mail, Chat, and Finances. Real layouts with real structure. Start from something that works and customize from there.
### Advanced AI Tooling
Pro licenses include a premium MCP server, agent skills, and a design taste skill with Pro component docs, usage patterns, and design principles baked in.
[Read the Pro Beta 1 announcement →](https://heroui.pro/docs/react/releases/beta-1)
## Get Started
### React (Web)
`bash npm i @heroui/styles @heroui/react ``bash pnpm add @heroui/styles @heroui/react ``bash yarn add @heroui/styles @heroui/react ``bash bun add @heroui/styles @heroui/react `
Add two lines to your CSS:
```css
@import "tailwindcss";
@import "@heroui/styles";
```
### React Native
`bash npm install heroui-native ``bash pnpm add heroui-native ``bash yarn add heroui-native ``bash bun add heroui-native `
See the [React docs](https://heroui.pro/docs/react/getting-started/installation) and [React Native docs](https://heroui.pro/docs/native/getting-started/installation) for full setup guides (peer dependencies, Uniwind config, and provider setup).
**Coming from HeroUI v2?** A migration guide will be available when v3 reaches stable release.
## Figma Kit v3
Every component in HeroUI v3 has a 1:1 match in Figma. Same variants, same naming, same structure. The kit uses auto layout throughout, Figma variables that map directly to code tokens (`--accent`, `--surface`, `--radius`), and Figma's new [slots](https://help.figma.com/hc/en-us/articles/38231200344599-Use-slots-to-build-flexible-components-in-Figma) for flexible component composition. Designers rearrange, swap, and customize parts the same way developers do in code.
[Get the Figma Kit](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
## Acknowledgments
[React Aria](https://react-aria.adobe.com/) gave us the accessibility layer we couldn't build as well on our own. Tailwind CSS v4's native CSS variable approach shaped the entire theming system. The compound component pattern was refined by studying how [Radix](https://www.radix-ui.com/), [Ark UI](https://ark-ui.com/), and [Base UI](https://base-ui.com/) solved composition.
Thanks to every community member who filed issues, tested betas, and gave feedback throughout the alpha and RC cycle. The library is better because of you.
## Links
* [React Docs](https://heroui.pro/docs/react/getting-started/installation)
* [React Native Docs](https://heroui.pro/docs/native/getting-started/installation)
* [Style Guides](https://heroui.pro/dashboard/pro/ds)
* [MCP Server](https://heroui.pro/docs/react/getting-started/mcp-server)
* [GitHub Repository](https://github.com/heroui-inc/heroui)
* [Figma Kit V3](https://www.figma.com/community/file/1546526812159103429/heroui-figma-kit-v3)
# Templates
**Category**: react
**URL**: https://heroui.pro/docs/react/templates
> Ready-to-use, full-page templates built with HeroUI Pro. Download and start building from a solid foundation.
# API keys
**Category**: agents
**URL**: https://heroui.pro/docs/agents/configure/api-keys
> Create, scope, and rotate workspace-wide keys used by your Agent integrations.
An API key authenticates your server, not your visitors. Each key can target every current and future active Agent in its workspace, subject to the permissions you assign. The key itself never leaves your infrastructure and the browser never holds a long-lived credential.
Keys are managed per workspace in the
[dashboard](https://heroui.pro/dashboard/agents/api-keys). Workspace members can create
keys and rename or revoke the keys they created. The workspace owner can rename or revoke any key.
Every workspace member can copy an active key.
## Create a key
### Name it for where it will be used
Names are how you tell keys apart when it is time to rotate one. `production-web` and `staging` are useful; `key 1` is not.
### Choose the minimum permissions
Select only what this server needs. The embed token endpoint needs `auth_tokens:create`; data integrations need the matching `users:read`, `conversations:read`, or `runs:read` permission.
### Copy the secret
Copy the key into your secret manager. Active keys can also be copied later from the key table;
revoked keys cannot be retrieved.
### Store it as a server-only environment variable
```bash title=".env.local"
HEROUI_AGENT_API_KEY=he_...
HEROUI_AGENT_ID=your_agent_id
```
### Verify your token endpoint
Send a message from the embed. If your endpoint can mint a token, the conversation connects. A rejected key surfaces as an authentication failure rather than a silent empty response.
Never prefix the key with `NEXT_PUBLIC_` or reference it in client code. Any variable a bundler
can see ends up in your JavaScript, and a leaked key can exercise every permission assigned to it.
## Permissions
| Permission | Dashboard label | Allows |
| -------------------- | -------------------------- | ------------------------------------------------------------------- |
| `auth_tokens:create` | Connect Agents to your app | Mint browser tokens for Agents in this workspace. |
| `users:read` | View users | See people who have used Agents in this workspace. |
| `conversations:read` | View conversations | See conversations and messages across Agents in this workspace. |
| `runs:read` | View runs | View run history, latency, and tool activity across this workspace. |
| `knowledge:read` | View knowledge | Read knowledge documents for Agents in this workspace. |
| `knowledge:write` | Manage knowledge | Add, update, refresh, schedule, and delete knowledge documents. |
Use separate keys when services have different responsibilities. A backend that only exports run metrics, for example, needs `runs:read` but does not need access to users, conversations, or token minting.
The Agent is selected by the request path, for example `/v1/agents/{agentId}/runs`. A key cannot
target an Agent from another workspace.
## Minting browser tokens
Minting a browser token requires `auth_tokens:create` and must happen inside your own server route:
```ts title="app/api/heroui-agent/auth-token/route.ts"
import {createAuthToken} from "@heroui/agent/server";
export async function POST(request: Request) {
const {anonymousId, agentId} = await request.json();
if (agentId !== process.env.HEROUI_AGENT_ID) {
return Response.json({error: "Invalid agent"}, {status: 400});
}
return Response.json(
await createAuthToken({
apiKey: process.env.HEROUI_AGENT_API_KEY!,
identity: {id: anonymousId, type: "anonymous"},
agentId,
}),
{headers: {"Cache-Control": "no-store"}},
);
}
```
See the [Quickstart](https://heroui.pro/docs/agents/quickstart) for the full setup and [Identifying users](https://heroui.pro/docs/agents/identifying-users) for connecting tokens to signed-in people.
To query users, conversations, or runs from a server, see the [HTTP API authentication guide](https://heroui.pro/docs/agents/api-reference/authentication).
## Managing keys
The table shows each key's name, masked token, status, creator, and creation date. The full secret is
encrypted at rest; only its prefix and last four characters appear until someone selects **Copy**.
| Action | Who can use it | Effect |
| ---------- | ----------------------------------------- | -------------------------------------------------------- |
| **Copy** | Any workspace member | Decrypts and copies an active, retrievable key |
| **Rename** | The key's creator, or the workspace owner | Changes the label only. The key keeps working |
| **Delete** | The key's creator, or the workspace owner | Revokes the key. Requires typing the key name to confirm |
Revoking is immediate and cannot be undone. Any server still presenting a revoked key can no longer
mint tokens or call the public API, and existing sessions are rejected on their next authenticated
request across every Agent in the workspace.
## Rotating a key
Because a workspace can hold several active keys, rotation does not need downtime:
1. Create a new key alongside the existing one
2. Deploy the new value to your environment
3. Confirm traffic is healthy in [Logs](https://heroui.pro/docs/agents/monitor/logs)
4. Revoke the old key
Rotate when someone with access leaves, when a key may have been exposed, or on whatever schedule your security policy sets.
Use separate keys and Agents for staging. Workspace-wide authorization does not merge their data;
every public API request still names exactly one Agent.
## Troubleshooting
**Authentication fails after a deploy.** Confirm the environment variable is present in the deployed environment, not only locally, and that the key has not been revoked.
**A public API request returns `403 insufficient_scope`.** The key is valid but does not have the permission required by that endpoint. Create a replacement with the minimum required read permission.
**Tokens are minted but the Agent is rejected.** Confirm the `agentId` belongs to the same workspace as the key and that the Agent is active.
**The key leaked.** Revoke it immediately and create a replacement. Revocation takes effect at once; there is no need to wait for a deploy to finish first.
## Next steps
* [Follow the quickstart](https://heroui.pro/docs/agents/quickstart) to install the SDK and wire up the token endpoint
* [Read the public API reference](https://heroui.pro/docs/agents/api-reference) to query Agent data from your server
* [Identify your users](https://heroui.pro/docs/agents/identifying-users) so conversations follow the people in your product
* [Monitor runs](https://heroui.pro/docs/agents/monitor/logs) to confirm authenticated traffic is arriving
# Appearance
**Category**: agents
**URL**: https://heroui.pro/docs/agents/configure/appearance
> Design the embed visually in the dashboard and have it apply automatically, or export the props and pin them in your code.
import {Kbd} from "@heroui/react";
The appearance editor in the [dashboard](https://heroui.pro/dashboard/agents) is the recommended way to customize the launcher, theme, typography, composer, and start screen against a live preview. Changes are saved with the project and applied at runtime, so restyling a deployed agent does not require a release.
You can also pass any of it as props instead. Props always win, so the two approaches compose rather than compete.
## Where appearance comes from
The embed resolves each setting in this order, so the last one that defines a value wins:
1. **SDK defaults** — what you get with no configuration at all.
2. **Your project's saved appearance** — everything in this editor, fetched when the embed loads.
3. **Props on `HeroUIAgent`** — anything you write in code.
Because the merge happens field by field, a partial override stays partial. Pinning your brand accent in code keeps the launcher icon, radius, greeting, and font that the dashboard supplies:
```tsx
```
### Complete inline example
When appearance needs to ship with your application, disable the saved dashboard configuration and pass the experience as props:
```tsx
```
The Setup panel generates this shape for you when you turn on **Inline configuration**. Use it when appearance changes should go through code review and deploy with the application.
### Choosing an approach
| | Dashboard | Inline props |
| ------------------------- | ---------------------------------- | ------------------------ |
| Changing a color | Takes effect on the next page load | Requires a deploy |
| Reviewed in pull requests | No | Yes |
| Who can change it | Anyone with dashboard access | Anyone who can ship code |
| Differs per environment | Use a separate project | Use your own config |
Most teams start with the dashboard and pin individual values in code as they become load-bearing.
Changes save automatically as you edit. Once saved, they reach visitors on their next page load —
within about a minute, since the configuration is cached at the edge.
## Editing workflow
### Preview the state you are designing
The **Preview** section switches the mock conversation between empty, streaming, generated components, sources, activity, approval, and error states. Design against the states your product will actually produce, not just the empty one.
### Adjust the design
Work through Launcher, Colors, Typography, and Style. Each control updates the preview immediately.
### Export the code
Open **Code** in the toolbar. The Setup panel generates a complete embed for vanilla JavaScript,
Next.js, Vite, TanStack Start, or React Router.
By default the snippet is minimal — identity, the token exchange, and your client tools — because everything you designed here is read at runtime. Turn on **Inline configuration** to write the whole design into the snippet instead, which also sets `remoteConfig={false}` so the two never disagree.
### Paste and deploy
Copy the snippet into your application. You only need to deploy again if you switch to inline configuration and change the design.
## What each section controls
Every editor control maps to a prop on `HeroUIAgent` — the same name you would use to override it in code. The reference documentation for each group is linked in the last column.
| Editor section | Controls | Prop |
| -------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Preview** | Chat state, view mode, Beta badge | `appearance.viewMode`, [`showBetaBadge`](https://heroui.pro/docs/agents/api-reference/hero-ui-agent) |
| **Launcher** | Position, custom icon, icon background, custom CSS | [`appearance.launcher`](https://heroui.pro/docs/agents/api-reference/configuration#appearance) |
| **Colors** | Accent, background, foreground, surface, secondary surface, overlay, tooltip | `appearance.theme.colors` |
| **Typography** | Font family and base size from 12 to 18 pixels | `appearance.theme.typography` |
| **Style** | Main surface, component surface, and radius | `appearance.surfaceVariant`, `appearance.componentSurfaceVariant`, `appearance.theme.radius` |
| **Streaming** | Caret style and text animation | [`markdown`](https://heroui.pro/docs/agents/api-reference/configuration#streaming-markdown) |
| **Start screen** | Greeting, subtitle, suggested prompts, prompt shortcuts | [`startScreen`](https://heroui.pro/docs/agents/api-reference/configuration#start-screen) |
| **Composer** | Default model, model picker, placeholder, disclaimer, attachments, dictation, and permissions | [`composer`](https://heroui.pro/docs/agents/api-reference/configuration#composer), [`permissions`](https://heroui.pro/docs/agents/api-reference/configuration#permissions) |
| **Tools** | Built-in toolkit switches | See [Tools](https://heroui.pro/docs/agents/configure/tools) |
| **Response actions** | Copy, feedback, and retry buttons | `responseActions` |
Accent colors apply to controls, generated UI, focus states, and the launcher unless you give the
launcher its own background. User message bubbles stay neutral: they use the Agent's surface and
foreground tokens (`--ha-surface` and `--ha-fg`), not the accent.
Two sections behave differently from the rest, and both are easy to trip over.
**Tools** is not appearance. The toolkit switches here are the same ones on the [Tools](https://heroui.pro/docs/agents/configure/tools) page — they are stored with the project and take effect server-side, without a deploy. Web, image, and news search also surface as `capabilities` props in the generated code.
**Main surface** controls charts, tables, maps, and other primary data containers. **Component surface** independently controls approval, calendar, commerce, and other response cards. Both are stored with the project and can be pinned in code through `appearance.surfaceVariant` and `appearance.componentSurfaceVariant`.
## Layout modes
| Mode | Behavior |
| ------------ | ------------------------------------------------------------------ |
| **Floating** | A launcher button in a bottom corner opens a panel above your page |
| **Sidebar** | A full-height panel docked to the right edge |
Launcher options only apply in floating mode, and the editor hides them in sidebar mode for that reason. Sidebar suits products where the agent is a primary surface; floating suits products where it is an assistant.
On desktop, opening a floating panel does not lock the host page. Visitors can keep scrolling and
interacting with the page behind it. By default, clicking or tapping outside the panel closes it.
Set `appearance.shouldCloseOnInteractOutside` to `false` when visitors need to keep the chat open
while selecting items on the page—for example, when a client tool tracks table rows or canvas
objects. Escape still closes the panel. The full-screen mobile panel is modal and locks background
scroll while open.
## Theme variants
Choose theme variants from the **Design system** picker. Its Pro collection includes Brutalism, Glass, and Mouve alongside the prebuilt and saved design systems.
A variant ships inside the hosted Agent UI. Selecting one in the dashboard updates the iframe on the
next reload; the customer application does not need another package or stylesheet import.
The stylesheet is prescoped to the agent root, so it restyles the panel without touching the rest of your page. Only import the one variant you selected — each sheet styles the agent surface directly, so loading several makes the last one win.
This import is required even with the minimal snippet. A stylesheet is not something the embed can fetch on your behalf, so switching variants in the dashboard is the one appearance change that also needs a code change.
Pro variants and saved design systems require an active Pro license, which is what grants access
to the `@heroui-pro/react` package. The editor shows an upgrade prompt rather than failing
silently.
## Design systems
The **Design system** picker applies a prebuilt variant or seeds colors, typography, radius, and theme variant from a saved [HeroUI Design System](https://heroui.pro/dashboard/pro/ds), so the embed inherits your product's tokens instead of being styled twice.
The customizer preview updates immediately when you choose a different design system, including its
accent, typography, radius, and theme variant.
The seeded values are a snapshot. Editing any color, font, or radius by hand clears the link to the design system — the embed keeps your manual values rather than silently drifting back.
## Custom fonts
Choosing a non-default typeface makes the embed load that webfont into your page, so a font selected here renders without any work on your side.
The exception is inline configuration: that snippet carries the webfont itself, either as a stylesheet `` or an `@font-face` rule. Keep that markup when you paste it, or the family name resolves to a fallback and the embed looks unstyled. The same applies if you set `appearance.theme.typography.fontFamily` in code — naming a family does not load it.
## Resetting
**Reset** — or pressing R — returns every appearance value to its default after a confirmation. It does not touch your system prompt, knowledge base, tools, or API keys.
## Next steps
* [Read the full configuration reference](https://heroui.pro/docs/agents/api-reference/configuration) for every option and its default
* [Review the HeroUIAgent props](https://heroui.pro/docs/agents/api-reference/hero-ui-agent) the editor generates
* [Set up tools](https://heroui.pro/docs/agents/configure/tools) so the agent can do more than answer questions
# Configure
**Category**: agents
**URL**: https://heroui.pro/docs/agents/configure
> Understand which agent settings live in the dashboard and which live in your code, and where to change each one.
An agent is configured in two places. The [dashboard](https://heroui.pro/dashboard/agents) holds everything the hosted runtime needs to reason — its instructions, the documents it can search, the tools it can call — plus how the embed looks. Your code holds what only your application can supply: identity, the token exchange, and client tools.
Knowing which side owns a setting tells you how it ships. Dashboard settings reach visitors without a deploy. Code settings are part of your bundle.
## What lives where
| Setting | Configured in | Takes effect |
| ------------------------- | ------------------------------------------------------------------------ | ----------------------- |
| System prompt | [Dashboard](https://heroui.pro/docs/agents/configure/system-prompt) | Next message, no deploy |
| Knowledge documents | [Dashboard](https://heroui.pro/docs/agents/configure/knowledge-base) | Next message, no deploy |
| Built-in toolkits | [Dashboard](https://heroui.pro/docs/agents/configure/tools) | Next message, no deploy |
| MCP servers | [Dashboard](https://heroui.pro/docs/agents/configure/mcp-servers) | Next message, no deploy |
| API keys | [Dashboard](https://heroui.pro/docs/agents/configure/api-keys) | Immediately |
| Appearance and theme | [Dashboard](https://heroui.pro/docs/agents/configure/appearance) or code | Next page load |
| Composer and start screen | [Dashboard](https://heroui.pro/docs/agents/configure/appearance) or code | Next page load |
| Permission modes | [Dashboard](https://heroui.pro/docs/agents/configure/appearance) or code | Next page load |
| Client tools | [Code](https://heroui.pro/docs/agents/api-reference/client-tools) | Next deploy |
Everything marked "or code" can be set in either place. The embed reads the appearance saved for your project, and any prop you pass overrides it, field by field. See [Appearance](https://heroui.pro/docs/agents/configure/appearance#where-appearance-comes-from) for how to choose.
## Why the split exists
Client tools run in the browser with the signed-in person's session, so their implementations never leave your application. The hosted runtime only ever receives each tool's name, description, and schema. Identity works the same way: your server mints every browser credential, so the agent never sees your API key.
Instructions, documents, and server-side tools are different. The runtime needs them to plan a response before the browser is involved, so they are stored with the project and fetched on every run. Appearance is stored with the project too, which is what lets you restyle a deployed agent without shipping a release.
Dashboard settings are scoped to a single project. Use separate projects for staging and
production so prompt or tool changes can be validated before they reach customers.
## Configuration pages
## Next steps
* [Follow the quickstart](https://heroui.pro/docs/agents/quickstart) if you have not embedded the agent yet
* [Write a system prompt](https://heroui.pro/docs/agents/configure/system-prompt) to establish how the agent behaves
* [Monitor conversations](https://heroui.pro/docs/agents/monitor) once real traffic starts arriving
# Knowledge base
**Category**: agents
**URL**: https://heroui.pro/docs/agents/configure/knowledge-base
> Attach documents and webpages so the agent answers from your own policies, pricing, and procedures instead of guessing.
A knowledge base is the set of documents an agent can read while answering. Without one, the agent knows your product only through the system prompt and whatever your client tools return. With one, it can look up the details that live in a PDF or a help center page and quote them back accurately.
Knowledge is configured per project in the [dashboard](https://heroui.pro/dashboard/agents/configure/knowledge-base) and applies to every conversation immediately — there is nothing to deploy.
## What to put in it
Knowledge works best for stable reference material that a person would otherwise have to look up:
* **Policies** — refunds, cancellations, shipping windows, SLAs, eligibility rules
* **Pricing and plans** — tiers, limits, overage rates, discount conditions
* **Product specifications** — compatibility, dimensions, supported configurations
* **Procedures** — onboarding steps, troubleshooting runbooks, escalation paths
* **Help center content** — the pages your support team already links to
It is the wrong home for anything live or per-user. Order status, account balances, and current inventory belong in [client tools](https://heroui.pro/docs/agents/api-reference/client-tools), which read your authenticated APIs at the moment the question is asked.
## How the agent uses knowledge
Understanding the retrieval model explains most of the advice further down this page.
Every enabled document's **name** is listed in the agent's system prompt, so the agent knows what the knowledge base covers before it searches. When a question looks like it depends on your own material, the agent calls a `searchKnowledge` tool with a natural-language query. The question is then matched against your documents by meaning rather than by keyword, and the closest passages come back with a short summary.
Matching by meaning is why a customer asking about "canceling my yearly subscription" reaches a document that only ever says "annual plan termination." The words do not have to line up.
There is nothing to tune: no chunk sizes, no index settings, no relevance thresholds. Each
document is prepared for search when you add it, so it becomes searchable the moment it reaches
**Ready**.
Two consequences are worth knowing:
* **Document names are part of the prompt.** `2026 Refund and Cancellation Policy` tells the agent when to search. `export-final-v3` tells it nothing. Names are the cheapest accuracy win available.
* **Excerpts are reference text, not data.** The agent quotes and paraphrases them in prose. It will not turn them into a chart or table, because knowledge passages are treated as untrusted document content rather than a dataset.
### How answers cite documents
An answer that used knowledge carries its sources underneath it. Each citation shows the document name and, when the matched passage sits under a heading, that heading too — so a reader sees `Refund Policy 2026 · Annual plan termination` rather than only a filename. Hovering a citation reveals the passage the answer was drawn from, which is usually enough to confirm it without opening the document.
The heading comes from the extracted markdown, so structure in the original file is what makes citations precise. A document that extracted without headings still cites correctly; it just points at the file rather than the section.
## Add a document
### Choose a source
Open **Add data source** and pick **Import documents** to upload files, or **Learn from a webpage** to fetch a public URL.
### Wait for processing
The document is converted to markdown and indexed for search in the background, appearing as **Processing** until both finish. The table refreshes on its own; most documents settle within a few seconds.
### Confirm the extracted content
Once it reads **Ready**, open the row's **View content** action and skim what was actually extracted. This is the text the agent will search — checking it now avoids debugging a bad answer later.
### Enable knowledge search
Turn on the **Search knowledge** toolkit under [Tools](https://heroui.pro/docs/agents/configure/tools). Documents alone do not give the agent the ability to search them.
### Files
Uploads are limited to 10 MB each and must be one of these formats:
| Format | Extensions |
| ------------ | -------------------------------------- |
| PDF | `.pdf` |
| Word | `.doc`, `.docx`, `.docm` |
| Excel | `.xls`, `.xlsx`, `.xlsm`, `.xlsb` |
| PowerPoint | `.ppt`, `.pptx`, `.pptm` |
| OpenDocument | `.odt`, `.ods`, `.odp` |
| Rich text | `.rtf` |
| EPUB | `.epub` |
| Text/data | `.txt`, `.md`, `.csv`, `.tsv`, `.json` |
| HTML | `.html` |
Anything else is rejected on upload. Text, Markdown, JSON, CSV, and TSV are decoded locally. Other documents use Firecrawl Parse; PDFs use automatic text/OCR selection, page markers, and a 100-page ceiling. The agent tells you when a longer file was truncated. HeroUI requires a Firecrawl Zero Data Retention account for customer documents, and never logs uploaded bytes or extracted content.
### Webpages
Paste a public URL and Context.dev fetches the page and reduces it to its main content — navigation, footers, and sidebars are dropped. Binary uploads use Firecrawl Parse instead, so each source goes through only the provider suited to it. The URL must be `http` or `https`, and pages behind authentication cannot be reached.
Webpage documents refresh every 24 hours by default. Use **Refresh schedule** to turn automatic refresh off or choose every 6 hours, 12 hours, 24 hours, or 7 days. **Refresh from source** starts an immediate check.
Scheduled refreshes are picked up by a five-minute maintenance sweep, so the displayed next refresh time is when the page becomes due rather than an exact execution time. If a refresh fails, the last ready version stays searchable while the system retries it.
Only add pages you have the right to use. Fetched content is stored with your project.
## Document statuses
| Status | Meaning |
| -------------- | ------------------------------------------------------------------------ |
| **Processing** | Extraction and indexing are running. The document is not searchable yet. |
| **Ready** | The document is indexed and the agent can search it. |
| **Failed** | Extraction or indexing did not succeed. Hover the status for the reason. |
| **Disabled** | The document is indexed, but you turned it off. It is not searched. |
**Disabled** is a switch you control, not a failure. Use it to retire a document temporarily without losing it — an expired policy you may need to restore, or a page you want to exclude while you verify a replacement.
## Manage documents
Each row exposes the actions you need to keep the base trustworthy:
* **View content** — read the extracted markdown exactly as the agent sees it
* **Rename** — change the name shown to the agent, up to 120 characters
* **Refresh from source** — re-fetch a webpage, available on weblink documents only
* **Refresh schedule** — choose how often a webpage is checked, or turn automatic refresh off
* **Enable** or **Disable** — include or exclude the document without deleting it
* **Delete** — remove the document and its extracted content permanently
## Limits
| Limit | Value |
| ------------------------------------ | ------------------ |
| Documents per project | 50 |
| File size per upload | 10 MB |
| Extracted text kept per document | 500,000 characters |
| Document name length | 120 characters |
| Documents named in the system prompt | 25 most recent |
Text beyond 500,000 characters is truncated rather than rejected, so a very long document is stored only up to that point.
## Best practices
* **Name documents the way someone would ask about them.** The name is what the agent sees before it searches, so it decides whether the agent searches at all.
* **Split by topic, not by file boundary.** One page per policy retrieves more precisely than a 300-page handbook, where a single question can pull passages from unrelated chapters.
* **Keep the headings.** Citations show the heading above the matched passage, so a document that keeps its section structure tells the reader exactly where an answer came from.
* **Remove superseded versions.** Two documents that disagree produce answers that disagree. Delete or disable the old one instead of relying on the agent to prefer the newer.
* **Prefer text sources over PDFs of screenshots.** Extraction quality sets the ceiling on answer quality.
* **Review real conversations.** [Conversations](https://heroui.pro/docs/agents/monitor/conversations) shows questions people actually asked. Gaps there are the shortlist of documents to add next.
## Troubleshooting
**The agent says it has no knowledge base.** The **Search knowledge** toolkit is off, or no document is both **Ready** and enabled. All three conditions are required before the tool exists at all.
**The agent ignores a document.** Check its name. If the name does not suggest the topic, the agent may never search it. Rename it and ask again.
**A document failed.** Open the status tooltip. The usual causes are a scanned PDF with no text layer, an empty file, or a page that returned no readable content. Re-export the source as text and upload again.
**A document says it needs re-adding.** Documents added before knowledge search moved to semantic matching were never indexed. Delete the row and add the source again.
**Answers cite outdated content.** Webpage documents are only as current as their last sync. Use **Refresh from source**, then confirm with **View content**.
## Next steps
* [Turn on the Search knowledge toolkit](https://heroui.pro/docs/agents/configure/tools) so the agent can query documents
* [Write a system prompt](https://heroui.pro/docs/agents/configure/system-prompt) that tells the agent when to trust documents over its own knowledge
* [Review conversations](https://heroui.pro/docs/agents/monitor/conversations) to find the questions your knowledge base does not answer yet
# MCP servers
**Category**: agents
**URL**: https://heroui.pro/docs/agents/configure/mcp-servers
> Connect Model Context Protocol servers so your agent can borrow tools from external systems without writing an integration.
The Model Context Protocol is a standard way for a system to advertise the tools it offers. Connecting an MCP server to a project borrows those tools into your agent — you supply a URL and any credentials, and the agent gains the server's capabilities without you writing an integration.
Servers are configured per project under [Tools](https://heroui.pro/dashboard/agents/configure/tools) and apply to the next message with no deploy. Adding, editing, or removing a server requires workspace owner permissions.
## Connect a server
### Add the server
Select **Add MCP server** and enter a name, the server URL, and its transport. The name determines how the agent sees the server's tools, so keep it short and recognizable — `Linear` rather than `Our Linear MCP integration`.
### Add authentication
Open **Authentication** and add the header pairs the server expects, such as `Authorization` with a bearer token. Values are encrypted before they are stored and never returned to a browser.
### Review discovered tools
Saving connects to the server and lists the tools it advertises. Open the server to see them, and narrow the allowlist if you do not want all of them exposed.
### Verify in a conversation
Ask the agent to do something that needs one of the tools, then confirm the call in [Logs](https://heroui.pro/docs/agents/monitor/logs). Each run records the tool names it invoked.
## Transports
| Transport | Stored as | When to use |
| ------------------- | --------- | ------------------------------------------------------------ |
| **Streamable HTTP** | `http` | The default, and what most current MCP servers implement |
| **SSE** | `sse` | Older servers that only expose a Server-Sent Events endpoint |
If you are unsure, try Streamable HTTP first. A transport mismatch surfaces as a connection error on the server row rather than a silent failure.
## How tools are named
Every borrowed tool is namespaced with the server's slug, derived from its name:
```
mcp_{slug}_{toolName}
```
A server named `Linear` exposing `create_issue` becomes `mcp_linear_create_issue`. The prefix does real work: it tells the agent which integration a tool belongs to, and it lets the runtime label results as untrusted data from that specific server. The `mcp_` prefix is reserved, so no [client tool](https://heroui.pro/docs/agents/api-reference/client-tools) can impersonate one.
Slugs are lowercase, non-alphanumeric characters become underscores, and the base is capped at 24 characters. Renaming a server does not change an existing slug.
## Authentication
Auth headers are the sensitive part of an MCP connection, and they are handled accordingly:
* Values are **AES-GCM encrypted** before storage and decrypted only inside the API worker
* The dashboard only ever receives header **names**, never values
* The runtime receives values over a service-authenticated internal endpoint
* Editing is **replace-only** — you cannot read a stored value back, you can only overwrite the whole set
A server can carry up to 10 headers, with names up to 80 characters and values up to 2,048 characters.
Because values cannot be read back, keep credentials in your own secret manager as well. Rotating
a token means replacing the header set here, not editing one value in place.
## Tool allowlist
By default every tool a server advertises is available to the agent. Open a server and check only the tools you want to permit — an allowlist with nothing selected means "allow all," which is why the dashboard normalizes an all-checked state back to empty.
Use **Refresh** to re-list tools after the remote server changes. Discovery also re-runs automatically when you change a server's URL, transport, or headers; renaming a server or editing its allowlist does not trigger it.
Narrow the allowlist to what the agent actually needs. It reduces the surface a compromised or
misbehaving server can reach, and a smaller tool set measurably improves tool-selection accuracy.
## Server statuses
| Status | Meaning |
| ----------------- | ----------------------------------------------------------------- |
| **Connected** | The last connection succeeded and tools were listed |
| **Not connected** | The server has never connected successfully |
| **Unreachable** | The last attempt failed. The row carries the error message |
| **Disabled** | You turned the server off. Its tools are not offered to the agent |
A server that cannot be reached during a conversation contributes no tools and the turn continues without them. The agent is not told why, so an unreachable server usually looks like an agent that "forgot" it could do something — check this page when a capability disappears.
## Limits
| Limit | Value |
| ------------------------------------- | ------------------ |
| Servers per project | 10 |
| Tools discovered per server | 100 |
| Tools exposed to the agent per server | 50 |
| Auth headers per server | 10 |
| Server name | 80 characters |
| Description | 500 characters |
| Connection timeout | 10 seconds |
| Tool call timeout | 30 seconds |
| Tool result text kept | 100,000 characters |
Results longer than 100,000 characters are truncated. A server whose tools return very large payloads is better wrapped in something that summarizes before returning.
## Security
An MCP server is third-party code with a foothold in your agent's tool set. Treat it that way.
**Tool results are untrusted input.** The runtime labels every MCP result as untrusted data from the named server, and the agent is instructed never to follow instructions embedded in one. A server that returns "ignore your previous instructions" is describing text, not issuing a command — but the safer posture is to only connect servers you trust with the credentials you gave them.
**URLs are validated to prevent SSRF.** Requests to `localhost`, private IPv4 ranges (`10.x`, `127.x`, `192.168.x`, `172.16–31.x`, `169.254.x`, `100.64–127.x`), IPv6 loopback and link-local addresses, and `.internal` hostnames are rejected. Production requires `https` for public hosts so credentials are never sent in the clear; local development may use `http` against a private address to test a server before deploying it.
**Grant the least privilege that works.** Scope the token you give a server to the operations the agent needs. A read-only credential cannot be talked into a destructive action.
**Prefer client tools for your own product.** If the system is yours, a [client tool](https://heroui.pro/docs/agents/api-reference/client-tools) runs in the visitor's browser with their own session and never needs a long-lived credential stored anywhere.
## Troubleshooting
**The server saved but shows Unreachable.** Read the error on the row. Common causes are the wrong transport, a URL missing its path (many servers expose `/mcp` or `/sse`), or an auth header the server rejects.
**No tools were discovered.** The server connected but advertised nothing. Confirm the endpoint is the MCP endpoint rather than the service's REST API.
**The agent never calls a tool.** Check that the server is enabled, the tool is in the allowlist, and the tool's description explains when to use it. Descriptions come from the remote server, and a vague one leaves the agent no basis to choose it.
**A tool call times out.** Calls are capped at 30 seconds. Long-running work needs to return a handle immediately and report completion separately.
## Next steps
* [Review your tool setup](https://heroui.pro/docs/agents/configure/tools) across toolkits, client tools, and MCP
* [Check the logs](https://heroui.pro/docs/agents/monitor/logs) to confirm which tools each run called
* [Write client tools](https://heroui.pro/docs/agents/api-reference/client-tools) for systems you own
# System prompt
**Category**: agents
**URL**: https://heroui.pro/docs/agents/configure/system-prompt
> Write the private instructions that shape how your agent behaves in every conversation.
The system prompt is the standing instruction set the agent follows on every turn. It is where you establish who the agent is, what it should do, and — most usefully — what it should refuse or escalate instead of improvising.
HeroUI uses that role and scope as the boundary for each request. Clearly unrelated requests are
declined, mixed requests are limited to their relevant portion, and ambiguous requests prompt a
clarifying question. Make the supported domain explicit so the Agent can apply that boundary
reliably.
It lives in project [Settings](https://heroui.pro/dashboard/agents/configure/settings), applies to the next message with no deploy, and is never shown to the people using your product.
## Project details
| Field | Notes |
| ----------------- | ----------------------------------------------------------------------------------- |
| **Project name** | Up to 100 characters. Internal only — used to identify the project in the dashboard |
| **Agent ID** | Read-only. The value you pass as `agentId` to the embed |
| **System prompt** | Up to 12,000 characters of private runtime guidance |
## How your prompt is used
Your instructions are appended to the runtime's own operating rules rather than replacing them. The runtime contributes the parts that have to be correct for the product to work — how to render generated UI, how to treat tool results as untrusted data, when tools exist at all — and your prompt supplies the domain knowledge and judgment on top.
Some of that context is assembled for you and does not belong in your prompt:
* The names of the tools available this turn, including client tools and MCP tools
* The names of your enabled [knowledge documents](https://heroui.pro/docs/agents/configure/knowledge-base), so the agent knows what it can look up
* Whether web search, knowledge search, or computation is available at all
Listing tools or documents yourself only risks contradicting what is actually available. Write about *when* to use them instead.
## Writing a prompt that holds up
A prompt that works in testing and drifts in production is usually missing structure rather than length. These sections cover most of what matters:
**Role and scope.** One or two sentences on what this agent is for, and explicitly what it is not for. Scope is what keeps an agent from confidently answering questions it has no business answering.
**Voice.** How to sound, and how long to be. Products differ far more here than teams expect — "answer in at most three sentences unless asked to elaborate" changes the felt quality of an agent more than most model choices.
**Tool guidance.** When to search knowledge versus ask a clarifying question. When to call a client tool versus explain what the person should do themselves. Which actions to confirm before taking.
**Boundaries.** What to refuse, and what to escalate. Give the agent the exact escape hatch you want — "direct billing disputes to [support@example.com](mailto:support@example.com)" beats a vague instruction not to discuss billing.
**Domain facts that never change.** Product names, tier names, the terminology your customers use. Anything that changes belongs in the [knowledge base](https://heroui.pro/docs/agents/configure/knowledge-base), not here.
Keep the prompt for judgment and put facts in documents. Documents are searched on demand and
edited independently, while every sentence in the prompt is paid for on every single turn.
### Example shape
```text
You are the assistant for Acme, a project management product for design teams.
Help with plans, billing, and how features work. For anything outside Acme,
say briefly that you can only help with Acme.
Answer in at most three sentences unless the person asks for detail. Prefer
concrete steps over description. Never invent a feature or a price.
Search knowledge before answering anything about pricing, limits, or refunds,
and quote what you find. If the documents do not cover it, say so plainly.
Confirm before changing anything in the person's workspace. Direct billing
disputes and account deletion requests to support@acme.com.
```
### MCP and knowledge base example
When both are available, tell the agent which source fits the job instead of listing every tool and document by name:
```text
Use the knowledge base for policies, pricing, plan limits, and troubleshooting.
Search before answering, cite the document you used, and never fill a gap with
an assumption. If the documents disagree or do not answer the question, say so.
Use connected MCP tools for live records and actions, such as checking an issue,
reading its current status, or creating a follow-up. Search for an existing record
before creating one. Before any tool call that writes or changes data, summarize
the action and ask for confirmation.
Use knowledge for reference answers and MCP tools for current state. If the right
source is unavailable, explain what is missing and do not guess.
```
## Iterating
Treat the prompt as something you tune against evidence rather than get right the first time:
1. Read real transcripts in [Conversations](https://heroui.pro/docs/agents/monitor/conversations)
2. Find the answers that were wrong, too long, or out of scope
3. Change one thing in the prompt
4. Watch the same class of question again
Changes apply to the next message in production immediately. Keep a separate project for staging
if you want to validate a prompt change before customers see it.
## Archiving a project
Archiving disables the project's embeds and stops further editing. It is available to the workspace owner from the danger zone in Settings, and the owner can restore an archived project later.
Archive when a project was created by mistake or an integration is retired. For a temporary pause, disabling individual [tools](https://heroui.pro/docs/agents/configure/tools) or [documents](https://heroui.pro/docs/agents/configure/knowledge-base) is less disruptive.
## Next steps
* [Attach knowledge documents](https://heroui.pro/docs/agents/configure/knowledge-base) so the agent answers from your own material
* [Give the agent tools](https://heroui.pro/docs/agents/configure/tools) so it can act, not just answer
* [Review conversations](https://heroui.pro/docs/agents/monitor/conversations) to see how the prompt performs in practice
# Tools
**Category**: agents
**URL**: https://heroui.pro/docs/agents/configure/tools
> Choose what your agent can do beyond writing text, across built-in toolkits, browser client tools, and MCP servers.
Tools are how an agent moves from describing to doing. An agent with no tools can only answer from its instructions and its training. An agent with tools can search your documents, read your application state, call your APIs, and act on the results.
HeroUI Agent has three kinds, and the difference that matters is where each one runs.
| Kind | Runs in | Configured in | Best for |
| --------------------- | ------------------------ | ---------------------------------------------------------------------- | --------------------------------------------- |
| **Built-in toolkits** | HeroUI's hosted runtime | [Dashboard](https://heroui.pro/dashboard/agents/configure/tools) | Capabilities we host for you |
| **Client tools** | The visitor's browser | [Your code](https://heroui.pro/docs/agents/api-reference/client-tools) | Your application state and authenticated APIs |
| **MCP servers** | A server you point us at | [Dashboard](https://heroui.pro/docs/agents/configure/mcp-servers) | Third-party systems that speak MCP |
Client tools are the ones to reach for first. Because they execute in the page with the signed-in person's own session, they inherit your existing permissions automatically — the hosted runtime only ever learns each tool's name, description, and schema, never its implementation or its results' path to your backend.
## Built-in toolkits
Toolkits are capabilities the hosted runtime provides. Switch them on per project; they apply to the next message with no deploy.
| Toolkit | What the agent can do | Requires |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| **Search knowledge** | Query the project's [knowledge base](https://heroui.pro/docs/agents/configure/knowledge-base) and quote relevant passages | At least one Ready, enabled document |
| **Web search** | Search the public web for current information | — |
| **Image search** | Find images matching a description | Web search |
| **News search** | Search recent news coverage with publication dates | Web search |
| **Generate files** | Create downloadable XLSX, PDF, PPTX, DOCX, CSV, Markdown, and JSON assets from exact tool datasets | — |
**Search knowledge** needs both halves. The toolkit switch alone does nothing if the project has
no document that is both **Ready** and enabled — the tool is not offered to the agent at all.
**Image search** and **News search** remain visible but disabled while **Web search** is off. Turning
web search back on restores the preferences you chose previously. News search is off by default.
**Generate files** is hosted by HeroUI. A user can ask a client tool to fetch and filter data, inspect metrics in the conversation, then say “export that to XLSX.” The runtime keeps the exact dataset across turns and sends it to a dedicated artifact subagent, independently of the selected conversation model. It can create a workbook with a Summary sheet, explicit dataset-to-sheet mappings, or one tab per category. PDF, PPTX, and DOCX exports can combine narrative, tables, and real line, bar, area, pie, or donut charts. Generated file cards remain downloadable from conversation history until that conversation is deleted.
Provider-hosted code execution is not Zero Data Retention or HIPAA eligible. File generation is
processed first by OpenAI Code Interpreter and may use Anthropic Code Execution—directly or
through Claude Platform on AWS—as a fallback. HeroUI deletes provider containers and files
immediately after the attempt, but the providers' retention terms still apply. Do not enable this
toolkit for workloads whose policy forbids that processing.
For datasets too large for the 64 KiB client-tool result limit, call `execution.uploadDataSource(...)` inside the client tool and return the resulting `AgentDataSource` handle. JSON, CSV, and TSV sources up to 10 MB are uploaded directly to the artifact provider without adding their rows to the conversation model context.
Web results and knowledge excerpts are both handled as untrusted external content. The agent quotes them in prose and keeps them separate from the datasets your client tools return.
## Client tools
Client tools are declared in code, not the dashboard, because their implementations are yours:
```tsx
import {HeroUIAgent, createToolHelper} from "@heroui/agent";
import {z} from "zod";
const tool = createToolHelper();
const tools = [
tool({
name: "search_orders",
displayName: "Search orders",
description: "Find orders for the signed-in customer by status or date",
parameters: z.object({status: z.string().optional()}),
execute: ({status}, context) => context.apiClient.searchOrders({status}),
}),
];
;
```
A project can declare up to 20 client tools, and the manifest sent to the runtime is capped at 16 KB — keep descriptions purposeful rather than exhaustive.
See [Client tools](https://heroui.pro/docs/agents/api-reference/client-tools) for the full API, typed context, and tool states.
### Reserved names
The runtime owns these names, and a client tool that uses one is rejected:
`callMcpTool`, `composeUI`, `executeSandbox`, `generateFile`, `getComponentSchema`, `renderComponent`, `searchKnowledge`, `searchMcpTools`, `searchWeb`
The `mcp_` prefix is reserved wholesale, so no client tool can impersonate a tool borrowed from an MCP server.
## MCP servers
Connecting an MCP server borrows its tools into your agent. Each one is exposed as `mcp_{server}_{tool}`, so the agent can tell which integration a tool came from, and results are labeled as untrusted data from that server.
Servers are added in the dashboard with an optional per-server allowlist, so you can connect a server that exposes twenty tools and permit only the three you want. See [MCP servers](https://heroui.pro/docs/agents/configure/mcp-servers) for setup, authentication, and security guidance.
## Approvals
Tools that change something should ask first. Client tools declare `needsApproval`, and the embed's permission mode decides how that flag is honored:
| Mode | Behavior |
| ------ | ------------------------------------------------------------ |
| `ask` | Confirm every tool call, whether or not it declares approval |
| `auto` | Confirm only tools that declare `needsApproval` |
| `full` | Run every declared tool without confirmation |
`auto` is the default and the right choice for most products: reads run immediately, writes prompt. Set the mode with `permissions.defaultMode`, and use `permissions.showPicker` to let people change it themselves. The picker only appears once you declare client tools — with nothing to approve, there is nothing to pick.
Allowing `full` lets a visitor bypass every `needsApproval` flag you set. Only enable `showPicker`
when no declared tool can do something irreversible.
## Choosing where a tool belongs
* The data lives behind your app's session, or the action affects your product — **client tool**
* The capability is generic and we host it — **built-in toolkit**
* A third-party system already exposes an MCP server — **MCP server**
* The answer is in a document that rarely changes — not a tool at all, use the [knowledge base](https://heroui.pro/docs/agents/configure/knowledge-base)
## Next steps
* [Connect an MCP server](https://heroui.pro/docs/agents/configure/mcp-servers) to borrow tools from an external system
* [Write client tools](https://heroui.pro/docs/agents/api-reference/client-tools) for your own data and actions
* [Check the logs](https://heroui.pro/docs/agents/monitor/logs) to see which tools each run actually called
# Conversations
**Category**: agents
**URL**: https://heroui.pro/docs/agents/monitor/conversations
> Read full transcripts of what people asked and how your agent answered.
Conversations is where you find out what your agent is actually like to use. Every exchange is stored with its transcript and the runs that produced it, so you can read a real answer instead of guessing how the prompt behaved.
It is also the highest-value page for improving the agent. Aggregate metrics tell you something changed; transcripts tell you what to fix.
## Statuses
A conversation's status reflects its most recent run.
| Status | Meaning |
| ------------- | ----------------------------------------------------- |
| **Pending** | A run has been accepted but has not started streaming |
| **Streaming** | The agent is producing a response right now |
| **Completed** | The last run finished successfully |
| **Failed** | The last run ended with an error |
| **Stopped** | Someone interrupted the response |
Filtering to **Failed** is the quickest way to find the conversations worth reading first.
## Filters
* **Search** — match on conversation title or ID
* **Date range** — presets or a custom range up to three months back
* **Status** — any combination of the five statuses
* **User** — narrow to one person, shown as a chip you can clear
Filters live in the URL, so a filtered view is a link you can paste into a ticket or a channel.
## Export conversations
Choose **Export** to download every conversation matching the current search, date range, status, and user filters. The export includes all matching pages, always includes dashboard preview traffic, and offers three detail levels:
| Detail level | Contents |
| -------------------- | ----------------------------------------------------------- |
| **Summary** | Conversation metadata, status, timestamps, and linked user |
| **Transcript** | Summary fields plus the complete ordered message transcript |
| **Full diagnostics** | Transcript fields plus the UI-safe run diagnostics |
JSON exports use nested `messages` and `runs` arrays. Summary CSV is one file. Transcript CSV downloads a ZIP containing `conversations.csv` and `messages.csv`; Full diagnostics adds `runs.csv`. The files join on conversation IDs.
Internal tenant and end-user keys, raw usage payloads, and other restricted fields are never included. A synchronous export can contain up to 10,000 conversations or 25 MiB; use a narrower date range, filters, or detail level if either limit is reached.
## Conversation detail
The drawer opens with the conversation's ID, user, status, and start and last-activity timestamps, then two sections.
**Transcript** opens in rendered view, reproducing the user-visible conversation: formatted Markdown, files, generated components, sources, and collapsed activity. It intentionally leaves out the composer, response actions, avatars, timestamps, and other Agent controls so you can focus on the output itself. Safe interactions inside the output still work, but the preview cannot retry a response, submit feedback or approvals, invoke client tools, or trigger generated-component actions.
Turn on **View raw** to inspect the literal emitted text with Markdown syntax, message bubbles, and timestamps. Structured parts are summarized instead of rendered in this view. Your choice lasts only while the monitoring page remains open and is not added to the URL or saved in the browser.
Both views update from the same live transcript while the conversation is streaming.
**Runs** lists the model calls behind the conversation, each with its model, duration, and status, linking into [Logs](https://heroui.pro/docs/agents/monitor/logs). A single message can involve several runs when the agent calls tools before answering.
## Using transcripts to improve the agent
Reading transcripts in bulk surfaces patterns no metric will:
* **Questions the agent could not answer** are your [knowledge base](https://heroui.pro/docs/agents/configure/knowledge-base) backlog, in priority order
* **Answers that were technically right but unhelpful** are usually a [system prompt](https://heroui.pro/docs/agents/configure/system-prompt) problem — tone, length, or missing escalation paths
* **The agent explaining what it cannot do** points at a missing [tool](https://heroui.pro/docs/agents/configure/tools)
* **Repeated rephrasing by the same person** means the first answer missed, even though nothing failed
Change one thing at a time and read the same class of conversation again. Prompt changes apply to the next message, so the loop is short.
Filter to **Failed**, then open the linked run in Logs. The transcript shows what the person
experienced and the run shows the error code behind it.
## Next steps
* [Tune the system prompt](https://heroui.pro/docs/agents/configure/system-prompt) based on what you read
* [Add knowledge documents](https://heroui.pro/docs/agents/configure/knowledge-base) for the questions that went unanswered
* [Inspect run details](https://heroui.pro/docs/agents/monitor/logs) for latency, tool activity, and errors
# Monitor
**Category**: agents
**URL**: https://heroui.pro/docs/agents/monitor
> Track usage, engagement, and reliability for your agent, then drill into the users, conversations, and runs behind each number.
Monitoring answers two different questions, and it helps to know which one you are asking. The overview tells you whether the agent is healthy and being used. The activity pages tell you why a specific thing happened.
Start at the [overview](https://heroui.pro/dashboard/agents) for the trend, then follow it down into [users](https://heroui.pro/docs/agents/monitor/users), [conversations](https://heroui.pro/docs/agents/monitor/conversations), or [logs](https://heroui.pro/docs/agents/monitor/logs).
## Headline metrics
The overview covers a 7, 30, or 90 day window. Each card carries a sparkline so you can see the shape of the period, not just its total.
| Metric | What it counts |
| ------------------------ | --------------------------------------------------------------------------- |
| **Conversations** | Conversations started in the period. A badge shows how many are live now |
| **Monthly active users** | Distinct people whose production embed turn recorded usage in the UTC month |
| **Messages** | User and agent messages combined |
| **Run success rate** | Completed runs as a share of runs that reached a terminal state |
Success rate deliberately ignores runs still in flight. Only terminal runs count toward it, so an active period does not depress the number while messages are still streaming.
Monthly active users are deduplicated per Agent and calendar month. Preview chats and turns with no recorded model usage do not count. The card shows the current month and recent historical totals.
## Charts
**Conversation activity** overlays message volume as a line on conversation counts as bars. The ratio between them is the interesting part: many conversations with few messages each suggests people are bouncing after one question, while the reverse suggests genuine back-and-forth.
**Run performance** plots completed against failed runs over time. A step change here usually lines up with a deploy, a prompt change, or an [MCP server](https://heroui.pro/docs/agents/configure/mcp-servers) going unreachable.
## Run summary
| Metric | Meaning |
| ---------------------------- | --------------------------------------------------------------------------- |
| **Avg. time to first chunk** | How long people wait before text starts appearing |
| **Failed runs** | Runs that ended with an error |
| **Sandbox runs** | Runs that reached a terminal state — completed, failed, stopped, or aborted |
| **Completed runs** | Runs that ended successfully |
Time to first chunk is the latency number that matters most to how the agent feels. Total latency can be long without anyone minding, as long as output starts quickly.
## Where to go next
| Question | Page |
| -------------------------------------------- | --------------------------------------------------------------------- |
| Who is using the agent? | [Users](https://heroui.pro/docs/agents/monitor/users) |
| What are people asking, and what did we say? | [Conversations](https://heroui.pro/docs/agents/monitor/conversations) |
| Why did that fail, and how long did it take? | [Logs](https://heroui.pro/docs/agents/monitor/logs) |
Monitoring is scoped to one project. Use the project selector in the dashboard header to switch,
and keep staging in a separate project so test traffic stays out of these numbers.
## Filters and paging
Every activity page shares the same controls: a search field, a date range with presets from **Today** through **Last 30 days**, and page-specific filters. Selections live in the URL, so a filtered view is a link you can share with a teammate.
Custom date ranges reach back three months. Results page 50 rows at a time with previous and next controls.
## Next steps
* [Identify your users](https://heroui.pro/docs/agents/identifying-users) so people appear by name instead of as anonymous visitors
* [View conversations](https://heroui.pro/docs/agents/monitor/conversations) to find gaps in your prompt or knowledge base
* [Debug failures in the logs](https://heroui.pro/docs/agents/monitor/logs)
# Logs
**Category**: agents
**URL**: https://heroui.pro/docs/agents/monitor/logs
> Inspect individual runs for latency, tool calls, errors, and the identifiers you need to report a problem.
A **run** is one model call: the agent receiving a turn, deciding what to do, calling tools, and producing a response. One user message can produce several runs when the agent gathers data before answering.
Logs is the record of those runs, and it is where you go when something is slow or broken. Where [Conversations](https://heroui.pro/docs/agents/monitor/conversations) shows what the person saw, Logs shows what the runtime did.
## Filters
* **Search** — match on request ID, model, or error code
* **Date range** — presets or a custom range up to three months back
* **Status** — Pending, Streaming, Completed, Failed, Aborted, or Stopped
* **Model** — any model in the catalog, plus any custom model IDs seen in your traffic
Arriving from a user or conversation adds a chip for that context, which you can clear to widen the view without losing your other filters.
## Run detail
The drawer groups everything recorded about a run into four sections.
### Status and model
Status, model, reasoning effort, start and completion times, and — on failures — the error code. A run with no model shown used the project's default.
### Performance
| Field | What it tells you |
| ----------------------- | ----------------------------------------------- |
| **Time to first chunk** | How long the person waited before text appeared |
| **Total latency** | Full duration from start to completion |
Time to first chunk and total latency answer different questions. A high first-chunk time means the agent was thinking or calling tools before it said anything, which is what people experience as slowness. High total latency with a fast first chunk is usually a long answer, and rarely a complaint.
### Context
Links to the user and the conversation this run belongs to, so you can move from a technical symptom to the human situation around it.
### Components and tools
The tool names the run called and the kinds of generated UI it produced. This is the ground truth for whether a tool was actually used — more reliable than reading the response and inferring it.
### Identifiers
Run ID, request ID, SDK version, and protocol version.
Include the **request ID** when reporting a problem to HeroUI support. It identifies the exact run
in our systems and turns "the agent failed yesterday" into something diagnosable.
## Debug a failed run
### Isolate the failures
Filter **Status** to **Failed** over the window where the problem appeared. If failures cluster at one point in time rather than spreading evenly, compare that moment against your deploys and prompt changes.
### Read the error code
Open a failed run and note its error code. Then check whether the other failures share it — one repeated code is a single bug, while scattered codes usually mean an upstream dependency.
### Check which tools ran
Look at **Components and tools**. A run that failed without calling the tool you expected is a configuration problem: a [toolkit](https://heroui.pro/docs/agents/configure/tools) that is off, an [MCP server](https://heroui.pro/docs/agents/configure/mcp-servers) that is unreachable, or a client tool that never registered.
### Read what the person saw
Follow the conversation link. The transcript shows how the failure surfaced — sometimes a "failed" run still produced a usable answer, and sometimes a completed one did not.
### Compare against a working run
Clear the status filter and open a successful run for the same model. Differences in latency or tool calls usually point at the cause: an oversized context, a tool that stopped returning data, or a model change.
## Common patterns
**Latency rising with no code change.** Compare the tools and knowledge sources used by an earlier run. A growing [knowledge base](https://heroui.pro/docs/agents/configure/knowledge-base) or tool set can make responses take longer.
**A tool stopped being called.** Check the [MCP server's status](https://heroui.pro/docs/agents/configure/mcp-servers) and its allowlist. An unreachable server contributes no tools and the turn continues without them, so the agent simply appears to have forgotten the capability.
**Runs stuck in Pending.** The run was accepted but never started streaming. Check for a failing token endpoint or a revoked [workspace API key](https://heroui.pro/docs/agents/configure/api-keys).
## Next steps
* [Read the conversation](https://heroui.pro/docs/agents/monitor/conversations) behind a run to see the human context
* [Review your tool setup](https://heroui.pro/docs/agents/configure/tools) when expected tools are not being called
* [Check the overview](https://heroui.pro/docs/agents/monitor) for whether a problem is isolated or a trend
# Users
**Category**: agents
**URL**: https://heroui.pro/docs/agents/monitor/users
> Browse the people who have talked to your agent and open their conversation history.
Users lists everyone who has opened the agent, whether or not you know who they are. It is the page to reach for when you want to answer a question about a person rather than about a conversation — what they have asked before, how long they have been using the agent, whether a complaint came from one account or many.
## Identified and anonymous
Every browser that opens the agent gets an anonymous identity. It becomes an identified user only when your token endpoint tells HeroUI who the person is.
| Type | How it appears |
| -------------- | ----------------------------------------------------------- |
| **Identified** | Your external ID, plus any name, email, and avatar you sent |
| **Anonymous** | A per-project browser ID with no profile |
When an anonymous visitor signs in, their earlier conversations move to the identified person and the identity keeps the earlier first-seen timestamp. That is why an identified user can show a first-seen date from before they had an account.
Mostly anonymous rows mean identity is not wired up. [Identifying
users](https://heroui.pro/docs/agents/identifying-users) is a change to your token endpoint, not the embed, and it makes
every other monitoring page more useful.
Identifiers are hashed into a pseudonymous key before they reach monitoring, so the raw ID you send is never stored.
## Filters
* **Search** — match on name, email, or external ID
* **Date range** — presets from Today through Last 30 days, or a custom range up to three months back
* **User type** — Identified, Anonymous, or both
## Table columns
| Column | Notes |
| --------------- | --------------------------------------------------------------- |
| **User** | Avatar and name when you sent a profile, otherwise the identity |
| **External ID** | The ID your server sent for identified users |
| **Type** | Identified or Anonymous |
| **First seen** | When this identity first opened the agent |
| **Last active** | Most recent activity |
## Export users
Choose **Export** to download every user matching the current search, date range, and user type filters. The export is not limited to the page you are viewing. Dashboard preview traffic is always included unless excluded by a user type filter.
* **CSV** includes stable headers and opens cleanly in spreadsheet applications
* **JSON** returns the same safe user fields as an array of objects
Exports include profile and activity fields such as display name, email, external ID, identity type, avatar URL, first seen, and last active. Internal identity keys are never included. A synchronous export can contain up to 10,000 users or 25 MiB; narrow the filters if either limit is reached.
## User detail
Selecting a row opens a drawer with the person's identity, their type, external ID, email, and first-seen and last-active timestamps, followed by their ten most recent conversations. Each one links straight into [Conversations](https://heroui.pro/docs/agents/monitor/conversations), and **View all conversations** opens that page filtered to this person.
This is the fastest path from a support ticket to a transcript: search the customer's email here, then open the conversation they are describing.
## Next steps
* [Identify your users](https://heroui.pro/docs/agents/identifying-users) if most rows are still anonymous
* [Read their conversations](https://heroui.pro/docs/agents/monitor/conversations) to see what they asked
* [Check the logs](https://heroui.pro/docs/agents/monitor/logs) when a specific person reports an error
# List conversation messages
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/conversations/list-conversation-messages
> Returns persisted messages for one conversation, ordered from oldest to newest. The conversation must belong to the authenticated agent.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# List conversations
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/conversations/list-conversations
> Returns conversations for the authenticated agent, ordered from most recently updated to oldest. Message content is available from List conversation messages.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Retrieve a conversation
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/conversations/retrieve-conversation
> Returns one conversation that belongs to the authenticated agent.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Add a knowledge URL
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/knowledge/create-knowledge-url
> Adds a public HTTP or HTTPS URL and starts extraction and indexing. The returned document begins in processing status.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Delete a knowledge document
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/knowledge/delete-knowledge-document
> Permanently deletes a knowledge document, its extracted content, and its search vectors.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# List knowledge documents
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/knowledge/list-knowledge-documents
> Returns knowledge documents for the authenticated Agent, ordered from newest to oldest. Poll this endpoint or Retrieve a knowledge document to observe processing status.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Refresh a knowledge document
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/knowledge/refresh-knowledge-document
> Starts a refresh of a URL knowledge document. Poll the document until refresh_status is no longer refreshing.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Retrieve knowledge content
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/knowledge/retrieve-knowledge-document-content
> Returns the extracted markdown currently indexed for the document. During a refresh, this remains the previous ready version until the replacement succeeds.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Retrieve a knowledge document
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/knowledge/retrieve-knowledge-document
> Returns one knowledge document and its current ingestion and refresh status.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Update a knowledge refresh schedule
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/knowledge/update-knowledge-document-schedule
> Sets the automatic refresh interval for a URL knowledge document. Use zero to disable scheduled refreshes.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Update a knowledge document
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/knowledge/update-knowledge-document
> Renames a knowledge document or enables or disables it for Agent search.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Upload a knowledge file
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/knowledge/upload-knowledge-file
> Uploads one file up to 10 MB and starts extraction and indexing. Supported formats are PDF, Word, Excel, PowerPoint, text, Markdown, HTML, CSV, and JSON. The returned document begins in processing status.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# List runs
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/runs/list-runs
> Returns model runs for the authenticated agent, ordered from newest to oldest. Usage, cost, and billing data are not exposed.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Retrieve a run
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/runs/retrieve-run
> Returns one model run that belongs to the authenticated agent.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# List users
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/users/list-users
> Returns users observed by the authenticated agent, ordered from most recently seen to oldest.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Retrieve a user
**Category**: agents
**URL**: https://heroui.pro/docs/agents/api-reference/users/retrieve-user
> Returns one user that belongs to the authenticated agent.
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
# Skills
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/agent-skills
> Teach your AI assistant how to build with HeroUI Native Pro
HeroUI Native Pro skills are static instruction files that teach your AI agent `heroui-native-pro` component patterns, React Native conventions, and the HeroUI design system — so it writes correct code without needing to look everything up.
Skills teach your agent **how to write code**. The [MCP server](https://heroui.pro/docs/native/getting-started/mcp-server) gives it **live access** to component docs and theme variables. Use both together for best results.
## Available Skills
| Skill | What it teaches | Install |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- |
| `heroui-native-pro` | `heroui-native-pro` component patterns, MCP tool usage, compound components, Uniwind styling | See below |
| `heroui-pro-design-taste` | HeroUI design system principles — spacing, typography, color, cards, forms, buttons, icons, navigation, accessibility (shared across React and Native) | See below |
## Installation
```bash
curl -fsSL https://heroui.pro/docs/install | HEROUI_PERSONAL_TOKEN=your_token bash -s -- heroui-native-pro
```
```bash
curl -fsSL https://heroui.pro/docs/install | HEROUI_PERSONAL_TOKEN=your_token bash -s -- heroui-pro-design-taste
```
```bash
curl -fsSL https://heroui.pro/docs/install | HEROUI_PERSONAL_TOKEN=your_token bash -s -- heroui-native-pro
curl -fsSL https://heroui.pro/docs/install | HEROUI_PERSONAL_TOKEN=your_token bash -s -- heroui-pro-design-taste
```
Replace `your_token` with your token from the [Pro dashboard](https://heroui.pro/dashboard). The installer detects your tools (Claude Code, Cursor, OpenCode, Codex, Antigravity) and places the skill in the correct directory.
## What's Inside
### `heroui-native-pro`
* Two packages: `heroui-native` (base) vs `heroui-native-pro` (pro) — what comes from where
* Unified MCP: one MCP covers both packages — `list_components`, `get_component_docs`, `get_docs`, `get_theme_variables`
* Critical Native rules: compound components, Uniwind (Tailwind CSS for React Native), React Native Reanimated, Keyboard avoidance
* All Native Pro component categories with names (buttons, date and time, forms, navigation, feedback)
* Theming system overview: semantic colors, typography, spacing with light/dark mode
* Common mistakes and corrections
### `heroui-pro-design-taste`
* Shared across the full HeroUI ecosystem: `heroui-native`, `heroui-native-pro`, `@heroui/react`, `@heroui-pro/react`
* Design principles learned from iterative human feedback
* Categories: spacing, typography, color, cards, forms, buttons, icons, navigation, accessibility
* Design philosophy: semantic over visual, generous whitespace, subtle depth, minimalism
## Start Prompting
```
Build a login screen with email and password fields, a "Sign in" button, and social auth options
```
```
Review this screen and improve the spacing, typography, and color usage following HeroUI design principles
```
```
Create an onboarding flow with illustrations, progress dots, and a "Get Started" button — make it look polished and production-ready
```
## Supported Tools
Claude Code, Cursor, OpenCode, Codex, Antigravity (Gemini CLI). Skills follow the [Agent Skills](https://agentskills.io) open standard.
## Related
* [MCP Server](https://heroui.pro/docs/native/getting-started/mcp-server) — Live access to both `heroui-native-pro` and `heroui-native` docs and themes from a single MCP
* [Design Taste](https://heroui.pro/docs/native/getting-started/design-taste) — Deep dive into the design principles
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview) — Overview of all AI tools
# Design Taste
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/design-taste
> Teach your AI assistant how to build polished, production-quality mobile UIs with HeroUI Native
The Design Taste skill teaches AI agents how to properly use the HeroUI design system to produce polished, production-quality mobile interfaces.
## Installation
```bash
curl -fsSL https://heroui.pro/docs/install | HEROUI_PERSONAL_TOKEN=your_token bash -s -- heroui-pro-design-taste
```
Replace `your_token` with your token from the [Pro dashboard](https://heroui.pro/dashboard). The installer places the skill into the correct directory for your tools.
## What It Teaches Your AI
Without design taste, AI assistants produce functional but generic UIs. This skill fixes that across 10 categories:
* **Spacing** — consistent padding, margins, and gaps using the design token scale
* **Typography** — proper font weights, sizes, and line heights for hierarchy
* **Color** — semantic token usage (`bg-surface`, `text-muted`, `bg-accent`) instead of arbitrary values
* **Cards** — correct anatomy with proper slot composition and shadow usage
* **Forms** — field grouping, label patterns, and validation styling
* **Buttons** — variant hierarchy (primary > secondary > tertiary > ghost)
* **Icons** — consistent sizing, semantic colors, and proper placement
* **Navigation** — tab bars, stack navigators, and drawer patterns that feel native to mobile
* **Accessibility** — accessible labels, roles, VoiceOver/TalkBack support, and touch target sizing
* **General** — minimalism, no duplicate representations, prefer built-in components
## Better Together
| Tool | What it does | When to use |
| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------- |
| **Design Taste** | Teaches *how* to design — spacing, color, typography, polish | "Build a settings screen" — gets the design right |
| **[Native Pro Skill](https://heroui.pro/docs/native/getting-started/agent-skills)** | Teaches *what* components exist and their APIs | "Use a BottomSheet with snap points" — gets the implementation right |
| **[MCP Server](https://heroui.pro/docs/native/getting-started/mcp-server)** | Real-time access to `heroui-native-pro` docs and theme variables | "What props does Button accept?" — gets the details right |
Together they produce UIs that are **correctly implemented**, **beautifully designed**, and **accurately documented**.
## Start Prompting
```
Review this screen and improve the spacing, typography, and color usage
```
```
Build a profile screen with avatar, stats row, action buttons, and a scrollable activity feed
```
```
Audit this screen for design consistency — check color token usage, spacing scale, and typography hierarchy
```
## Related
* [Skills](https://heroui.pro/docs/native/getting-started/agent-skills) — Install guide and full skills reference
* [MCP Server](https://heroui.pro/docs/native/getting-started/mcp-server) — Live access to `heroui-native-pro` documentation and theme variables
* [UI for Agents](https://heroui.pro/docs/native/getting-started/overview) — Overview of all AI tools
# MCP Server
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/mcp-server
> Access HeroUI Native Pro and OSS documentation from a single AI assistant connection
The HeroUI Native Pro MCP server gives your AI agent access to **both** `heroui-native-pro` (Pro) and `heroui-native` (OSS) — component docs, theme variables, and setup guides — all from a single connection.
You do NOT need the separate `@heroui/native-mcp` (OSS MCP) installed. This Pro MCP covers both packages.
## Setup
Add the Native Pro MCP to your editor:
Add to `.cursor/mcp.json` or **Settings > Tools > MCP Servers**:
```json title=".cursor/mcp.json"
{
"mcpServers": {
"heroui-native-pro": {
"url": "https://native-mcp.heroui.pro/mcp",
"headers": {
"x-heroui-personal-token": "HEROUI_PERSONAL_TOKEN"
}
}
}
}
```
Make sure the `heroui-native-pro` connection is **enabled** in **Settings > Tools & MCPs**.
```bash
claude mcp add --transport http heroui-native-pro https://native-mcp.heroui.pro/mcp --header "x-heroui-personal-token: HEROUI_PERSONAL_TOKEN"
```
Or add to `.mcp.json`:
```json title=".mcp.json"
{
"mcpServers": {
"heroui-native-pro": {
"type": "http",
"url": "https://native-mcp.heroui.pro/mcp",
"headers": {
"x-heroui-personal-token": "HEROUI_PERSONAL_TOKEN"
}
}
}
}
```
Restart and run `/mcp` to verify.
Add to `.vscode/mcp.json`:
```json title=".vscode/mcp.json"
{
"servers": {
"heroui-native-pro": {
"url": "https://native-mcp.heroui.pro/mcp",
"headers": {
"x-heroui-personal-token": "HEROUI_PERSONAL_TOKEN"
}
}
}
}
```
Open the file and click **Start** next to `heroui-native-pro`.
Add to `.windsurf/mcp.json`:
```json title=".windsurf/mcp.json"
{
"mcpServers": {
"heroui-native-pro": {
"url": "https://native-mcp.heroui.pro/mcp",
"headers": {
"x-heroui-personal-token": "HEROUI_PERSONAL_TOKEN"
}
}
}
}
```
Restart Windsurf to activate.
Add to `settings.json` (Cmd-,):
```json title="settings.json"
{
"context_servers": {
"heroui-native-pro": {
"url": "https://native-mcp.heroui.pro/mcp",
"headers": {
"x-heroui-personal-token": "HEROUI_PERSONAL_TOKEN"
}
}
}
}
```
Restart and check the Agent Panel for a green indicator.
Add to your `~/.codex/config.toml` (or a project-scoped `.codex/config.toml`):
```toml title="config.toml"
[mcp_servers.heroui-native-pro]
url = "https://native-mcp.heroui.pro/mcp"
http_headers = { "x-heroui-personal-token" = "HEROUI_PERSONAL_TOKEN" }
```
Restart Codex and run `/mcp` to verify.
Get your `HEROUI_PERSONAL_TOKEN` from the [Pro dashboard](https://heroui.pro/dashboard).
## Available Tools
| Tool | Description |
| --------------------- | --------------------------------------------------------------------------------- |
| `list_components` | List all components from both `heroui-native-pro` (Pro) and `heroui-native` (OSS) |
| `get_component_docs` | Full component documentation for any Pro or OSS native component |
| `get_docs` | Guides from both Pro and OSS native documentation |
| `get_theme_variables` | Native theme tokens (default theme) |
### `get_theme_variables`
* `get_theme_variables()` — list available themes
* `get_theme_variables({ theme: "default" })` — default native theme tokens
### `get_docs`
Pro and OSS docs use distinct path prefixes so there's no ambiguity:
* Pro: `/pro/docs/native/getting-started/theming`
* OSS: `/docs/native/getting-started/theming`
## Start Prompting
```
Build a login screen with email and password fields, a "Sign in" button, social auth options, and a "Forgot password?" link
```
```
Create a three-step onboarding flow with illustrations, progress dots, and a "Get Started" button on the last slide
```
```
Build a settings screen with profile avatar, account section with toggle switches, notification preferences, and a danger zone with "Delete Account"
```
```
Show me how to build a bottom tab navigator with Home, Search, Notifications, and Profile tabs using HeroUI Native components
```
## Troubleshooting
Not connecting
The Native Pro MCP uses HTTP transport — no Node.js required. Verify `https://native-mcp.heroui.pro/mcp` is reachable and the MCP is enabled in your editor settings.
Authentication errors
Check your `HEROUI_PERSONAL_TOKEN` in the `headers` field of your config. Verify the token at [heroui.pro/dashboard](https://heroui.pro/dashboard). The CI/CD Token (`HEROUI_AUTH_TOKEN`) is for automation only — use your Personal Token for editor MCP setups.
Tools not being called
Be explicit in your prompts: "Use the HeroUI Native Pro MCP to look up the Button component API" or "Check `heroui-native-pro` docs for the Card anatomy."
Need help?
Contact [support@heroui.pro](mailto:support@heroui.pro) or use the live chat at [heroui.pro/dashboard](https://heroui.pro/dashboard).
# Overview
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/overview
> Tools and resources for building HeroUI Native projects with AI coding assistants
HeroUI Native provides three tools to help AI assistants build better mobile UIs with `heroui-native-pro`. Use them together for the best results.
## Quick Setup
Get your `HEROUI_PERSONAL_TOKEN` from the [Pro dashboard](https://heroui.pro/dashboard), then:
### 1. MCP Server
Gives your agent live access to component docs, theme variables, and guides at runtime. Add to your editor's MCP config:
```json
{
"mcpServers": {
"heroui-native-pro": {
"url": "https://native-mcp.heroui.pro/mcp",
"headers": {
"x-heroui-personal-token": "HEROUI_PERSONAL_TOKEN"
}
}
}
}
```
[Full MCP setup guide](https://heroui.pro/docs/native/getting-started/mcp-server)
### 2. Native Skill
Teaches your agent `heroui-native-pro` conventions, compound patterns, and React Native best practices.
```bash
curl -fsSL https://heroui.pro/docs/install | HEROUI_PERSONAL_TOKEN=your_token bash -s -- heroui-native-pro
```
[Full skills guide](https://heroui.pro/docs/native/getting-started/agent-skills)
### 3. Design Taste Skill
Helps your agent better use the HeroUI design system and produce polished mobile UIs.
```bash
curl -fsSL https://heroui.pro/docs/install | HEROUI_PERSONAL_TOKEN=your_token bash -s -- heroui-pro-design-taste
```
[Full design taste guide](https://heroui.pro/docs/native/getting-started/design-taste)
## What Each Tool Does
| | MCP Server | Native Pro Skill | Design Taste Skill |
| ---------------- | ----------------------------------------------- | ------------------------------------------------- | -------------------------------------------------- |
| **Purpose** | Live access to docs, themes, and component APIs | Teaches component patterns and Native conventions | Teaches design system principles and visual polish |
| **How it works** | Remote tools your agent calls at runtime | Static instructions loaded into agent context | Static instructions loaded into agent context |
| **Install** | Add URL to editor MCP config | One-line `curl` install | One-line `curl` install |
| **Offline** | No (requires network) | Yes | Yes |
| **Best for** | "Look up the Button API" | "Build a screen with HeroUI Native cards" | "Make this look production-quality" |
**Skills teach, MCP does.** Skills give your agent knowledge of patterns and conventions. The MCP server gives it live access to component documentation and theme variables.
## Recommended Stack
For best results, install all three:
1. **MCP Server** — your agent can look up any component API, browse theme variables, and read guides in real time
2. **Native Pro Skill** — your agent knows `heroui-native-pro` conventions, compound patterns, and common mistakes without needing to look them up
3. **Design Taste Skill** — your agent produces polished, well-designed mobile UIs instead of generic layouts
## Using with HeroUI Native OSS
The tools above cover `heroui-native-pro` components. For `heroui-native` base components, use the separate [HeroUI Native OSS docs and tools](https://heroui.com/docs/native/getting-started). Both work side by side.
# Animation
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/animation
> Add smooth animations and transitions to HeroUI Native components
HeroUI Native Pro components follow the same animation patterns as [HeroUI Native OSS](https://heroui.com/docs/native/getting-started) — all built on [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/), with a unified `animation` prop for customizing values, timing, layout transitions, and disabling animations.
Read the full [Animation guide on heroui.com](https://heroui.com/docs/native/getting-started/animation) to learn about the `animation` prop, value modification, spring/timing configs, layout animations, disabling animations, global configuration, and accessibility.
Pro components use the same `animation` prop for customizing animation behavior:
```tsx
import { Stepper } from 'heroui-native-pro';
import { Easing } from 'react-native-reanimated';
Account
```
And the same `"disable-all"` option to turn off animations for an entire component tree:
```tsx
import { SlideButton } from 'heroui-native-pro';
Slide to confirm
```
See each Pro component's API reference for the available `animation` prop options and their configurable properties.
# Colors
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/colors
> Color palette and theming system for HeroUI Native
HeroUI Native Pro components use the exact same color system and design tokens as [HeroUI Native OSS](https://heroui.com/docs/native/getting-started) — semantic CSS variables in the `oklch` color space, automatic light/dark theme switching, and foreground/background pairing. No additional color configuration is needed for Pro components.
Read the full [Colors guide on heroui.com](https://heroui.com/docs/native/getting-started/colors) to learn about the color system, semantic variables, default theme values, customizing and adding colors, the `useThemeColor` hook, and best practices.
Pro components use the same semantic color variables as OSS — `--accent`, `--danger`, `--surface`, and all other tokens apply consistently across both:
```tsx
import { DatePicker } from 'heroui-native-pro';
import { Button, Label } from 'heroui-native';
import { View } from 'react-native';
```
## Chart Colors (Pro)
HeroUI Native Pro adds a chart color palette for use with chart components. These are defined in `heroui-native-pro/styles` and automatically derive from your theme's accent color:
```css
@theme inline static {
--color-chart-1: color-mix(in oklab, var(--accent) 60%, black);
--color-chart-2: color-mix(in oklab, var(--accent) 80%, black);
--color-chart-3: var(--accent);
--color-chart-4: color-mix(in oklab, var(--accent) 80%, white);
--color-chart-5: color-mix(in oklab, var(--accent) 60%, white);
}
```
The palette is centered on your accent (`--color-chart-3`) and fans out from darkest (`--color-chart-1`) to lightest (`--color-chart-5`), giving multi-series charts balanced contrast on either side of your brand color.
### Customizing Chart Colors
Override these variables in your `global.css` to use a custom chart palette:
```css title="global.css"
@theme inline static {
--color-chart-1: oklch(0.35 0.18 220);
--color-chart-2: oklch(0.45 0.18 220);
--color-chart-3: oklch(0.55 0.18 220);
--color-chart-4: oklch(0.7 0.15 220);
--color-chart-5: oklch(0.85 0.12 220);
}
```
**Want to create your own theme?** [Design Systems](https://heroui.pro/ds) lets you visually customize colors (including chart colors), radius, fonts, and more — then export HeroUI Native-compatible CSS from the Native CSS tab.
## useThemeColorPro Hook
The `useThemeColorPro` hook is the Pro counterpart to OSS's [`useThemeColor`](https://heroui.com/docs/native/getting-started/colors#usethemecolor-hook) — it resolves Pro-specific theme variables (currently the chart palette) to their runtime string values, and updates automatically when the theme switches between light and dark.
**Single Color Selection:**
Pass a single color name to get back a resolved color string:
```tsx
import { useThemeColorPro } from 'heroui-native-pro';
const chart1 = useThemeColorPro('chart-1');
;
```
**Multiple Colors Selection:**
You can also select multiple colors at once, which is useful when you need to work with related color values together — for example, assigning one color per series in a chart:
```tsx
import { useThemeColorPro } from 'heroui-native-pro';
const [chart1, chart2, chart3, chart4, chart5] = useThemeColorPro([
'chart-1',
'chart-2',
'chart-3',
'chart-4',
'chart-5',
]);
Series label;
```
This batched form improves performance when working with multiple color values and makes it easier to manage complex charting and theming scenarios where several colors need to be selected and applied together.
# Composition
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/composition
> Build flexible React Native UI with HeroUI Pro compound components, asChild, and custom variants.
HeroUI Native uses the same composition patterns as [HeroUI Native OSS](https://heroui.com/native/getting-started) — compound components with dot notation, the `asChild` prop for polymorphic rendering, and custom variants via `tailwind-variants`.
Read the full [Composition guide on heroui.com](https://heroui.com/docs/native/getting-started/composition) to learn about compound components, the `asChild` prop, custom components, and custom variants.
Pro components use the same compound component pattern with dot notation:
```tsx
import { Stepper } from 'heroui-native-pro';
AccountCreate your account
```
And the same `asChild` prop for polymorphic rendering:
```tsx
import { DatePicker } from 'heroui-native-pro';
import { Button } from 'heroui-native';
```
See each component's documentation for the full compound component anatomy.
# Portal
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/portal
> Configure portals for HeroUI Native Pro overlays, including default and custom PortalHost behavior.
HeroUI Native Pro components use the same portal system as [HeroUI Native OSS](https://heroui.com/docs/native/getting-started) — `PortalHost` is included in `HeroUINativeProvider` by default, and overlay-based components render through portals automatically.
Read the full [Portal guide on heroui.com](https://heroui.com/docs/native/getting-started/portal) to learn about the default setup, custom portal hosts, state management considerations, and the `Portal` / `PortalHost` API reference.
Pro components that use overlays (like `DatePicker`) include a `.Portal` sub-component in their compound anatomy:
```tsx
import { DatePicker } from 'heroui-native-pro';
import { Calendar, Label } from 'heroui-native';
```
# Provider
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/provider
> Configure HeroUI Native provider with text, animation, and toast settings
HeroUI Native Pro components work directly with the [HeroUI Native OSS](https://heroui.com/docs/native/getting-started) `HeroUINativeProvider` — no additional provider or configuration is required. If your app already has the OSS provider set up, Pro components work out of the box.
Read the full [Provider guide on heroui.com](https://heroui.com/docs/native/getting-started/provider) to learn about the provider setup, configuration options (text, animation, toast, dev info), Expo Router integration, the raw provider variant, best practices, and the full API reference.
Pro components inherit all global settings from the same provider — animation configuration, text props, toast settings, and portal management all apply to both OSS and Pro components:
```tsx
import { HeroUINativeProvider } from 'heroui-native';
import type { HeroUINativeConfig } from 'heroui-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
const config: HeroUINativeConfig = {
textProps: {
maxFontSizeMultiplier: 1.5,
},
animation: 'disable-all',
};
export default function App() {
return (
{/* Both OSS and Pro components work here with no extra setup */}
);
}
```
# Styling
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/styling
> Style HeroUI Native components with Tailwind or StyleSheet API
HeroUI Native Pro components use the exact same styling system as [HeroUI Native OSS](https://heroui.com/docs/native/getting-started) — `className` with Tailwind CSS utilities via [Uniwind](https://uniwind.dev/), the `style` prop, render props, `tailwind-variants` for custom variants, exported `classNames` objects, the `cn` utility, and `useThemeColor`. No additional styling setup is needed for Pro components.
Read the full [Styling guide on heroui.com](https://heroui.com/docs/native/getting-started/styling) to learn about styling principles, basic styling, render props, wrapper components, component classNames, responsive design, the `cn` utility, and the `useThemeColor` hook.
Pro components accept the same `className` and `style` props as OSS components:
```tsx
import { Stepper } from 'heroui-native-pro';
Account
Create your account
```
# Theming
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/theming
> Customize HeroUI Native's design system with CSS variables and global styles
HeroUI Native Pro components use the exact same theming system as [HeroUI Native OSS](https://heroui.com/docs/native/getting-started) — CSS variables via [Uniwind](https://uniwind.dev/), automatic light/dark switching, custom themes, custom colors, and custom fonts. No additional theming configuration is needed for Pro components.
Read the full [Theming guide on heroui.com](https://heroui.com/docs/native/getting-started/theming) to learn about color overrides, creating custom themes, adding custom colors, custom fonts, the variables reference, and calculated theme utilities.
Any theme customization you make in your `global.css` applies to both OSS and Pro components equally:
```css
/* global.css — applies to all OSS and Pro components */
@layer theme {
@variant light {
--accent: oklch(0.65 0.25 270);
}
@variant dark {
--accent: oklch(0.65 0.25 270);
}
}
```
```tsx
import { Stepper } from 'heroui-native-pro';
import { Button } from 'heroui-native';
Account
```
# Figma
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/figma
> Design HeroUI Pro Native interfaces with exclusive Figma components and theme-variable sync.
HeroUI Pro Native includes mobile-focused Figma resources for designing screens that stay aligned with the components shipped in your React Native app.
## What's Included
* **Native Pro component Figma file** — Mobile interface assets for HeroUI Pro Native components, matching the anatomy, variants, and slots available in code
* **Figma theme variables plugin** — Sync your supported HeroUI theme variables directly into Figma, so design tokens stay consistent between your codebase and design files
## Download
Figma files (.fig) are available exclusively to HeroUI Pro license holders. Sign in with an active
license to download.
## Figma Sync Plugin
The **Figma Sync Plugin** lets you push your custom themes from the [HeroUI Design Systems](https://heroui.pro/dashboard/pro/ds) directly into the HeroUI Figma design system — keeping supported design tokens and code variables perfectly in sync. It works with supported **OSS** and **Pro** themes.
The plugin is live on the Figma Community: [HeroUI Theme
Sync](https://www.figma.com/community/plugin/1628472563022614828/heroui-theme-sync).
### How it works
1. Create or customize a theme in the [Design Systems](https://heroui.pro/dashboard/pro/ds) and copy the CSS
2. Open the HeroUI Sync plugin inside a Figma file
3. Paste the CSS output from the Design Systems in the plugin
4. Sync — your supported Figma variables, including colors, radius, and spacing, update instantly
### Before you sync
* **Use the right Figma file version** — Community files require HeroUI Figma Kit **v3.0.3 or newer**. Pro files support the plugin from their initial versions, but we recommend using the latest files for minor updates and fixes.
* **Treat code as the source of truth** — create and maintain your themes in the [HeroUI Design Systems](https://heroui.pro/dashboard/pro/ds), then sync those variables into Figma with the plugin.
* **Duplicate first** — run the plugin in a duplicated Figma file first, check that everything looks right, then sync your main file.
### Notes and limitations
* Font family changes are manual. In Figma, open the Variables panel, go to the Typography collection, and update the font variable.
* Shadow styles also require a manual update in Figma.
* Brutalism, Glass, and Mouve Pro themes are not supported yet.
This is the first version of the Figma plugin, so your feedback is welcome.
## HeroUI OSS Figma
Looking for the free HeroUI OSS Figma kit? It's available on the Figma Community:
* [HeroUI Figma Kit V3](https://www.figma.com/community/file/1546526812159103429) (free, OSS components)
# Installation
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/installation
> Set up HeroUI Native Pro in your project
## Requirements
* Bare [React Native](https://reactnative.dev/) or [Expo](https://expo.dev/)
* [Tailwind CSS v4](https://tailwindcss.com/) via [Uniwind](https://uniwind.dev/)
* [HeroUI Native OSS](https://heroui.com/docs/native/getting-started) (`heroui-native`)
If you haven't set up HeroUI Native OSS yet, follow the [HeroUI Native Quick Start](https://heroui.com/docs/native/getting-started/quick-start) first.
## Login
Run the HeroUI Pro CLI and log in with your GitHub account:
```bash
npx heroui-pro@latest login
```
```bash
bunx heroui-pro@latest login
```
```bash
pnpm dlx heroui-pro@latest login
```
A browser tab will open automatically. Sign in with your GitHub account, authorize the application, and wait for the terminal to confirm:
```
Logged in as @your-username
```
## Install
Once logged in, run:
```bash
npx heroui-pro@latest install
```
```bash
bunx heroui-pro@latest install
```
```bash
pnpm dlx heroui-pro@latest install
```
The CLI handles everything automatically:
1. **Adds `heroui-native-pro`** to your project (if not already present)
2. **Downloads Pro components** from the CDN using your license
3. **Detects missing peer dependencies** (like `@internationalized/date` for date components) and installs them
4. **Configures your package manager** — for pnpm and bun, offers to allowlist the postinstall script so future installs work seamlessly
After installation completes you'll see:
```
└ ✓ HeroUI React Native Pro v1.0.0-beta.1 installed successfully.
```
You can also use the **interactive menu** by running `npx heroui-pro` with no arguments. It provides a guided flow for login, installation, and account management.
## Configure global.css
Add the HeroUI Native Pro source path to your `global.css` file so Tailwind can scan Pro component classes:
```css title="global.css"
@import 'tailwindcss';
@import 'uniwind';
/* [!code highlight] */
@import 'heroui-native/styles';
@import 'heroui-native-pro/styles';
@source './node_modules/heroui-native/lib';
/* [!code highlight] */
@source './node_modules/heroui-native-pro/lib';
```
The `@source` path is relative to your `global.css` file. Adjust accordingly if your CSS file is not at the project root (e.g., `../node_modules/heroui-native-pro/lib` if `global.css` is in `app/`).
## Use a Pro Component
```tsx
import { Stepper } from 'heroui-native-pro';
import { View } from 'react-native';
export default function MyComponent() {
return (
AccountCreate your accountProfileSet up your profile
);
}
```
## CLI Reference
| Command | Description |
| ------------------------------ | ------------------------------------------------------ |
| `heroui-pro login` | Log in with GitHub |
| `heroui-pro install` | Install Pro packages, peer deps, and configure your PM |
| `heroui-pro install --yes` | Non-interactive install (auto-accept all prompts) |
| `heroui-pro install --dry-run` | Preview what would be installed without executing |
| `heroui-pro status` | Show login and installed package info |
| `heroui-pro logout` | Sign out |
## CI/CD
For automated environments (GitHub Actions, Vercel, Netlify, EAS, etc.), use a **CI/CD token** instead of interactive login. Get your token from the [dashboard](https://heroui.pro/dashboard).
Set the `HEROUI_AUTH_TOKEN` environment variable in your CI pipeline:
Add `HEROUI_AUTH_TOKEN` as a [repository secret](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions), then reference it in your workflow:
```yaml title=".github/workflows/build.yml"
env:
HEROUI_AUTH_TOKEN: ${{ secrets.HEROUI_AUTH_TOKEN }}
```
Add `HEROUI_AUTH_TOKEN` as an [EAS secret](https://docs.expo.dev/build-reference/variables/):
```bash
eas env:create --name HEROUI_AUTH_TOKEN --value your-cicd-token --scope project --visibility secret
```
```bash
export HEROUI_AUTH_TOKEN=your-cicd-token
npm install
```
When `HEROUI_AUTH_TOKEN` is set, the postinstall script automatically authenticates and downloads Pro artifacts — no interactive login needed. This works with all package managers.
Use your **CI/CD token** for pipelines, not your personal token. CI/CD tokens are scoped to your license and can be rotated independently from the [dashboard](https://heroui.pro/dashboard).
## Verify Installation
After installation, verify everything is working:
1. Check that your app starts without errors
2. Try importing and using a Pro component like `Stepper` or `DatePicker`
3. Run the CLI to check your status:
```bash
npx heroui-pro status
```
## Troubleshooting
**Installation fails with permission errors**
Try running the CLI with elevated permissions or check that your package manager has write access to `node_modules`.
**pnpm or bun: postinstall didn't run**
The CLI handles this automatically — `heroui-pro install` downloads artifacts directly and offers to configure your `package.json` so future installs work natively. If you prefer to configure it manually:
* **bun**: add `"trustedDependencies": ["heroui-pro", "heroui-native-pro"]` to `package.json`
* **pnpm**: add `"pnpm": { "onlyBuiltDependencies": ["heroui-pro", "heroui-native-pro"] }` to `package.json`
**Yarn Berry (PnP) not supported**
HeroUI Pro requires `node_modules`. If using Yarn Berry, add `nodeLinker: node-modules` to your `.yarnrc.yml`.
**Authentication expired**
Run `npx heroui-pro login` to re-authenticate. Sessions are valid for 180 days.
Still having issues? Contact [support@heroui.pro](mailto:support@heroui.pro) or reach out via live chat at [heroui.pro/dashboard](https://heroui.pro/dashboard).
## What's Next?
* [Browse Components](../components) — See all available Pro components
* [Provider](./provider) — Configure the HeroUI Native provider for your app
* [Agent Skills](./agent-skills) — Set up AI tools for HeroUI Native Pro development
# Licensing
**Category**: native
**URL**: https://heroui.pro/docs/native/getting-started/licensing
> HeroUI Pro Native licensing: perpetual access, optional renewals, team seats, personal tokens, and v2 upgrades.
HeroUI Pro uses a simple license model: you make a one-time purchase, get one year of updates, and keep access to the version you already paid for even if you never renew.
## How It Works
* **One-time purchase** — HeroUI Pro is not a mandatory subscription.
* **1 year of updates included** — Your license includes an Updates Window with access to new components, fixes, templates, and features released during that period.
* **Renewal is optional** — If you want another year of updates, you can renew. If you do not renew, you keep using the latest version you were entitled to.
## What "Perpetual" Means
Perpetual access means your HeroUI Pro license does not expire in the usual SaaS sense.
If your Updates Window ends and you choose not to renew:
* You keep access to all components, templates, and features released before your license expired
* Your existing projects can continue using that version
* You are not locked out of your previous purchase
* You simply stop receiving new releases after that point
## What Renewal Gives You
Renewing extends your Updates Window for another year.
That means you keep receiving:
* New components and templates
* Feature updates
* Bug fixes and improvements
* New releases across the products included in your plan
If you skip renewal, nothing breaks. You just stay on the latest eligible version from your last active Updates Window.
## Team Licenses
Team licenses are built for companies that want centralized billing, member management, and shared access under one plan.
Team plans are seat-based. Owners and Members use one seat each; Billing users do not use a seat.
Here is the current team flow:
1. Purchase a Team plan.
2. Go to [Members](https://heroui.pro/dashboard/pro/members).
3. Create your team.
4. Invite people as Members or Billing users.
5. Members accept and link a GitHub account; Billing users only need to sign in and accept.
After a Member invitation is accepted, HeroUI Pro creates the individual license keys for the products included in your team plan automatically. It also creates that Member's Personal Token automatically. Billing users receive financial access only.
### Team Roles
| Role | Uses a seat | Product access | Team management | Billing management |
| ------- | ----------- | --------------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------- |
| Owner | Yes | Yes | Full access, including roles and ownership transfer | Yes |
| Member | Yes | Yes, including personal package keys and a Personal Token | Read-only roster | No |
| Billing | No | No product access, package keys, Personal Token, or CI/CD Token | Read-only roster | Plans, seats, payments, invoices, renewals, and AI credit purchases |
### Team Owners
Team owners can:
* Create and rename the team
* Invite members by email
* Remove members or cancel pending invitations
* Add more seats from Billing when all seats are in use
* View usage data for active license keys
* Change people between Member and Billing
* Transfer ownership to an accepted teammate and remain as either a Member or Billing user
Ownership controls team administration and is separate from the original purchase record. Transferring ownership does not move or duplicate Stripe charges, orders, or the commercial license record.
### Team Members
Invited members receive an email with an invitation link. To activate access, they need to:
* Sign in to HeroUI Pro
* Link a GitHub account in their account settings
* Return to the invite link and accept the invitation
GitHub is still required to provision the per-platform package license keys. Once the invitation is accepted, HeroUI Pro creates the member's Personal Token automatically as well.
### Billing Users
Billing users can manage the plan, seat count, payment methods, invoices and receipts, renewal, and AI credit purchases. They can also view the team roster. They do not use a seat and cannot access Pro products, themes, downloads, package keys, Personal Tokens, CI/CD Tokens, or team-wide product usage.
### Usage and Visibility
Team owners can open [Usage](https://heroui.pro/dashboard/pro/usage) to monitor download activity across active license keys and local AI usage buckets.
The usage page currently lets owners:
* See total downloads for a selected license
* Change the date range
* Switch between active license keys, including member-assigned keys, Personal Tokens, and the CI/CD Token
When team members use their own Personal Token for MCPs, Skills, and installs, owners get practical per-member attribution. Team members do not get access to the team-wide usage page.
## Personal Token
HeroUI Pro includes a Personal Token for every licensed user.
This token is for human local workflows such as:
* MCP server setups in editors like Cursor, Claude Code, VS Code, Windsurf, and Zed
* HeroUI Pro Skills installs
* Other local AI or developer-tooling flows that should be attributed to a specific user
See [MCP Server](https://heroui.pro/docs/native/getting-started/mcp-server) and [Agent Skills](https://heroui.pro/docs/native/getting-started/agent-skills) for setup instructions.
### How It Is Created
* For Individual license owners and team Owners, the Personal Token is created automatically when access becomes active
* For invited team members, the Personal Token is created automatically when they accept the invitation
* Every Owner and Member gets their own Personal Token; Billing users do not
### Where to Find It
You can view your Personal Token in:
* [Overview](https://heroui.pro/dashboard)
* [Tokens](https://heroui.pro/dashboard/pro/tokens)
This token is only visible to the licensed user it belongs to. Team owners cannot view a team member's Personal Token secret, and team members cannot view someone else's token.
### Why It Matters
* Personal Token usage is attributed to the specific licensed user
* It is the correct default for local MCP and Skills usage
* Team members should use this token for normal local AI workflows
## CI/CD Token
HeroUI Pro also includes a CI/CD Token for automated installs and deployment pipelines.
Unlike the Personal Token, the CI/CD Token is designed for non-interactive environments. It lets your pipeline authenticate without signing in with GitHub on every run.
### How It Is Created
* The CI/CD token is created automatically for active licenses
* It is intended for CI providers like GitHub Actions, Vercel, Railway, and similar deployment systems
* It works without an interactive GitHub login
* Plan access still applies: React requires a Web or Super plan, and React Native requires a Mobile or Super plan
* CI/CD usage does not have monthly download limits (1,000 package downloads per day per license)
### Where to Find It
If you are the license owner, you can view your CI/CD Token in:
* [Overview](https://heroui.pro/dashboard)
* [Tokens](https://heroui.pro/dashboard/pro/tokens)
If you are an invited team member, you will not be able to view or reset the CI/CD Token. Only the license owner or team owner can access it.
### Environment Variable
Use `HEROUI_AUTH_TOKEN` as the secret that your CI system exposes to the step that installs HeroUI Pro.
```yaml title=".github/workflows/deploy.yml"
env:
HEROUI_AUTH_TOKEN: ${{ secrets.HEROUI_AUTH_TOKEN }}
```
Keep the token server-side only. Do not expose it to client-side code or public logs.
### Scope
The CI/CD Token is only for non-interactive automation.
### Security Best Practices
* Store the token in your CI provider's encrypted secrets
* Never commit it to your repository
* Never expose it in browser code, preview builds, or public environment variables
* Rotate it from the Tokens page immediately if you think it was leaked
* After rotating, update every pipeline that used the old token
### Usage Tracking
CI/CD downloads appear in the same usage reporting as your other license keys. Owners can inspect CI/CD usage from [Usage](https://heroui.pro/dashboard/pro/usage) by selecting the CI/CD key from the license picker.
## Personal vs CI/CD
| Token | Who can view/reset it | Use it for | Env var | Attribution |
| -------------- | -------------------------------- | ------------------------------------- | ----------------------- | ------------------ |
| Personal Token | The licensed user only | Local MCP, Skills, and install flows | `HEROUI_PERSONAL_TOKEN` | Per-user |
| CI/CD Token | License owner or team owner only | CI/CD pipelines and shared automation | `HEROUI_AUTH_TOKEN` | Shared team bucket |
## Which Token Should I Use?
* Use the Personal Token for any local editor, MCP, or Skills setup
* Use the CI/CD Token only for non-interactive environments like GitHub Actions, Vercel, or Railway
* Use the Personal Token for all local AI tooling, including MCPs, Skills, and installs
## For HeroUI Pro v2 Customers
If you previously purchased HeroUI Pro v2, you are eligible for an upgrade discount on v3. Use the same email address from your v2 purchase and the discount should apply automatically.
If you need your old dashboard or downloads, go to [HeroUI Pro v2](https://v2.heroui.pro).
If the discount does not apply automatically, contact [support@heroui.pro](mailto:support@heroui.pro).
## Legal Terms
This page is the plain-English version. For the exact legal definitions of **Perpetual**, **Renewal**, and **Updates Window**, see the [HeroUI Pro Terms and Conditions](https://heroui.pro/terms).
# FAB
**Category**: native
**URL**: https://heroui.pro/docs/native/components/fab
> A floating action button that expands into a list of actions with automatic content placement and a shared, progress-driven open/close animation.
## Import
```tsx
import { FAB } from 'heroui-native-pro';
```
## Anatomy
```tsx
...New messageShare
```
* **FAB**: Root container. Owns the open state (controlled via `isOpen` + `onOpenChange` or uncontrolled via `isDefaultOpen`), resolves the content placement and alignment — automatically from the trigger position on screen by default — and drives the shared open/close progress (`0` = idle, `1` = open, `2` = close) that orchestrates the overlay, items, and trigger rotation. Cascades `disable-all` to animated descendants.
* **FAB.Trigger**: The floating button itself. Toggles the open state on press and measures its own position so auto placement can resolve. Its content rotates with the shared progress (a plus icon reads as a close affordance while open). Position the FAB by passing positioning classes (e.g. `absolute bottom-6 right-6`) to the root.
* **FAB.Portal**: Renders the overlay and content in a portal layer above other content (using `FullWindowOverlay` on iOS). Stays mounted while the close animation plays and re-provides the FAB contexts to portaled descendants.
* **FAB.Overlay**: Optional backdrop behind the content. Its opacity follows the shared progress and pressing it closes the FAB. The `default` variant paints a solid backdrop; the `blur` variant renders an animated blur layer instead. Replace the part with a custom component built on `useFABAnimation` for fully custom backdrops.
* **FAB.Content**: Positioned column of items. Placement and alignment follow the root resolution and the column hugs the trigger edge. Provides each child its index so items can stagger.
* **FAB.Item**: Single action row. Appears and disappears with the shared progress — staggered by default, starting from the item nearest the trigger — and closes the FAB on press unless `closeOnPress={false}`. Plain string children are wrapped in `FAB.ItemLabel` automatically.
* **FAB.ItemBackground**: Optional theme-aware background container rendered behind the item content. Mounted automatically when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop on `FAB.Item`.
* **FAB.ItemLabel**: Text label inside an item. Only needed for custom layouts (e.g. icon + label); string children get it for free.
## Usage
### Basic usage
Place the FAB anywhere on screen; the content placement resolves automatically. A FAB in the bottom-right corner opens its items upwards with end alignment, a FAB in the top-left opens downwards with start alignment, and so on.
```tsx
New messageNew label
```
### Manual placement
Override the automatic resolution with explicit `placement` and `align` values on the root.
```tsx
......
```
### Items appearing mode
Items appear staggered by default (nearest to the trigger first, reversed on close). Set `itemsAppearance="normal"` to animate all items together.
```tsx
......
```
Control the stagger intensity with `animation.stagger.itemWindow` on the root: the fraction of the progress range each item's animation occupies. Smaller values produce a more sequential stagger; `1` makes all items animate together.
```tsx
......
```
### Items with icons
Pass elements as item children for custom layouts. Use `FAB.ItemLabel` for the text part.
```tsx
Share
```
### Controlled
Drive the open state externally with `isOpen` and `onOpenChange`.
```tsx
const [isOpen, setIsOpen] = useState(false);
......;
```
### Blur backdrop
Use the built-in `blur` variant for an animated blur backdrop: the blur intensity follows the shared progress instead of the overlay opacity. iOS only, requires the optional `expo-blur` package; other platforms (or a missing package) fall back to the default solid backdrop. When the library theme is `glass`, the blur variant is used by default.
```tsx
......
```
### Custom backdrop
Build a custom backdrop on the shared progress via `useFABAnimation` and place it inside `FAB.Portal` instead of `FAB.Overlay`. The progress follows the `[idle, open, close]` = `[0, 1, 2]` convention.
```tsx
import { useFAB, useFABAnimation } from 'heroui-native-pro';
import { BlurView } from 'expo-blur';
import { Pressable, StyleSheet } from 'react-native';
import Animated, {
interpolate,
useAnimatedProps,
} from 'react-native-reanimated';
const AnimatedBlurView = Animated.createAnimatedComponent(BlurView);
function FABBlurBackdrop() {
const { onOpenChange } = useFAB();
const { progress } = useFABAnimation();
const animatedProps = useAnimatedProps(() => ({
intensity: interpolate(progress.get(), [0, 1, 2], [0, 50, 0]),
}));
return (
onOpenChange(false)}
>
);
}
...;
```
### Custom animation configuration
Customize the progress driver on the root (a spring with `{ mass: 3, stiffness: 1200, damping: 90 }` unless configured) and the per-part motion on each part.
```tsx
...
...
```
## Example
```tsx
import { Ionicons } from '@expo/vector-icons';
import { FAB } from 'heroui-native-pro';
import { Text, View } from 'react-native';
const MESSAGES = [
{
sender: 'Maya Chen',
subject: 'Design review notes',
time: '09:41',
},
{
sender: 'Hero Bank',
subject: 'Your statement is ready',
time: '08:15',
},
{
sender: 'Luis Ortega',
subject: 'Re: Offsite agenda',
time: 'Yesterday',
},
];
export default function InboxComposeFab() {
return (
Inbox3 unread messages
{MESSAGES.map((message) => (
{message.sender}
{message.time}
{message.subject}
))}
console.log('new message')}>
New message
console.log('new label')}>
New label
console.log('new folder')}>
New folder
);
}
```
## API Reference
### FAB
| prop | type | default | description |
| ----------------- | --------------------------- | ------------- | ----------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound parts to render (`FAB.Trigger` + `FAB.Portal`) |
| `placement` | `FABAutoPlacement` | `"auto"` | Side of the trigger on which the content opens. `"auto"` resolves from the trigger screen position |
| `align` | `FABAutoAlign` | `"auto"` | Alignment along the perpendicular axis. `"auto"` resolves from the trigger screen position |
| `itemsAppearance` | `FABItemsAppearance` | `"staggered"` | Appearing mode for the items (`"staggered"` or `"normal"`) |
| `isOpen` | `boolean` | - | Whether the FAB is open (controlled mode) |
| `isDefaultOpen` | `boolean` | - | Default open state for uncontrolled mode |
| `isDisabled` | `boolean` | - | Whether the FAB is disabled |
| `className` | `string` | - | Additional CSS classes for the root container (use for positioning, e.g. `absolute bottom-6 right-6`) |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Callback fired when the open state changes |
| `animation` | `FABRootAnimation` | - | Animation configuration for the progress (spring/timing driver, stagger, and `disable-all` cascade) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### FABAutoPlacement
Side of the trigger on which the content is rendered.
* `"auto"`: Resolves from the trigger position — a trigger in the bottom half of the screen opens upwards, a trigger in the top half opens downwards
* `"top"` / `"bottom"` / `"left"` / `"right"`: Explicit placement
#### FABAutoAlign
Alignment of the content along the axis perpendicular to the placement.
* `"auto"`: Resolves from the trigger position — the perpendicular axis is split into thirds mapping to `"start"`, `"center"`, and `"end"`
* `"start"` / `"center"` / `"end"`: Explicit alignment
#### FABItemsAppearance
* `"staggered"`: Items appear one after another, nearest to the trigger first; the order is reversed on close
* `"normal"`: All items appear and disappear together
#### FABRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations (open/close snaps instantly; children can still animate)
* `"disable-all"`: Disable all animations including children (cascades down through `AnimationSettingsProvider`)
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------- | ---------------- | ------- | -------------------------------------------------------------------- |
| `progress` | `AnimationValue` | - | Configuration for the open/close progress |
| `stagger` | `AnimationValue` | - | Configuration for the item stagger (staggered items appearance only) |
##### progress
| prop | type | default | description |
| -------- | -------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | `"spring" \| "timing"` | `"spring"` | Driver animating the progress (0 = idle, 1 = open, 2 = close) |
| `config` | `WithSpringConfig \| WithTimingConfig` | `{ mass: 3, stiffness: 1200, damping: 90 }` | Configuration for the chosen driver. A custom spring config replaces the default; timing configs use Reanimated defaults when omitted |
##### stagger
| prop | type | default | description |
| ------------ | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `itemWindow` | `number` | `0.5` | Fraction of the progress range each item's animation occupies, clamped to `(0, 1]`. Smaller values produce a more sequential stagger; `1` makes all items animate together |
### FAB.Trigger
| prop | type | default | description |
| ----------------------- | ------------------------------------------ | ------- | ------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Content rendered inside the trigger (typically an icon) |
| `isDisabled` | `boolean` | `false` | Whether the trigger is disabled |
| `className` | `string` | - | Additional CSS classes for the trigger container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for the trigger slots (`container`, `contentContainer`) |
| `styles` | `Partial>` | - | Additional styles for the trigger slots |
| `animation` | `FABTriggerAnimation` | - | Animation configuration for the content rotation |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles are active |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### FABTriggerAnimation
| prop | type | default | description |
| -------- | ---------------- | ------- | ------------------------------- |
| `rotate` | `AnimationValue` | - | Rotation of the trigger content |
##### rotate
| prop | type | default | description |
| ------- | -------------------------- | ------------ | -------------------------------------------------------------- |
| `value` | `[number, number, number]` | `[0, 45, 0]` | Rotation degrees for the `[idle, open, close]` progress states |
#### FABTriggerRef
The trigger ref exposes imperative methods:
| method | description |
| --------- | ------------------------------ |
| `open()` | Programmatically open the FAB |
| `close()` | Programmatically close the FAB |
### FAB.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------------- | ------- | ---------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to render within the portal |
| `hostName` | `string` | - | Optional name of the portal host to render into |
| `forceMount` | `true` | - | Force mount the portal regardless of the open state |
| `disableFullWindowOverlay` | `boolean` | `false` | Use a regular View instead of FullWindowOverlay on iOS |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Whether VoiceOver treats the overlay window as a modal container (iOS) |
| `className` | `string` | - | Additional CSS classes for the portal container |
### FAB.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `closeOnPress` | `boolean` | `true` | Whether pressing the overlay closes the FAB |
| `className` | `string` | - | Additional CSS classes for the overlay |
| `variant` | `'default' \| 'blur'` | `'default'` (`'blur'` when the library theme is `glass`) | Overlay variant. `'blur'` renders an animated blur backdrop (iOS only, requires `expo-blur`; falls back to `'default'` otherwise) |
| `blurViewProps` | `FABOverlayBlurViewProps` | - | Props forwarded to the BlurView rendered by the `'blur'` variant; `intensity` acts as the maximum (animated) intensity |
| `animation` | `FABOverlayAnimation` | - | Animation configuration for the overlay opacity |
| `isAnimatedStyleActive` | `boolean` | `true` for the `default` variant, `false` for the `blur` variant | Whether animated styles are active. The blur variant animates blur intensity instead of the overlay opacity |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### FABOverlayAnimation
| prop | type | default | description |
| --------- | ---------------- | ------- | ---------------------- |
| `opacity` | `AnimationValue` | - | Opacity of the overlay |
##### opacity
| prop | type | default | description |
| ------- | -------------------------- | ----------- | ------------------------------------------------------------ |
| `value` | `[number, number, number]` | `[0, 1, 0]` | Opacity values for the `[idle, open, close]` progress states |
### FAB.Content
| prop | type | default | description |
| ------------------------- | ----------------- | ---------------------------------------------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Items to render, typically `FAB.Item` parts |
| `offset` | `number` | `12` | Gap between the trigger and the content in pixels |
| `alignOffset` | `number` | `0` | Offset along the alignment axis in pixels |
| `insets` | `Insets` | `{ top: 12, bottom: 12, left: 12, right: 12 }` | Screen edge insets respected when positioning |
| `avoidCollisions` | `boolean` | `true` | Whether to adjust position to avoid screen edges |
| `disablePositioningStyle` | `boolean` | `false` | Disable the automatic positioning styles |
| `className` | `string` | - | Additional CSS classes for the content container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### FAB.Item
| prop | type | default | description |
| ----------------------- | ------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Item content. Strings/numbers are wrapped in `FAB.ItemLabel` automatically |
| `closeOnPress` | `boolean` | `true` | Whether pressing the item closes the FAB |
| `className` | `string` | - | Additional CSS classes for the item container |
| `background` | `React.ReactNode` | - | Background layer behind the item content. `undefined` renders the theme-aware default; custom node replaces it; `null` removes it |
| `animation` | `FABItemAnimation` | - | Animation configuration for the appearing motion |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles are active |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### FABItemAnimation
| prop | type | default | description |
| ----------- | ---------------- | ------- | ------------------------------------------- |
| `translate` | `AnimationValue` | - | Translation of the item towards the trigger |
| `scale` | `AnimationValue` | - | Scale of the item while appearing |
##### translate
| prop | type | default | description |
| ------- | -------- | ------- | -------------------------------------------------------------- |
| `value` | `number` | `16` | Distance in pixels the item travels from the trigger direction |
##### scale
| prop | type | default | description |
| ------- | ------------------ | ---------- | ----------------------------------------------- |
| `value` | `[number, number]` | `[0.9, 1]` | Scale values for the `[hidden, visible]` states |
### FAB.ItemBackground
Absolute-fill container rendered behind the item content. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### FAB.ItemLabel
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Label text |
| `className` | `string` | - | Additional CSS classes for the label |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useFAB
Hook to access the FAB root context. Must be used within a `FAB` component (works inside `FAB.Portal` too).
```tsx
import { useFAB } from 'heroui-native-pro';
const { isOpen, onOpenChange, placement, align } = useFAB();
```
#### Returns
| property | type | description |
| ----------------- | --------------------------- | -------------------------------------------- |
| `isOpen` | `boolean` | Whether the FAB is currently open |
| `onOpenChange` | `(isOpen: boolean) => void` | Callback to change the open state |
| `triggerPosition` | `LayoutPosition \| null` | Measured trigger position (page coordinates) |
| `contentLayout` | `LayoutRectangle \| null` | Measured content layout |
| `placement` | `FABPlacement` | Resolved content placement |
| `align` | `FABAlign` | Resolved content alignment |
| `nativeID` | `string` | Unique identifier for the FAB instance |
### useFABAnimation
Hook to access the shared open/close progress. Use it to build custom progress-driven parts (e.g. a blur backdrop). Must be used within a `FAB` component (works inside `FAB.Portal` too).
```tsx
import { useFABAnimation } from 'heroui-native-pro';
const { progress } = useFABAnimation();
```
#### Returns
| property | type | description |
| ---------- | --------------------- | --------------------------------------------------------------------------------------------------- |
| `progress` | `SharedValue` | Animated open/close progress (0 = idle/closed, 1 = open, 2 = close target; resets to 0 after close) |
# MorphButton
**Category**: native
**URL**: https://heroui.pro/docs/native/components/morph-button
> A pressable surface that morphs between auto-measured collapsed and expanded content, growing toward one of eight logical directions.
A pressable surface that morphs between auto-measured collapsed and expanded content, growing toward one of eight logical directions.
## Import
```tsx
import { MorphButton } from 'heroui-native-pro';
```
## Anatomy
```tsx
......
```
* **MorphButton**: Root container managing the open state. Its layout footprint always equals the collapsed content, so the expanding surface overflows without shifting surrounding layout. Tapping toggles the open state. Supports controlled and uncontrolled open state.
* **MorphButton.CollapsedContent**: In-flow content shown while collapsed. Its natural size defines the root footprint and the collapsed morph target. Fades and scales out while open.
* **MorphButton.ExpandedContent**: Panel content shown while expanded. Always mounted and measured at its natural size while hidden, so the morph target is known before the first open and the content never reflows mid-morph. Fades and scales in while open.
## Usage
### Basic usage
Both content parts are measured automatically; the surface springs between their sizes when tapped.
```tsx
...
...
```
### Variants
Apply a surface color scheme with the `variant` prop. `primary` is a high-contrast inverted surface for floating buttons over app content; `secondary` reads clearly when the button sits on a `surface` card.
```tsx
......
```
### Directions
Choose which way the surface grows with the `direction` prop. The opposite corner/edge stays pinned to the collapsed button. `start` and `end` are logical, so all eight directions mirror in RTL.
```tsx
.........
```
### Positioning
The root's footprint stays at the collapsed size, so position it like any static element. Give the expanding side enough room; the panel overflows the root without shifting siblings.
```tsx
...
```
### Panel width
The expanded size is measured from the content. For panel layouts, set an explicit width on `ExpandedContent`; height flows from the content and re-measures when it changes.
```tsx
...
...
```
### Controlled
Control the open state externally with `isOpen` and `onOpenChange`. Taps on the surface still request a toggle through `onOpenChange`.
```tsx
const [isOpen, setIsOpen] = useState(false);
...
;
```
### Disabled
Disable the toggle interaction with `isDisabled`.
```tsx
...
```
### Custom morph spring
Customize the width/height spring through the `animation` prop, or pass `"disable-all"` to snap every transition.
```tsx
...
...
```
## Example
```tsx
import { Button, Separator } from 'heroui-native';
import { MorphButton } from 'heroui-native-pro';
import { useState } from 'react';
import { Text, View } from 'react-native';
export default function CartMorphButton() {
const [isOpen, setIsOpen] = useState(false);
return (
2 products in bag
View
Order summary
2 × Gazelle Indoor
$208
Subtotal
$208
);
}
```
## API Reference
### MorphButton
| prop | type | default | description |
| ------------------- | ----------------------------------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound children, typically `MorphButton.CollapsedContent` and `MorphButton.ExpandedContent` |
| `direction` | `'top' \| 'top-end' \| 'end' \| 'bottom-end' \| 'bottom' \| 'bottom-start' \| 'start' \| 'top-start'` | `"top"` | Logical direction the surface grows toward when expanding |
| `variant` | `'primary' \| 'secondary'` | `"primary"` | Visual variant controlling the surface color scheme |
| `isOpen` | `boolean` | - | Whether the button is expanded (controlled mode) |
| `defaultOpen` | `boolean` | `false` | Default expanded state for uncontrolled mode |
| `isDisabled` | `boolean` | `false` | Whether the toggle interaction is disabled |
| `className` | `string` | - | Additional CSS classes for the root container (see animated property notes below) |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `{ container?: ViewStyle; surface?: ViewStyle }` | - | Styles for individual slots |
| `style` | `StyleProp` | - | Style for the root container. The Pressable function form is not supported |
| `onOpenChange` | `(isOpen: boolean) => void` | - | Callback fired when the open state changes |
| `animation` | `MorphButtonRootAnimation` | - | Animation configuration for the root component |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### ElementSlots\
| slot | description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `container` | Root container: the collapsed-size footprint that the consumer positions |
| `surface` | The morphing card. `width`, `height`, `top`, and `start` are animated and cannot be set via className; use the `animation` prop to customize the spring |
#### styles
| slot | type | description |
| ----------- | ----------- | ------------------------------ |
| `container` | `ViewStyle` | Style for the root container |
| `surface` | `ViewStyle` | Style for the morphing surface |
#### MorphButtonRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `morphSpringConfig` | `WithSpringConfig` | `{ damping: 25, stiffness: 300, mass: 0.8, overshootClamping: false, restDisplacementThreshold: 0.01, restSpeedThreshold: 0.01 }` | Spring used when morphing the surface between the measured collapsed and expanded sizes |
### MorphButton.CollapsedContent
In-flow content shown while collapsed. Its natural size defines the root footprint and the collapsed morph target.
| prop | type | default | description |
| ----------------------- | ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Content to display while collapsed |
| `className` | `string` | - | Additional CSS classes. `opacity` and `transform` (scale) are animated and cannot be set via className |
| `animation` | `MorphButtonContentAnimation` | - | Animation configuration for the cross-fade and scale transition |
| `isAnimatedStyleActive` | `boolean` | `true` | When `false`, animated styles are not applied |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### MorphButton.ExpandedContent
Always-mounted panel content measured at its natural size while hidden, so the expanded morph target is known before the first open. Set an explicit width via `className` (e.g. `w-72`) for panel layouts.
| prop | type | default | description |
| ----------------------- | ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Content to display while expanded |
| `className` | `string` | - | Additional CSS classes. `opacity` and `transform` (scale) are animated and cannot be set via className |
| `animation` | `MorphButtonContentAnimation` | - | Animation configuration for the cross-fade and scale transition |
| `isAnimatedStyleActive` | `boolean` | `true` | When `false`, animated styles are not applied |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### MorphButtonContentAnimation
Animation configuration for the content parts. Value tuples read `[closed, open]`. Can be:
* `false` or `"disabled"`: Disable the part's animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| --------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------- |
| `opacity` | `{ value?: [number, number]; timingConfig?: WithTimingConfig }` | `[1, 0]` for `CollapsedContent`, `[0, 1]` for `ExpandedContent`, 200ms timing | Opacity values `[closed, open]` |
| `scale` | `{ value?: [number, number]; timingConfig?: WithTimingConfig }` | `[1, 0.96]` for `CollapsedContent`, `[0.97, 1]` for `ExpandedContent`, 200ms timing | Scale values `[closed, open]` |
## Hooks
### useMorphButton
Hook to access the morph button context. Must be used within a `MorphButton` component.
```tsx
import { useMorphButton } from 'heroui-native-pro';
const {
isOpen,
isOpenValue,
direction,
variant,
isDisabled,
collapsedWidth,
collapsedHeight,
expandedWidth,
expandedHeight,
surfaceWidth,
surfaceHeight,
open,
close,
toggle,
} = useMorphButton();
```
#### Returns
| property | type | description |
| ----------------- | ---------------------- | --------------------------------------------------- |
| `isOpen` | `boolean` | Whether the button is expanded |
| `isOpenValue` | `SharedValue` | UI-thread mirror of `isOpen`, updated during render |
| `direction` | `MorphButtonDirection` | Logical direction the surface grows toward |
| `variant` | `MorphButtonVariant` | Visual variant applied to the surface |
| `isDisabled` | `boolean` | Whether the toggle interaction is disabled |
| `collapsedWidth` | `SharedValue` | Measured natural width of the collapsed content |
| `collapsedHeight` | `SharedValue` | Measured natural height of the collapsed content |
| `expandedWidth` | `SharedValue` | Measured natural width of the expanded content |
| `expandedHeight` | `SharedValue` | Measured natural height of the expanded content |
| `surfaceWidth` | `SharedValue` | Current animated width of the morphing surface |
| `surfaceHeight` | `SharedValue` | Current animated height of the morphing surface |
| `open` | `() => void` | Programmatically expand the button |
| `close` | `() => void` | Programmatically collapse the button |
| `toggle` | `() => void` | Programmatically toggle the open state |
# ProgressButton
**Category**: native
**URL**: https://heroui.pro/docs/native/components/progress-button
> A press-and-hold button that fills a progress overlay to confirm an action.
## Import
```tsx
import { ProgressButton } from 'heroui-native-pro';
```
## Anatomy
```tsx
......
```
* **ProgressButton**: Root container that manages press-and-hold state, fill progress, and completion detection. Supports controlled and uncontrolled completion state with optional auto-reset.
* **ProgressButton.Background**: Optional theme-aware background container rendered behind the button surface. Mounted automatically when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **ProgressButton.Label**: Base text layer always visible beneath the overlay. Captures its own layout position for the MaskLabel counter-animation.
* **ProgressButton.Overlay**: Absolutely positioned layer that sweeps left-to-right via animated translateX with a variant-colored background. Renders children (typically MaskLabel).
* **ProgressButton.MaskLabel**: Inverted-color text inside the Overlay that counter-translates to stay visually aligned with the base Label, creating a color-wipe effect.
## Usage
### Basic usage
Pass a string as children for the simplest usage. The Label, Overlay, and MaskLabel are rendered automatically.
```tsx
Hold to confirm
```
### Compound parts
Use compound parts for full control over the label and overlay content.
```tsx
Hold to confirmHold to confirm
```
### Variants
Apply different color schemes with the `variant` prop.
```tsx
Hold to unlockHold to approveHold to delete
```
### Custom hold duration
Control how long the user must hold with the `holdDuration` prop.
```tsx
Quick holdHold to end run
```
### Auto reset
Automatically reset after completion with `autoReset` and an optional `autoResetDelay`.
```tsx
Hold to confirm
```
### Disabled
Disable the entire hold interaction with `isDisabled`.
```tsx
Hold is disabled
```
### Controlled
Control the completion state externally with `isCompleted` and `onCompleteChange`.
```tsx
const [isCompleted, setIsCompleted] = useState(false);
Hold to verify
;
```
### Render function children
Use a render function to access progress state for custom animated content.
```tsx
{({ progress, isCompleted }) => (
<>
Hold to end runHold to end run
>
)}
```
## Example
```tsx
import { useToast } from 'heroui-native';
import { ProgressButton } from 'heroui-native-pro';
import { useCallback } from 'react';
import { View } from 'react-native';
export default function ConfirmProgressButton() {
const { toast } = useToast();
const handleComplete = useCallback(() => {
toast.show({
variant: 'success',
label: 'Completed',
description: 'Hold action completed!',
duration: 1000,
});
}, [toast]);
return (
Hold to confirmHold to confirm
);
}
```
## API Reference
### ProgressButton
| prop | type | default | description |
| -------------------- | ---------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: ProgressButtonRenderProps) => React.ReactNode)` | - | Children elements or render function receiving hold state |
| `variant` | `'default' \| 'accent' \| 'success' \| 'danger'` | `"default"` | Visual variant controlling color scheme |
| `holdDuration` | `number` | `2000` | Duration in milliseconds the user must hold to complete |
| `isCompleted` | `boolean` | - | Whether the hold action has completed (controlled mode) |
| `isDefaultCompleted` | `boolean` | `false` | Default completed state for uncontrolled mode |
| `isDisabled` | `boolean` | `false` | Whether the component is disabled |
| `autoReset` | `boolean` | `false` | Whether the button automatically resets after completion |
| `autoResetDelay` | `number` | `1000` | Delay in milliseconds before auto-reset occurs |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onCompleteChange` | `(isCompleted: boolean) => void` | - | Callback fired when the completed state changes |
| `onComplete` | `() => void` | - | Callback fired when the hold action completes |
| `onReset` | `() => void` | - | Callback fired when the button resets to start |
| `animation` | `ProgressButtonRootAnimation` | - | Animation configuration for the root component |
| `background` | `React.ReactNode` | - | Background layer behind the button surface. `undefined` renders the theme-aware default; custom node replaces it; `null` removes it |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### ProgressButtonRenderProps
| prop | type | description |
| ------------- | ----------------------- | ------------------------------------------------------------- |
| `progress` | `SharedValue` | Animated progress value (0 = start, 1 = complete) |
| `isCompleted` | `boolean` | Whether the hold action has been completed |
| `trackWidth` | `SharedValue` | Measured width of the root container |
| `textX` | `SharedValue` | Measured x-offset of the Label relative to the root container |
| `textWidth` | `SharedValue` | Measured width of the Label text |
| `isDisabled` | `boolean` | Whether the component is disabled |
| `variant` | `ProgressButtonVariant` | Visual variant applied to the component |
#### ProgressButtonRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------------- | ----------------------------------------------------- | --------------------------------------------------- | --------------------------------------------------------------- |
| `progressSpringConfig` | `WithSpringConfig` | `{ damping: 120, stiffness: 900, mass: 4 }` | Spring configuration for the progress reset and controlled-sync |
| `scale` | `{ value?: number; timingConfig?: WithTimingConfig }` | `{ value: 0.985, timingConfig: { duration: 150 } }` | Scale press-feedback configuration |
### ProgressButton.Background
Absolute-fill container rendered behind the button surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ProgressButton.Overlay
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to display in the overlay (typically MaskLabel) |
| `className` | `string` | - | Additional CSS classes for the overlay container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ProgressButton.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Text content for the label |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### ProgressButton.MaskLabel
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Text content for the mask label (should match Label text) |
| `className` | `string` | - | Additional CSS classes for the mask label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useProgressButton
Hook to access the progress button context. Must be used within a `ProgressButton` component.
```tsx
import { useProgressButton } from 'heroui-native-pro';
const {
progress,
isCompleted,
trackWidth,
textX,
textWidth,
isDisabled,
variant,
reset,
complete,
} = useProgressButton();
```
#### Returns
| property | type | description |
| ------------- | ----------------------- | ------------------------------------------------------------- |
| `progress` | `SharedValue` | Animated progress value (0 = start, 1 = complete) |
| `isCompleted` | `boolean` | Whether the hold action has been completed |
| `trackWidth` | `SharedValue` | Measured width of the root container |
| `textX` | `SharedValue` | Measured x-offset of the Label relative to the root container |
| `textWidth` | `SharedValue` | Measured width of the Label text |
| `isDisabled` | `boolean` | Whether the component is disabled |
| `variant` | `ProgressButtonVariant` | Visual variant applied to the component |
| `reset` | `() => void` | Programmatically reset the progress to 0 |
| `complete` | `() => void` | Programmatically trigger the completion flow |
# SlideButton
**Category**: native
**URL**: https://heroui.pro/docs/native/components/slide-button
> A slide-to-action button that requires a deliberate swipe gesture to confirm an action.
## Import
```tsx
import { SlideButton } from 'heroui-native-pro';
```
## Anatomy
```tsx
......
```
* **SlideButton**: Root container that manages gesture state, progress tracking, and completion detection. Supports controlled and uncontrolled completion state with optional auto-reset.
* **SlideButton.ContainerBackground**: Optional theme-aware background container rendered behind the container surface. Mounted automatically when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **SlideButton.UnderlayContent**: Static content layer beneath the overlay. Right-anchored clip wrapper that reveals content to the right of the thumb as it slides.
* **SlideButton.OverlayContent**: Progress fill layer that clips from left to right as the thumb slides. Uses an overflow-hidden wrapper with animated width tied to thumb position.
* **SlideButton.Thumb**: Draggable handle driven by a pan gesture. Renders a chevron-right icon by default; accepts custom children.
* **SlideButton.Label**: Styled text element that automatically inherits the variant color from context. Use inside UnderlayContent or OverlayContent.
## Usage
### Basic usage
The SlideButton uses compound parts to build a slide-to-confirm interaction.
```tsx
Slide to confirm
```
### Variants
Apply different color schemes with the `variant` prop.
```tsx
Slide to unlockUnlocked!Slide to approveApproved!Slide to deleteDeleted!
```
### Auto reset
Automatically reset the slider after completion with `autoReset` and an optional `autoResetDelay`.
```tsx
Slide to confirmConfirmed!
```
### Disabled
Disable the entire slide interaction with `isDisabled`.
```tsx
Slide is disabled
```
### Controlled
Control the completion state externally with `isCompleted` and `onCompleteChange`.
```tsx
const [isCompleted, setIsCompleted] = useState(false);
Slide to verifyVerified!;
```
### Render function children
Use a render function to access slide state for progress-driven custom content.
```tsx
{({ progress }) => (
<>
Slide to buy · $49.99Purchased!
>
)}
```
## Example
```tsx
import { Spinner } from 'heroui-native';
import { SlideButton, useSlideButton } from 'heroui-native-pro';
import { useCallback, useState } from 'react';
import { View } from 'react-native';
import Animated, {
interpolate,
useAnimatedStyle,
} from 'react-native-reanimated';
const PurchaseOverlayLabel = () => {
const { progress } = useSlideButton();
const rLabelStyle = useAnimatedStyle(() => ({
opacity: interpolate(progress.get(), [0.5, 1], [0, 1]),
}));
return (
Purchased!
);
};
export default function PurchaseSlideButton() {
const [purchased, setPurchased] = useState(false);
const handleReset = useCallback(() => {
setPurchased(false);
}, []);
return (
Slide to buy · $49.99
{purchased ? : null}
);
}
```
## API Reference
### SlideButton
| prop | type | default | description |
| --------------------- | ------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: SlideButtonRenderProps) => React.ReactNode)` | - | Children elements or render function receiving slide state |
| `variant` | `'default' \| 'accent' \| 'success' \| 'danger'` | `"default"` | Visual variant controlling color scheme |
| `isCompleted` | `boolean` | - | Whether the slide action has completed (controlled mode) |
| `isDefaultCompleted` | `boolean` | `false` | Default completed state for uncontrolled mode |
| `isDisabled` | `boolean` | `false` | Whether the component is disabled |
| `completionThreshold` | `number` | `0.85` | Progress threshold (0–1) at which the slide action triggers |
| `autoReset` | `boolean` | `false` | Whether the slider automatically resets after completion |
| `autoResetDelay` | `number` | `1000` | Delay in milliseconds before auto-reset occurs |
| `background` | `React.ReactNode` | - | Background layer behind the container surface. `undefined` renders the theme-aware default; custom node replaces it; `null` removes it |
| `className` | `string` | - | Additional CSS classes for the root container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Styles for individual slots |
| `onCompleteChange` | `(isCompleted: boolean) => void` | - | Callback fired when the completed state changes |
| `onComplete` | `() => void` | - | Callback fired when the slide action completes |
| `onReset` | `() => void` | - | Callback fired when the slider resets to start |
| `animation` | `SlideButtonRootAnimation` | - | Animation configuration for the root component |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SlideButtonRenderProps
| prop | type | description |
| ------------- | --------------------- | --------------------------------------------- |
| `progress` | `SharedValue` | Animated progress value (0 = start, 1 = end) |
| `isCompleted` | `boolean` | Whether the slide action has been completed |
| `trackWidth` | `SharedValue` | Measured width of the root content container |
| `trackHeight` | `SharedValue` | Measured height of the root content container |
| `thumbWidth` | `SharedValue` | Measured width of the thumb element |
| `thumbHeight` | `SharedValue` | Measured height of the thumb element |
| `isDisabled` | `boolean` | Whether the component is disabled |
| `variant` | `SlideButtonVariant` | Visual variant applied to the component |
#### ElementSlots\
| slot | description |
| ------------------ | ----------------------------------------------- |
| `container` | Outer root wrapper with padding and background |
| `contentContainer` | Inner content wrapper that holds compound parts |
#### styles
| slot | type | description |
| ------------------ | ----------- | ----------------------------------- |
| `container` | `ViewStyle` | Style for the outer root wrapper |
| `contentContainer` | `ViewStyle` | Style for the inner content wrapper |
#### SlideButtonRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------- | ------------------ | ------------------------------------------- | ----------------------------------------------------------- |
| `resetSpringConfig` | `WithSpringConfig` | `{ damping: 120, stiffness: 900, mass: 4 }` | Spring configuration for the reset and auto-reset animation |
### SlideButton.ContainerBackground
Absolute-fill container rendered behind the container surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### SlideButton.UnderlayContent
| prop | type | default | description |
| -------------- | ------------------------------------------------------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to display in the underlay |
| `className` | `string` | - | Additional CSS classes for the container slot |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Styles for individual slots |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ------------------ | ----------------------------------------------------------------- |
| `container` | Outer clip wrapper anchored to the right, shrinks as thumb slides |
| `contentContainer` | Inner container at full track width for natural content layout |
#### styles
| slot | type | description |
| ------------------ | ----------- | ------------------------------------------------ |
| `container` | `ViewStyle` | Style for the outer clip wrapper |
| `contentContainer` | `ViewStyle` | Style for the inner full-width content container |
### SlideButton.OverlayContent
| prop | type | default | description |
| -------------- | ------------------------------------------------------------ | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to display in the overlay |
| `className` | `string` | - | Additional CSS classes for the container slot |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Styles for individual slots |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ------------------ | ----------------------------------------------------------------- |
| `container` | Outer clip wrapper that expands from left as the thumb slides |
| `contentContainer` | Inner container at full track width with variant background color |
#### styles
| slot | type | description |
| ------------------ | ----------- | ------------------------------------------------ |
| `container` | `ViewStyle` | Style for the outer clip wrapper |
| `contentContainer` | `ViewStyle` | Style for the inner full-width content container |
### SlideButton.Thumb
| prop | type | default | description |
| ----------------------- | --------------------------- | ------- | -------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content for the thumb. Defaults to a chevron-right icon |
| `className` | `string` | - | Additional CSS classes for the thumb |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `iconProps` | `SlideButtonThumbIconProps` | - | Props forwarded to the default chevron icon. Ignored when `children` is provided |
| `animation` | `SlideButtonThumbAnimation` | - | Animation configuration for the thumb |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SlideButtonThumbIconProps
| prop | type | default | description |
| ------- | -------- | ------- | ---------------------------------------------------------------- |
| `size` | `number` | `20` | Icon size in logical pixels |
| `color` | `string` | - | Icon fill color. When omitted, uses the variant foreground color |
#### SlideButtonThumbAnimation
Animation configuration for the thumb component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------- | ------------------ | ------------------------------------------- | ------------------------------------------------------------ |
| `springConfig` | `WithSpringConfig` | `{ damping: 120, stiffness: 900, mass: 4 }` | Spring configuration for snap-back and snap-to-end animation |
### SlideButton.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Text content for the label |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useSlideButton
Hook to access the slide button context. Must be used within a `SlideButton` component.
```tsx
import { useSlideButton } from 'heroui-native-pro';
const {
progress,
isCompleted,
trackWidth,
trackHeight,
thumbWidth,
thumbHeight,
completionThreshold,
isDisabled,
variant,
reset,
} = useSlideButton();
```
#### Returns
| property | type | description |
| --------------------- | --------------------- | ------------------------------------------------------- |
| `progress` | `SharedValue` | Animated progress value (0 = start, 1 = end) |
| `isCompleted` | `boolean` | Whether the slide action has been completed |
| `trackWidth` | `SharedValue` | Measured width of the root content container |
| `trackHeight` | `SharedValue` | Measured height of the root content container |
| `thumbWidth` | `SharedValue` | Measured width of the thumb element |
| `thumbHeight` | `SharedValue` | Measured height of the thumb element |
| `completionThreshold` | `number` | Progress threshold at which completion triggers |
| `isDisabled` | `boolean` | Whether the component is disabled |
| `variant` | `SlideButtonVariant` | Visual variant applied to the component |
| `reset` | `() => void` | Programmatically reset the slider to the start position |
# SocialAuthButton
**Category**: native
**URL**: https://heroui.pro/docs/native/components/social-auth-button
> A specialised Button that renders a provider-specific icon alongside a label for social login flows.
## Import
```tsx
import { SocialAuthButton } from 'heroui-native-pro';
```
## Usage
### Basic usage
Pass a `provider` to render the corresponding icon and default label.
```tsx
```
### Custom label
Override the default label with the `label` prop.
```tsx
```
### Multiple providers
Stack multiple SocialAuthButtons for a social login form.
```tsx
```
### Side-by-side layout
Display compact icon-only buttons in a row.
```tsx
```
### Custom icon props
Customise the icon size or color with `iconProps`.
```tsx
```
### Custom children
Replace the default icon and label entirely with custom children.
```tsx
Google SSO
```
### All providers
The component supports 11 built-in providers.
```tsx
```
## Example
```tsx
import { Separator } from 'heroui-native';
import { SocialAuthButton } from 'heroui-native-pro';
import { Text, View } from 'react-native';
export default function SocialLoginForm() {
return (
Sign in to Acme Co
Welcome back! Please sign in to continue
or
);
}
```
## API Reference
### SocialAuthButton
Defaults to `variant="outline"`. Extends all [Button](https://heroui.com/docs/native/components/button#button) props.
| prop | type | default | description |
| ----------- | --------------------------- | ------- | --------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom children replacing the default icon and label |
| `provider` | `SocialAuthButtonProvider` | - | OAuth / social-login provider determining which icon and label are rendered |
| `iconProps` | `SocialAuthButtonIconProps` | - | Props forwarded to the provider icon component |
| `label` | `string` | - | Custom label text. When omitted the default provider label is used |
#### SocialAuthButtonProvider
| value | description |
| ------------- | ----------------- |
| `"google"` | Google login |
| `"apple"` | Apple login |
| `"github"` | GitHub login |
| `"facebook"` | Facebook login |
| `"x"` | X (Twitter) login |
| `"microsoft"` | Microsoft login |
| `"discord"` | Discord login |
| `"linkedin"` | LinkedIn login |
| `"slack"` | Slack login |
| `"notion"` | Notion login |
| `"linear"` | Linear login |
#### SocialAuthButtonIconProps
| prop | type | default | description |
| ---------------- | -------- | --------------------- | ------------------------------------------- |
| `size` | `number` | `18` | Size of the icon in pixels |
| `color` | `string` | - | Color of the icon fill |
| `colorClassName` | `string` | `"accent-foreground"` | Uniwind class name mapped to the icon color |
# ToggleButtonGroup
**Category**: native
**URL**: https://heroui.pro/docs/native/components/toggle-button-group
> Groups multiple React Native ToggleButtons into a unified control for single or multiple selection.
## Import
```tsx
import { ToggleButtonGroup } from 'heroui-native-pro';
```
## Usage
### Basic usage
Wrap `ToggleButton`s and assign each a unique `id`. The group manages selection, size, and disabled state via context.
```tsx
.........
```
### Selection mode
Use `selectionMode="single"` to pick one option at a time, or `selectionMode="multiple"` to allow several. Pre-select with `defaultSelectedKeys`.
```tsx
..................
```
### Controlled
Use `selectedKeys` and `onSelectionChange` for controlled state. Selection is exposed as a `Set`.
```tsx
const [selectedKeys, setSelectedKeys] = useState(new Set(['bold']));
.........;
```
### Sizes
Use the `size` prop to propagate a size to every child `ToggleButton`.
```tsx
.........
```
### Orientation
Switch between `horizontal` and `vertical` layouts with the `orientation` prop.
```tsx
......
```
### Detached
Set `isDetached` to render each toggle as a separate rounded button with gaps instead of a single attached segment.
```tsx
.........
```
### Full width
Set `fullWidth` to make the group fill the available width and stretch each toggle equally.
```tsx
...
```
### Disallow empty selection
Set `disallowEmptySelection` to prevent users from clearing the last selected toggle.
```tsx
.........
```
### Disabled
Set `isDisabled` on the group to dim and block presses on all child toggles.
```tsx
...
```
## Example
```tsx
import { ToggleButton, ToggleButtonGroup } from 'heroui-native-pro';
import { useState } from 'react';
import { View } from 'react-native';
import Svg, { Path } from 'react-native-svg';
const BoldIcon = () => (
);
const ItalicIcon = () => (
);
const UnderlineIcon = () => (
);
export default function TextFormattingToolbar() {
const [selectedKeys, setSelectedKeys] = useState(new Set(['bold']));
return (
);
}
```
## API Reference
### ToggleButtonGroup
| prop | type | default | description |
| ------------------------ | -------------------------------- | -------------- | -------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Child `ToggleButton`s. Each must declare a unique `id` |
| `selectionMode` | `ToggleButtonGroupSelectionMode` | `'single'` | Whether one or multiple buttons can be selected |
| `orientation` | `ToggleButtonGroupOrientation` | `'horizontal'` | Layout direction of the group |
| `size` | `ButtonSize` | `'md'` | Size propagated to every child `ToggleButton` |
| `isDetached` | `boolean` | `false` | Whether buttons render as separate rounded items with gaps instead of a single segment |
| `fullWidth` | `boolean` | `false` | Whether the group fills available width and stretches each child to flex equally |
| `selectedKeys` | `Iterable` | - | Controlled selection state. Resolved internally to a `Set` |
| `defaultSelectedKeys` | `Iterable` | - | Default selected keys (uncontrolled) |
| `disallowEmptySelection` | `boolean` | `false` | Prevents clearing all selections |
| `isDisabled` | `boolean` | `false` | Whether the group is disabled. Cascades to every child `ToggleButton` |
| `className` | `string` | - | Additional CSS classes for the group container |
| `onSelectionChange` | `(keys: Set) => void` | - | Handler called when selection changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ToggleButtonGroupSelectionMode
| value | description |
| ------------ | ---------------------------------------------------- |
| `'single'` | Only one toggle can be selected at a time |
| `'multiple'` | Any number of toggles can be selected simultaneously |
#### ToggleButtonGroupOrientation
| value | description |
| -------------- | ------------------------- |
| `'horizontal'` | Toggles flow in a row |
| `'vertical'` | Toggles stack in a column |
## Hooks
### useToggleGroup
Hook to access the ToggleButtonGroup context. Must be used within a `ToggleButtonGroup` component.
```tsx
import { useToggleGroup } from 'heroui-native-pro';
const {
selectedKeys,
onToggle,
size,
orientation,
isDetached,
fullWidth,
isDisabled,
} = useToggleGroup();
```
#### Returns: ToggleButtonGroupContextValue
| property | type | description |
| -------------- | ------------------------------ | ---------------------------------------------------------------------- |
| `selectedKeys` | `Set` | Set of currently selected keys |
| `onToggle` | `(key: string) => void` | Callback invoked when a child toggle is pressed |
| `size` | `ButtonSize` | Size cascaded to children |
| `orientation` | `ToggleButtonGroupOrientation` | Layout orientation of the group |
| `isDetached` | `boolean` | Whether buttons are visually separated with gaps |
| `fullWidth` | `boolean` | Whether the group fills available width (children stretch to `flex-1`) |
| `isDisabled` | `boolean` | Whether the group is disabled |
# ToggleButton
**Category**: native
**URL**: https://heroui.pro/docs/native/components/toggle-button
> A selectable React Native button with controlled state, icons, labels, and ToggleButtonGroup integration.
## Import
```tsx
import { ToggleButton } from 'heroui-native-pro';
```
## Anatomy
```tsx
...
```
* **ToggleButton**: Root pressable wrapping HeroUI Native `Button`. Owns the controllable selection state, applies selected/unselected background styling, and integrates with `ToggleButtonGroup` via context (selection, size, disabled state). Exposes its state to descendants through `useToggleButton`.
* **ToggleButton.Label**: Text label inside the toggle. Wraps `Button.Label` and automatically applies selected/unselected text colors based on the parent toggle state.
## Usage
### Basic usage
Pass a string as children to render a label.
```tsx
Like
```
### Compound label
Use `ToggleButton.Label` for explicit control over the label element.
```tsx
Like
```
### With icon
Compose icons alongside the label inside the toggle.
```tsx
Like
```
### Variants
Switch the resting background style with the `variant` prop. Both variants share the same selected appearance.
```tsx
......
```
### Sizes
Control the button dimensions with the `size` prop.
```tsx
.........
```
### Icon only
Set `isIconOnly` to render a square button with no horizontal padding.
```tsx
```
### Controlled
Use `isSelected` and `onChange` for controlled state, or `defaultSelected` for uncontrolled.
```tsx
...
```
### Custom colors
Override the resting and selected background colors with `unselectedColor` and `selectedColor`. Pass resolved color strings (e.g. from `useThemeColor`).
```tsx
...
```
### Disabled
Set `isDisabled` to dim the button and block presses.
```tsx
......
```
### Inside a group
Place toggles inside a `ToggleButtonGroup` and assign each an `id`. The group manages selection, size, and disabled state.
```tsx
...
...
...
```
### Reading state from descendants
Call `useToggleButton` from a descendant to react to the toggle state without prop drilling.
```tsx
const HeartToggle = () => {
const { isSelected } = useToggleButton();
return ;
};
Like;
```
## Example
```tsx
import { useThemeColor } from 'heroui-native';
import { ToggleButton, useToggleButton } from 'heroui-native-pro';
import { View } from 'react-native';
import Svg, { Path } from 'react-native-svg';
const HeartIcon = ({ color, filled }: { color: string; filled: boolean }) => (
);
const HeartToggleContent = () => {
const { isSelected } = useToggleButton();
const fg = useThemeColor('foreground') as string;
const accentFg = useThemeColor('accent-soft-foreground') as string;
const color = isSelected ? accentFg : fg;
return (
<>
{isSelected ? 'Liked' : 'Like'}
>
);
};
export default function ToggleButtonExample() {
return (
);
}
```
## API Reference
### ToggleButton
`ToggleButton` extends every prop of HeroUI Native [`Button`](https://heroui.com/docs/native/components/button#api-reference) except `variant` (redefined as `ToggleButtonVariant`) and `feedbackVariant` (owned by `ToggleButton`).
| prop | type | default | description |
| ------------------- | ---------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content of the toggle button. Plain strings are rendered as a label |
| `variant` | `ToggleButtonVariant` | `'default'` | Visual style variant |
| `size` | `ButtonSize` | `'md'` | Size of the button. Inherited from the underlying `Button` |
| `isIconOnly` | `boolean` | `false` | Whether the button displays an icon only (square aspect ratio) |
| `id` | `string` | - | Unique identifier. Required when used inside `ToggleButtonGroup` |
| `isSelected` | `boolean` | - | Controlled selected state |
| `defaultSelected` | `boolean` | `false` | Default selected state (uncontrolled) |
| `isDisabled` | `boolean` | `false` | Whether the button is disabled |
| `className` | `string` | - | Additional CSS classes for the button container |
| `selectedColor` | `string` | - | Override background color for the selected state. Defaults to theme `accent-soft` |
| `unselectedColor` | `string` | - | Override background color for the unselected state. Defaults to theme `default` |
| `background` | `React.ReactNode` | - | Background layer behind the toggle surface. `undefined` renders the theme-aware default (scoped as described below); custom node replaces it; `null` removes it |
| `onChange` | `(isSelected: boolean) => void` | - | Handler called when the selection changes |
| `onPress` | `(event: GestureResponderEvent) => void` | - | Press handler invoked after the toggle is applied |
| `animation` | `ButtonAnimation` | - | Animation configuration forwarded to the underlying `Button` (scale press feedback) |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
The `background` prop forwards to the underlying core `Button`, which renders `Button.Background` behind the toggle surface for the `default` variant while unselected (selection paints its own accent-soft tint) when the active library theme registers default background content (e.g. `glass`).
#### ToggleButtonVariant
| type | description |
| ---------------------- | -------------------------------------------------------------------------------------------------------- |
| `'default' \| 'ghost'` | Visual style variants of the toggle. `default` uses an opaque resting background; `ghost` is transparent |
### ToggleButton.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Label text content |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useToggleButton
Hook to access the ToggleButton context. Must be used within a `ToggleButton` component.
```tsx
import { useToggleButton } from 'heroui-native-pro';
const { isSelected, isDisabled, size, variant } = useToggleButton();
```
#### Returns: ToggleButtonContextValue
| property | type | description |
| ------------ | --------------------- | ------------------------------ |
| `isSelected` | `boolean` | Whether the toggle is selected |
| `isDisabled` | `boolean` | Whether the toggle is disabled |
| `size` | `ButtonSize` | Resolved size variant |
| `variant` | `ToggleButtonVariant` | Resolved visual variant |
# AreaChart
**Category**: native
**URL**: https://heroui.pro/docs/native/components/area-chart
> An area chart for visualizing trends, stacked contributions, and confidence bands with Skia-accelerated rendering.
> `AreaChart` is built on top of [victory-native](https://nearform.com/open-source/victory-native/docs/), wrapping its `CartesianChart` and area primitives (`Area`, `StackedArea`, `AreaRange`) with HeroUI Native theming, optional path `animate` from victory-native. See the [victory-native area docs](https://nearform.com/open-source/victory-native/docs/cartesian/area/) for curve types, `connectMissingData`, and path animation behavior.
## Import
```tsx
import { AreaChart, useAreaPath, useStackedAreaPaths } from 'heroui-native-pro';
```
## Anatomy
```tsx
{({ points, chartBounds }) => (
<>
>
)}
```
* **AreaChart**: Root container wrapping `victory-native` `CartesianChart` in a themed outer `View`. Accepts `animation` for cascading `"disable-all"` through `AnimationSettingsProvider`. Forwards `ref` to the underlying chart.
* **AreaChart.Area**: Themed filled area. Uniwind `colorClassName` drives fill color; default `opacity` is `0.2`. Respects cascaded `isAllAnimationsDisabled` for the `animate` prop.
* **AreaChart.StackedArea**: Multiple stacked layers; pass `points` as an array of `PointsArray` in stack order (typically bottom → top) plus matching `colors` and optional `areaOptions` for per-layer gradients.
* **AreaChart.AreaRange**: Band between upper and lower series or mapped `points` with `y` / `y0` bounds.
## Usage
### Basic usage
Provide `data`, `xKey`, `yKeys`, `chartBounds.bottom` as `y0`, then render `AreaChart.Area`.
```tsx
{({ points, chartBounds }) => (
)}
```
### Gradient fill
Nest Skia `LinearGradient` as a child of `AreaChart.Area` for web-style gradient fills.
```tsx
import { LinearGradient, vec } from '@shopify/react-native-skia';
;
```
### Curve type
Use `curveType` on `Area`, `StackedArea`, or `AreaRange`. `monotoneX` matches common web `type="monotone"` charts.
```tsx
```
### Animate data transitions
Pass an `animate` config to `AreaChart.Area` (or `StackedArea` / `AreaRange`) so victory-native's `useAnimatedPath` interpolates the old path into the new one when `points` change — useful for timeframe toggles, filter swaps, or live-updating series.
> Skia can only interpolate (and hence animate) paths with the same number of points. If the number of samples changes between renders, the path snaps instead of animating. Keep each dataset's point count consistent when driving `Area.animate` from variable-length data.
```tsx
const [timeframe, setTimeframe] = useState<'month' | 'year'>('month');
{({ points, chartBounds }) => (
)}
;
```
### Stacked areas
Use `AreaChart.StackedArea` with `points={[points.a, points.b, ...]}` (bottom-most series first) and parallel `colors` in the same stack order. The `areaOptions` callback receives `{ rowIndex, lowestY, highestY }` for per-layer Skia children — typically a `LinearGradient` sized against the layer's stacked extent (use a shallow `highestY - 25` rise on the thick bottom band and a deeper `highestY - 100` rise on thinner upper bands so each layer still shows a clear color falloff). See [StackedArea](https://nearform.com/open-source/victory-native/docs/cartesian/area/stacked-area/) for the upstream reference.
```tsx
import {
DashPathEffect,
LinearGradient,
vec,
} from '@shopify/react-native-skia';
,
},
]}
wrapperClassName="h-56"
>
{({ points, chartBounds }) => (
{
switch (rowIndex) {
case 0:
return {
children: (
),
};
case 1:
return {
children: (
),
};
case 2:
return {
children: (
),
};
default:
return {};
}
}}
/>
)}
;
```
> Stacked layers render cumulatively, but `CartesianChart`'s auto y-domain only considers each `yKey`'s individual maximum. Pass an explicit `domain={{ y: [0, maxStackTotal] }}` (rounded up to a clean tick to leave headroom for `natural`-curve overshoot) so the upper layers stay inside `chartBounds.top`. Pair with `domainPadding={{ top: 0 }}` if you'd rather rely entirely on the explicit domain for top spacing instead of victory-native's default top padding.
### Area range (confidence band)
Use `AreaChart.AreaRange` with `upperPoints` / `lowerPoints` (sourced from the chart's render callback) or a single `points` array typed as `AreaRangePointsArray` (`y` upper, `y0` lower). Pair with `LineChart.Line` rendered after the band to draw a central-tendency line on top. See [AreaRange](https://nearform.com/open-source/victory-native/docs/cartesian/area/area-range/).
```tsx
import { LineChart } from 'heroui-native-pro';
{({ points }) => (
<>
>
)}
;
```
### Outline strokes on top of areas
Pair `AreaChart.Area` (or each band of `AreaChart.StackedArea`) with `LineChart.Line` to render a solid outline along the area's top edge in its matching color. For a `StackedArea`, build the cumulative top-edge polyline of each layer (since `points.` are scaled per series, not pre-stacked) and feed each into a `LineChart.Line`.
### Chart press overlays
Compose `ChartIndicator` and `ChartCrosshair` from `heroui-native-pro` with `useChartPressState` — they are Skia primitives in the same canvas as `AreaChart` children.
## Example
```tsx
import { Card } from 'heroui-native';
import { AreaChart, ChartCrosshair, ChartIndicator } from 'heroui-native-pro';
import { View } from 'react-native';
import { useChartPressState } from 'victory-native';
const REVENUE_DATA = [
{ month: 'Jan', revenue: 4200 },
{ month: 'Feb', revenue: 5800 },
// ...
];
export default function MonthlyRevenueArea() {
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0 },
});
return (
Monthly Revenue
{({ points, chartBounds }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
);
}
```
## API Reference
### AreaChart
| prop | type | default | description |
| ------------------ | ------------------------ | ------- | ------------------------------------------------------------------------------ |
| `wrapperClassName` | `string` | - | Tailwind classes for the outer `View` (supply height, e.g. `h-48`) |
| `animation` | `AreaChartRootAnimation` | - | Root animation config; `"disable-all"` cascades to all animated compound parts |
Extends [victory-native `CartesianChart`](https://nearform.com/open-source/victory-native/docs/cartesian/cartesian-chart/) — all chart props (`data`, `xKey`, `yKeys`, `xAxis`, `yAxis`, `chartPressState`, `ref`, …) are supported.
### AreaChart.Area
| prop | type | default | description |
| ---------------- | --------------------- | ------------------ | ---------------------------------------------------------------------------------------------- |
| `colorClassName` | `string` | `'accent-chart-3'` | Uniwind accent class for fill |
| `opacity` | `number` | `0.2` | Default fill opacity |
| `animate` | `PathAnimationConfig` | - | Path interpolation when points change; dropped when cascaded `isAllAnimationsDisabled` is true |
Extends [victory-native `Area`](https://nearform.com/open-source/victory-native/docs/cartesian/area/) — `points`, `y0`, `curveType`, `connectMissingData`, `children`, and Skia paint props flow through.
### AreaChart.StackedArea
Extends [victory-native `StackedArea`](https://nearform.com/open-source/victory-native/docs/cartesian/area/stacked-area/). `animate` is dropped when cascaded `isAllAnimationsDisabled` is true.
### AreaChart.AreaRange
Extends [victory-native `AreaRange`](https://nearform.com/open-source/victory-native/docs/cartesian/area/area-range/). `animate` respects the same cascade.
## Hooks
### useAreaPath
Returns a Skia `SkPath` for a single filled area — see [useAreaPath](https://nearform.com/open-source/victory-native/docs/cartesian/area/use-area-path/).
### useStackedAreaPaths
Returns per-layer path objects for custom stacked rendering — see [useStackedAreaPaths](https://nearform.com/open-source/victory-native/docs/cartesian/area/use-stacked-area-paths/).
# BarChart
**Category**: native
**URL**: https://heroui.pro/docs/native/components/bar-chart
> A bar chart for visualizing categorical data with single, grouped, and stacked column layouts.
> `BarChart` is built on top of [victory-native](https://nearform.com/open-source/victory-native/docs/), wrapping its `CartesianChart`, `Bar`, `BarGroup`, and `StackedBar` primitives with HeroUI Native theming and animation cascading. For full context on chart props, gestures, scales, and rendering internals, read the [victory-native docs](https://nearform.com/open-source/victory-native/docs/) alongside this page.
## Import
```tsx
import { BarChart } from 'heroui-native-pro';
```
## Anatomy
```tsx
{({ points, chartBounds }) => (
<>
>
)}
```
* **BarChart**: Root container that wraps `victory-native` `CartesianChart` in a themed outer `View`. Applies a default `domainPadding` and accepts an `animation` prop for cascading `"disable-all"` to animated compound parts through `AnimationSettingsProvider`. Forwards `ref` to the underlying chart.
* **BarChart.Bar**: Themed Skia bar series for a single `yKey`. Uniwind-wrapped Skia primitive whose fill is driven by `colorClassName`. Defaults `roundedCorners` to small top radii and respects cascaded `isAllAnimationsDisabled`: when disabled, the `animate` prop is dropped.
* **BarChart.BarGroup**: Clustered (side-by-side) bars for multiple series per category. Reads child `BarChart.BarGroupItem` props, computes per-series Skia paths with `useBarGroupPaths`, and renders themed bars while reporting the resolved `barWidth`, `groupWidth`, and `gapWidth` through `onBarSizeChange`.
* **BarChart.BarGroupItem**: One series inside a `BarChart.BarGroup`. Same Uniwind `colorClassName` and `animate` cascade as `BarChart.Bar`.
* **BarChart.StackedBar**: Stacked columns built from an ordered array of `PointsArray` entries (bottom of the stack first). Gates `animate` through the same animation cascade.
## Usage
### Basic usage
Provide `data`, `xKey`, and `yKeys`, then render a `BarChart.Bar` for the series in the children render function. Pass `chartBounds` from the render args so victory-native can size the columns.
```tsx
{({ points, chartBounds }) => (
)}
```
### Custom bar width and rounded corners
Tune column thickness with `barWidth` and round individual corners through `roundedCorners`.
```tsx
```
### Grouped bars
Render `BarChart.BarGroup` with one `BarChart.BarGroupItem` per series. Pass distinct `colorClassName` values so the clustered columns are visually separable.
```tsx
{({ points, chartBounds }) => (
)}
```
### Stacked bars
Compute the per-row stack maximum and pass it as the `y` domain so each stack matches the data scale. `points` for `BarChart.StackedBar` is an ordered array — index `0` is the bottom of the stack.
```tsx
{({ points, chartBounds }) => (
)}
```
### Round only the top of a stack
Use `barOptions` to receive each segment's position in the stack. Apply `roundedCorners` only when `isTop` is true so the cap of the column is rounded while inner segments stay square.
```tsx
({
roundedCorners: isTop
? { topLeft: 10, topRight: 10, bottomLeft: 0, bottomRight: 0 }
: undefined,
})}
/>
```
### Animate data transitions
Pass an `animate` config to `BarChart.Bar` (or `BarChart.BarGroupItem` / `BarChart.StackedBar`) to interpolate the Skia bar paths when the underlying `points` change. Useful for timeframe toggles, filter swaps, or live-updating series.
> Skia can only interpolate paths with the same number of points. If the number of bars changes between renders, the path snaps instead of animating. Keep each dataset's row count consistent when driving `animate` from variable-length data.
> To get an enter animation on first paint, render the chart with placeholder values close to `0` (same row count as the real data) and swap to the actual values once you're ready — for example on screen focus, after a fetch resolves, or behind a loading flag. Because the bar count stays the same, each column animates from the near-zero baseline up to its real height.
```tsx
```
### Categorical X-axis tick values
When the X field is a `string`, victory-native's default tick generator can return fractional positions and render the literal `"undefined"` for labels at those ticks. Generate integer indices for `xAxis.tickValues` to keep labels aligned.
```tsx
const tickValues = Array.from({ length: DATA.length }, (_, index) => index);
{({ points, chartBounds }) => (
)}
;
```
### Gradient fill with useBarPath
`BarChart.Bar` does not pass children through to its Skia `Path`. To paint a shader on a column, build the bar path with `useBarPath` and render a Skia `` directly with a `` child.
```tsx
import { LinearGradient, Path, vec } from '@shopify/react-native-skia';
import { useBarPath } from 'heroui-native-pro';
function GradientBars({ points, chartBounds }) {
const { path } = useBarPath(
points,
chartBounds,
0.25,
{ topLeft: 8, topRight: 8, bottomLeft: 0, bottomRight: 0 },
16
);
const cx = (chartBounds.left + chartBounds.right) / 2;
return (
);
}
```
## Example
```tsx
import { Card } from 'heroui-native';
import { BarChart } from 'heroui-native-pro';
import { View } from 'react-native';
const SALES_DATA = [
{ month: 'Jan', sales: 18 },
{ month: 'Feb', sales: 32 },
{ month: 'Mar', sales: 28 },
{ month: 'Apr', sales: 45 },
{ month: 'May', sales: 38 },
{ month: 'Jun', sales: 52 },
{ month: 'Jul', sales: 42 },
{ month: 'Aug', sales: 55 },
{ month: 'Sep', sales: 48 },
{ month: 'Oct', sales: 60 },
{ month: 'Nov', sales: 53 },
{ month: 'Dec', sales: 58 },
];
const categoryAxisTickValues = (count: number): number[] =>
Array.from({ length: count }, (_, index) => index);
export default function DailySalesChart() {
return (
Daily sales
Units sold per month
`${Math.round(value)}` },
]}
wrapperClassName="h-[220px]"
>
{({ points, chartBounds }) => (
)}
);
}
```
## API Reference
### BarChart
| prop | type | default | description |
| ------------------ | ----------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `wrapperClassName` | `string` | - | Additional Tailwind classes for the outer `View` that wraps the chart. Required for chart height (e.g. `h-52`) |
| `domainPadding` | `SidedNumber` | `{ top: 8, bottom: 8, left: 12, right: 12 }` | Padding (in pixels) added inside the chart bounds. A caller-supplied value replaces the default object in full |
| `animation` | `BarChartRootAnimation` | - | Animation configuration for the chart root. Accepts `"disable-all"` to cascade animation skipping to all animated compound parts |
Extends [victory-native `CartesianChart`](https://nearform.com/open-source/victory-native/docs/cartesian/cartesian-chart) — all `CartesianChart` props (`data`, `xKey`, `yKeys`, `children`, `xAxis`, `yAxis`, `domain`, `chartPressState`, `axisOptions`, `ref`, etc.) are supported in addition to the BarChart-specific props above.
#### BarChartRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including animated compound parts
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration with a `state` field for the same disabling semantics
The root does not drive any of its own animated styles; its sole animation responsibility is cascading `isAllAnimationsDisabled` to compound parts that do animate.
### BarChart.Bar
| prop | type | default | description |
| ---------------- | --------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `colorClassName` | `string` | `'accent-chart-3'` | Uniwind `accent-*` class for the bar fill. Resolves to the Skia `color` prop; pass `color` directly to bypass Uniwind |
| `roundedCorners` | `RoundedCorners` | `{ topLeft: 4, topRight: 4 }` | Per-corner radii for the bar path |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `points` change. Dropped when cascaded `isAllAnimationsDisabled` is true |
| `className` | `string` | - | Uniwind class forwarded to the underlying Skia `Bar` |
Extends [victory-native `Bar`](https://nearform.com/open-source/victory-native/docs/cartesian/bar/) — only props affected by the HeroUI Native wrapper are listed above; refer to the upstream docs for `points`, `chartBounds`, `barWidth`, `innerPadding`, and Skia paint props.
### BarChart.BarGroup
| prop | type | default | description |
| --------------------- | ----------- | ------- | ------------------------------------------------------------ |
| `children` | `ReactNode` | - | One or more `BarChart.BarGroupItem` children, one per series |
| `betweenGroupPadding` | `number` | `0.25` | Fractional padding between adjacent groups (`0`–`1`) |
| `withinGroupPadding` | `number` | `0.25` | Fractional padding between bars inside a group (`0`–`1`) |
Mirrors the layout contract of [victory-native `BarGroup`](https://nearform.com/open-source/victory-native/docs/cartesian/bar/bar-group) using `useBarGroupPaths` under the hood. The bar paths and themed fills come from rendering one styled `BarGroup.Bar` per child, which is why children must be `BarChart.BarGroupItem` elements. Refer to the upstream docs for `chartBounds`, `barWidth`, `barCount`, `roundedCorners`, and `onBarSizeChange`.
### BarChart.BarGroupItem
| prop | type | default | description |
| ---------------- | --------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `colorClassName` | `string` | `'accent-chart-3'` | Uniwind `accent-*` class for the bar fill. Resolves to the Skia `color` prop; pass `color` directly to bypass Uniwind |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `points` change. Dropped when cascaded `isAllAnimationsDisabled` is true |
| `className` | `string` | - | Uniwind class forwarded to the underlying Skia `Bar` |
Extends victory-native `BarGroup.Bar` — only props affected by the HeroUI Native wrapper are listed above. The `chartBounds`, `barWidth`, and `roundedCorners` props are computed by the parent `BarChart.BarGroup` and should not be supplied directly on the item; refer to the upstream `BarGroup.Bar` docs for any other passthrough props.
### BarChart.StackedBar
| prop | type | default | description |
| --------- | --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `points` change. Dropped when cascaded `isAllAnimationsDisabled` is true |
Extends [victory-native `StackedBar`](https://nearform.com/open-source/victory-native/docs/cartesian/bar/stacked-bar) — only props affected by the HeroUI Native wrapper are listed above; refer to the upstream docs for `points`, `chartBounds`, `colors`, `barWidth`, `innerPadding`, and `barOptions`. For correct stacking, callers typically set `domain={{ y: [0, maxStackSum] }}` on the root when auto-domain is insufficient.
## Hooks
### useBarPath
Re-exported from `victory-native` so consumers can build custom Skia `` renderings on the same `PointsArray` `BarChart.Bar` consumes — useful for layering shaders, gradients, or custom strokes on top of standard bars.
```tsx
import { Path } from '@shopify/react-native-skia';
import { useBarPath } from 'heroui-native-pro';
function CustomBar({ points, chartBounds }) {
const { path } = useBarPath(points, chartBounds, 0.2, {
topLeft: 4,
topRight: 4,
});
return ;
}
```
See the full reference in the [victory-native `useBarPath` docs](https://nearform.com/open-source/victory-native/docs/cartesian/bar/use-bar-path).
### useBarGroupPaths
Re-exported from `victory-native` so consumers can compute clustered-group bar paths outside of `BarChart.BarGroup` — useful when rendering custom Skia primitives per series while keeping the same layout the themed group uses internally.
```tsx
import { useBarGroupPaths } from 'heroui-native-pro';
const { paths, barWidth, groupWidth, gapWidth } = useBarGroupPaths(
[points.online, points.retail, points.direct],
chartBounds,
0.25,
0.25
);
```
See the full reference in the [victory-native `useBarGroupPaths` docs](https://nearform.com/open-source/victory-native/docs/cartesian/bar/use-bar-group-paths).
### useStackedBarPaths
Re-exported from `victory-native` so consumers can compute stacked-segment paths outside of `BarChart.StackedBar` — useful when each segment needs its own shader or post-processing while staying aligned with the standard stack layout.
```tsx
import { useStackedBarPaths } from 'heroui-native-pro';
const { paths } = useStackedBarPaths(
[points.starter, points.pro, points.enterprise],
chartBounds,
0.2
);
```
See the full reference in the [victory-native `useStackedBarPaths` docs](https://nearform.com/open-source/victory-native/docs/cartesian/bar/use-stacked-bar-paths).
# ChartCrosshair
**Category**: native
**URL**: https://heroui.pro/docs/native/components/chart-crosshair
> A vertical rule and tooltip overlay that highlight the pressed point on a chart.
## Import
```tsx
import { ChartCrosshair } from 'heroui-native-pro';
```
## Anatomy
```tsx
{({ chartBounds }) => (
)}
```
* **ChartCrosshair**: Skia vertical rule (`Path`) drawn from `(x, top)` to `(x, bottom)`. Renders dashed by default via `DashPathEffect`; pass `variant="solid"` for an unbroken stroke. Render inside the chart canvas with `useChartPressState`-driven shared values.
* **ChartCrosshair.Anchor**: Relatively positioned React Native `View` that wraps the chart and the sibling RN value overlay. Supplies crosshair context (`x`, `isActive`, `chartBounds`) so descendants can position themselves on the same coordinate system as the Skia rule.
* **ChartCrosshair.Value**: Absolutely positioned animated overlay that hosts the tooltip pill. Measures its own width to center on `x`, clamps to `chartBounds`, and tracks press activity via `isActive` opacity. Must be a descendant of `ChartCrosshair.Anchor`.
* **ChartCrosshair.ValueBackground**: Optional theme-aware background container rendered behind the value pill surface. Mounted automatically for the `default` variant when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **ChartCrosshair.ValueLabel**: Read-only animated label backed by an internal `ReText` (read-only Reanimated `TextInput`). Reads the `value` shared string from `ChartCrosshair.Value` context, so the label updates on the UI thread without React renders.
## Usage
> When wrapping a chart with `ChartCrosshair.Anchor`, the chart's `wrapperClassName` must not contain padding (e.g. `p-*`, `px-*`, `py-*`). The anchor reads `chartBounds` in the same coordinate space as the Skia canvas, so any padding on the wrapper offsets the chart relative to the anchor and breaks centering / clamping of `ChartCrosshair.Value`. Apply spacing on a parent container instead.
### Basic usage
Render `ChartCrosshair` inside the chart's render callback. Drive `x` from `useChartPressState`, and pass `top` / `bottom` from `chartBounds`. Gate visibility with `isActive` from the same hook.
```tsx
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0 },
});
{({ points, chartBounds }) => (
<>
{isActive ? (
) : null}
>
)}
;
```
### Variants
Switch the rule style with the `variant` prop. `dashed` attaches a themed `DashPathEffect`; `solid` renders an unbroken stroke.
```tsx
```
### Custom dash pattern
Override the dashed pattern by nesting your own Skia `DashPathEffect` as a child.
```tsx
import { DashPathEffect } from '@shopify/react-native-skia';
;
```
### Custom color and stroke width
Pass `color` and `strokeWidth` directly to override the themed defaults.
```tsx
```
### Tooltip overlay
Wrap the chart and the value pill in `ChartCrosshair.Anchor`, then render `ChartCrosshair.Value` as a sibling **outside** the chart. Build the label string on the UI thread with `useDerivedValue` and pass it as `value`. Mirror Skia `chartBounds` from `onChartBoundsChange` so the overlay clamps correctly near the plot edges.
> Keep `wrapperClassName` free of padding on the wrapped chart — the anchor measures positions in the chart's native coordinate space.
```tsx
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0 },
});
const [chartBounds, setChartBounds] = useState(null);
const labelText = useDerivedValue(() => `${state.y.revenue.value.get()}`);
{({ points, chartBounds: bounds }) => (
<>
{isActive ? (
) : null}
>
)}
;
```
### Value variants
Switch the pill surface with the `variant` prop on `ChartCrosshair.Value`. `default` renders a filled rounded pill; `ghost` renders a transparent label-only surface.
```tsx
```
### Value placement
Position the pill above (`top`) or below (`bottom`) the anchor with the `placement` prop.
```tsx
```
### Value offset
Nudge the overlay without fighting the animated style. `offset` accepts CSS-like additive `top` / `bottom` / `left` / `right` pixels.
> The animated style owns vertical edge (`top` / `bottom`) and horizontal `transform.translateX`, so do not override those via `className` or `styles.container`. Use `offset` instead.
```tsx
```
### Custom value content
Compose extra content (e.g. icons, prefixes) by passing children. The default label is replaced by the children — render `ChartCrosshair.ValueLabel` explicitly to keep the animated text alongside your custom nodes.
```tsx
```
## Example
```tsx
import { Card } from 'heroui-native';
import { ChartCrosshair, ChartIndicator, LineChart } from 'heroui-native-pro';
import { useState } from 'react';
import { View } from 'react-native';
import { useDerivedValue } from 'react-native-reanimated';
import type { ChartBounds } from 'victory-native';
import { useChartPressState } from 'victory-native';
const REVENUE_DATA = [
{ month: 'Jan', revenue: 4200 },
{ month: 'Feb', revenue: 5800 },
{ month: 'Mar', revenue: 4900 },
{ month: 'Apr', revenue: 7200 },
{ month: 'May', revenue: 6100 },
{ month: 'Jun', revenue: 8400 },
{ month: 'Jul', revenue: 7800 },
{ month: 'Aug', revenue: 9200 },
{ month: 'Sep', revenue: 8600 },
{ month: 'Oct', revenue: 10200 },
{ month: 'Nov', revenue: 9800 },
{ month: 'Dec', revenue: 11500 },
];
const formatThousandsCurrency = (value: number): string =>
`$${(value / 1000).toFixed(0)}k`;
export default function CrosshairChartExample() {
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0 },
});
const [chartBounds, setChartBounds] = useState(null);
const tooltipLabel = useDerivedValue(() => `${state.y.revenue.value.get()}`);
return (
Monthly Revenue
{({ points, chartBounds: bounds }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
);
}
```
## API Reference
### ChartCrosshair
| prop | type | default | description |
| ------------------ | ----------------------------- | ---------- | -------------------------------------------------------------------------------------------- |
| `x` | `SharedValue` | - | Horizontal position of the rule, typically `state.x.position` from `useChartPressState` |
| `top` | `number` | - | Top y-coordinate (Skia canvas pixels) where the rule starts. Typically `chartBounds.top` |
| `bottom` | `number` | - | Bottom y-coordinate (Skia canvas pixels) where the rule ends. Typically `chartBounds.bottom` |
| `variant` | `ChartCrosshairVariant` | `'dashed'` | Visual style of the rule. `'dashed'` attaches a themed `DashPathEffect` |
| `color` | `Color` | - | Skia stroke color. Falls back to a themed muted color when omitted |
| `strokeWidth` | `number` | `1` | Stroke width in logical pixels |
| `children` | `ReactNode` | - | Optional Skia children (e.g. a custom `DashPathEffect`) to nest inside the `Path` |
| `...SkiaPathProps` | `ComponentProps` | - | Remaining Skia `Path` props. `path`, `style`, `start`, and `end` are controlled internally |
#### ChartCrosshairVariant
| type | description |
| --------------------- | ------------------------------------------------------------------ |
| `'solid' \| 'dashed'` | Visual style of the rule. `'dashed'` attaches a themed dash effect |
### ChartCrosshair.Anchor
| prop | type | default | description |
| -------------- | ---------------------- | ------- | ---------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | The chart and sibling `ChartCrosshair.Value` overlay |
| `chartBounds` | `ChartBounds` | - | Plot bounds mirrored from the chart's `onChartBoundsChange`. Enables horizontal clamping |
| `isActive` | `SharedValue` | - | Press activity shared value used to drive overlay opacity |
| `x` | `SharedValue` | - | Horizontal crosshair position in chart space (`state.x.position`) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ChartCrosshair.Value
| prop | type | default | description |
| -------------- | ------------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `value` | `SharedValue` | - | Shared label string forwarded to `ChartCrosshair.ValueLabel` via context |
| `variant` | `ChartCrosshairValueVariant` | `'default'` | Visual variant for the pill container |
| `placement` | `ChartCrosshairValuePlacement` | `'top'` | Whether the pill sits above (`'top'`) or below (`'bottom'`) the anchor |
| `offset` | `ChartCrosshairValueOffset` | - | Pixel offsets applied on top of the auto-centering animated style. CSS-like additive `top`/`bottom`/`left`/`right` |
| `className` | `string` | - | Additional classes merged onto the `container` slot |
| `classNames` | `ElementSlots` | - | Additional classes per slot (`container`, `label`) |
| `styles` | `ChartCrosshairValueStyles` | - | Inline style overrides per slot |
| `children` | `ReactNode` | - | Optional content rendered after (or replacing) the default label |
| `background` | `ReactNode` | - | Background layer behind the value pill surface. `undefined` renders the theme-aware default for the `default` variant; custom node replaces it; `null` removes it |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported except `children` (typed above) |
#### ChartCrosshairValueVariant
| type | description |
| ---------------------- | ---------------------------------------------------------- |
| `'default' \| 'ghost'` | Pill surface variant. `'ghost'` removes background and pad |
#### ChartCrosshairValuePlacement
| type | description |
| ------------------- | ---------------------------------------------------- |
| `'top' \| 'bottom'` | Vertical placement of the overlay relative to anchor |
#### ChartCrosshairValueOffset
Pixel offsets applied to the animated overlay on top of auto-centering. Values are CSS-like additive — use this prop instead of overriding `top` / `bottom` / `transform` via `className` or `styles`, since those properties are owned by the animated style and will be overwritten on every frame.
| prop | type | default | description |
| -------- | -------- | ------- | ------------------------------------------------------------------------------- |
| `top` | `number` | `0` | Vertical inset that pushes the overlay down (positive) |
| `bottom` | `number` | `0` | Vertical inset that pushes the overlay up (positive) |
| `left` | `number` | `0` | Horizontal pixel offset added to `translateX`. Positive values push right |
| `right` | `number` | `0` | Horizontal pixel offset subtracted from `translateX`. Positive values push left |
#### ElementSlots\
| slot | description |
| ----------- | ---------------------------------------------------------------------------- |
| `container` | Outer animated `Animated.View` that hosts the pill |
| `label` | Default label slot classes merged onto the inner `ChartCrosshair.ValueLabel` |
#### styles
| slot | type | description |
| ----------- | ----------- | -------------------------------------------------- |
| `container` | `ViewStyle` | Style for the animated overlay `Animated.View` |
| `label` | `TextStyle` | Style for the read-only animated `TextInput` label |
### ChartCrosshair.ValueBackground
Absolute-fill container rendered behind the value pill surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `children` | `ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ChartCrosshair.ValueLabel
Reads the animated string from `ChartCrosshair.Value` context — the `value` is **never** a prop. Extra props forward to the underlying `ReText` / `TextInput`.
| prop | type | default | description |
| ------------------- | -------------------------------------------------------------------------- | ------- | ----------------------------------------------------------- |
| `className` | `string` | - | Additional classes merged with the default label typography |
| `style` | `AnimatedProps['style']` | - | Animated style for the `TextInput` / `ReText` surface |
| `...TextInputProps` | `Omit` | - | All standard `TextInput` props except the excluded ones |
## Hooks
### useChartCrosshairAnchor
Hook to access the `ChartCrosshair.Anchor` context. Must be used within a `ChartCrosshair.Anchor` subtree.
```tsx
import { useChartCrosshairAnchor } from 'heroui-native-pro';
const { x, isActive, chartBounds } = useChartCrosshairAnchor();
```
#### Returns: ChartCrosshairAnchorContextValue
| property | type | description |
| ------------- | ---------------------- | --------------------------------------------------------- |
| `x` | `SharedValue` | Horizontal crosshair position in chart space |
| `isActive` | `SharedValue` | Press activity shared value (overlay opacity tracks this) |
| `chartBounds` | `ChartBounds` | Latest Skia plot bounds, when available |
### useChartCrosshairValue
Hook to access the `ChartCrosshair.Value` context. Must be used within a `ChartCrosshair.Value` subtree.
```tsx
import { useChartCrosshairValue } from 'heroui-native-pro';
const { value } = useChartCrosshairValue();
```
#### Returns: ChartCrosshairValueContextValue
| property | type | description |
| -------- | --------------------- | ------------------------------------------------ |
| `value` | `SharedValue` | Animated label string from the root `value` prop |
# ChartIndicator
**Category**: native
**URL**: https://heroui.pro/docs/native/components/chart-indicator
> A themed Skia double-dot (outer halo + inner) marker that follows the pressed point on a chart.
## Import
```tsx
import { ChartIndicator } from 'heroui-native-pro';
```
## Usage
### Basic usage
Render `ChartIndicator` inside the chart's render callback. Drive `x` and `y` from `useChartPressState` and gate visibility with `isActive` from the same hook.
```tsx
const { state, isActive } = useChartPressState({
x: 0,
y: { value: 0 },
});
{({ points }) => (
<>
{isActive ? (
) : null}
>
)}
;
```
### Custom radius
Override the default radii with `innerRadius` (default `5`) and `outerRadius` (default `7`).
```tsx
```
### Custom colors
Override the themed defaults with `innerColor` and `outerColor`. The outer halo defaults to the `--color-background` token; the inner dot defaults to `--color-chart-3`.
```tsx
```
### Forwarding Skia props
Any extra Skia `Circle` props are forwarded to the **inner** circle only. Use this to add effects such as a stroke or opacity.
```tsx
```
## Example
```tsx
import { Card } from 'heroui-native';
import { ChartIndicator, LineChart } from 'heroui-native-pro';
import { View } from 'react-native';
import { useChartPressState } from 'victory-native';
const REVENUE_DATA = [
{ month: 'Jan', revenue: 4200 },
{ month: 'Feb', revenue: 5800 },
{ month: 'Mar', revenue: 4900 },
{ month: 'Apr', revenue: 7200 },
{ month: 'May', revenue: 6100 },
{ month: 'Jun', revenue: 8400 },
{ month: 'Jul', revenue: 7800 },
{ month: 'Aug', revenue: 9200 },
{ month: 'Sep', revenue: 8600 },
{ month: 'Oct', revenue: 10200 },
{ month: 'Nov', revenue: 9800 },
{ month: 'Dec', revenue: 11500 },
];
export default function ChartIndicatorExample() {
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0 },
});
return (
Monthly Revenue
{({ points }) => (
<>
{isActive ? (
) : null}
>
)}
);
}
```
## API Reference
### ChartIndicator
| prop | type | default | description |
| -------------------- | ------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `x` | `SharedValue` | - | Horizontal position of the indicator center, typically `state.x.position` |
| `y` | `SharedValue` | - | Vertical position of the indicator center, typically `state.y[yKey].position` |
| `innerRadius` | `number` | `5` | Radius of the inner (front) circle in logical pixels |
| `outerRadius` | `number` | `7` | Radius of the outer (halo) circle in logical pixels |
| `outerColor` | `Color` | `--color-background` | Fill color for the outer halo. Falls back to the themed background CSS variable |
| `innerColor` | `Color` | `--color-chart-3` | Fill color for the inner dot. Falls back to the themed chart-3 CSS variable |
| `...SkiaCircleProps` | `ComponentProps` | - | Remaining Skia `Circle` props forwarded to the inner circle. `cx`, `cy`, `c`, `r`, and `color` are controlled internally |
# ChartTooltip
**Category**: native
**URL**: https://heroui.pro/docs/native/components/chart-tooltip
> A composable React Native tooltip for chart data points with customizable indicators, labels, and value formatters.
> **Cartesian charts only.** `ChartTooltip` is driven by victory-native's Cartesian press state (`useChartPressState` → `state.x.position`, `state.y[yKey].position`, `state.matchedIndex`, `state.isActive`) and clamps against `chartBounds` from `onChartBoundsChange`. It works with `LineChart`, `BarChart`, `AreaChart`, and `ComposedChart`. It does **not** support polar charts (`PieChart` / `PolarChart`), which expose no press positions, `matchedIndex`, or `chartBounds` — there is no anchor coordinate for the card to follow. For pie/donut selection labels, render your own label inside the `PieChart.Pie` render callback instead.
## Import
```tsx
import { ChartTooltip, useChartTooltipAnchor } from 'heroui-native-pro';
```
## Anatomy
```tsx
...
```
* **ChartTooltip**: Floating card root. Measures itself, follows the `(x, y)` anchor, clamps inside `chartBounds` on both axes, and fades with press activity when `isVisible="auto"`. Must be a descendant of `ChartTooltip.Anchor`.
* **ChartTooltip.Anchor**: Relatively positioned React Native `View` that wraps the chart and sibling tooltip. Supplies press coordinates, `activeIndex`, and plot bounds via context.
* **ChartTooltip.Header**: Optional title row (typically the X-axis category label).
* **ChartTooltip.Item**: One series row (`flex-row` with indicator, label, and value).
* **ChartTooltip.Indicator**: Color swatch beside a series name.
* **ChartTooltip.Label**: Series name within an item row.
* **ChartTooltip.Value**: Formatted data value within an item row.
## Usage
> When wrapping a chart with `ChartTooltip.Anchor`, the chart's `wrapperClassName` must not contain padding (e.g. `p-*`, `px-*`, `py-*`). The anchor reads `chartBounds` in the same coordinate space as the Skia canvas, so any padding on the wrapper offsets the chart relative to the anchor and breaks positioning / clamping of `ChartTooltip`. Apply spacing on a parent container instead.
### Basic usage
Pass `state.isActive`, `state.matchedIndex`, and press positions to `ChartTooltip.Anchor`. Read the bridged `activeIndex` from `useChartTooltipAnchor()` inside a descendant to map tooltip rows from your data array. Visibility fades automatically with press activity (`isVisible="auto"` is the default).
```tsx
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0, orders: 0 },
});
const [chartBounds, setChartBounds] = useState(null);
function OrdersTooltip() {
const { activeIndex } = useChartTooltipAnchor();
const activeRow = activeIndex != null ? DATA[activeIndex] : null;
return (
{activeRow?.month ?? ''}Revenue
${activeRow?.revenue.toLocaleString() ?? ''}
Orders
{activeRow?.orders.toLocaleString() ?? ''}
);
}
{({ points, chartBounds: bounds }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
;
```
### Visibility
Control tooltip visibility with `isVisible` on `ChartTooltip`:
* `'auto'` (default) — fades with press activity from the anchor.
* `true` — always visible.
* `false` — not rendered.
```tsx
......
```
### Indicator variants
Set the swatch shape on each `ChartTooltip.Indicator` with `variant`. `'dot'` renders a circular marker; `'line'` renders a narrow vertical pill suited to line-series tooltips.
```tsx
Revenue$4,200
```
### Placement
Position the tooltip above (`top`) or below (`bottom`) the `(x, y)` anchor with the `placement` prop on `ChartTooltip`.
```tsx
......
```
### Offset and clamping
Nudge the overlay without fighting the animated style. `offset` accepts CSS-like additive `top` / `bottom` / `left` / `right` pixels. When `chartBounds` is supplied on `ChartTooltip.Anchor`, `ChartTooltip` clamps its position so the card stays inside the plot box on both axes.
> The animated style owns `transform` and `opacity`, so do not override those via `className`. Use `offset` instead.
```tsx
...
```
### Motion animation
The card springs to follow the press indicator by default (`withSpring`, no config). Control the motion type and config with the `animation` prop on `ChartTooltip`. Pass `false` / `"disabled"` to snap instantly.
```tsx
.........
```
Disable all tooltip animations (including descendants) by cascading from the anchor:
```tsx
...
```
## Accessibility
Sensible accessibility defaults are applied; every default is overridable via the matching prop.
* **ChartTooltip** is a polite live region (`accessibilityRole="summary"`, `accessibilityLiveRegion="polite"`) so screen readers announce the active values as the press selection changes.
* **ChartTooltip.Item** is an accessible group (`accessible`, `accessibilityRole="text"`) so its label and value are read together as one node (e.g. "Revenue, $4,200").
* **ChartTooltip.Indicator** is decorative and hidden from screen readers (`accessible={false}`, `aria-hidden`, `accessibilityElementsHidden`, `importantForAccessibility="no-hide-descendants"`).
* **ChartTooltip.Header** uses `accessibilityRole="header"`; **ChartTooltip.Label** / **ChartTooltip.Value** use `accessibilityRole="text"`.
```tsx
MarRevenue$4,200
```
## Example
```tsx
import { Card } from 'heroui-native';
import {
ChartCrosshair,
ChartIndicator,
ChartTooltip,
ComposedChart,
useChartTooltipAnchor,
} from 'heroui-native-pro';
import { useState } from 'react';
import { View } from 'react-native';
import type { ChartBounds } from 'victory-native';
import { useChartPressState } from 'victory-native';
const DATA = [
{ month: 'Jan', revenue: 4200, orders: 320 },
{ month: 'Feb', revenue: 5800, orders: 450 },
];
function TooltipBody() {
const { activeIndex } = useChartTooltipAnchor();
const activeRow = activeIndex != null ? DATA[activeIndex] : null;
return (
{activeRow?.month ?? ''}Revenue
${activeRow?.revenue.toLocaleString() ?? ''}
Orders
{activeRow?.orders.toLocaleString() ?? ''}
);
}
export default function TooltipChartExample() {
const [chartBounds, setChartBounds] = useState(null);
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0, orders: 0 },
});
return (
{({ points, chartBounds: bounds }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
);
}
```
## API Reference
### ChartTooltip
| prop | type | default | description |
| -------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | Card content: `Header` and `Item` rows |
| `animation` | `ChartTooltipRootAnimation` | spring | Motion animation while tracking the press indicator. `withSpring` (no config) by default |
| `isVisible` | `ChartTooltipVisibility` | `'auto'` | `'auto'` fades with press activity; `true` always visible; `false` unmounts |
| `placement` | `ChartTooltipPlacement` | `'top'` | Whether the tooltip sits above or below the `(x, y)` anchor |
| `gap` | `number` | `12` | Gap in logical pixels between the anchor and the tooltip edge |
| `offset` | `ChartTooltipOffset` | - | Pixel offsets applied on top of auto-positioning. CSS-like additive `top`/`bottom`/`left`/`right` |
| `className` | `string` | - | Additional classes merged onto the animated card container |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported except `children` (typed above) |
#### ChartTooltipVisibility
| type | description |
| ------------------- | --------------------------------------------------- |
| `'auto' \| boolean` | `'auto'` fades with anchor press activity (default) |
#### ChartTooltipPlacement
| type | description |
| ------------------- | ---------------------------------------------------- |
| `'top' \| 'bottom'` | Vertical placement of the overlay relative to anchor |
#### ChartTooltipRootAnimationConfig
Discriminated on `type`. Both branches accept the matching Reanimated config fields.
| type | description |
| --------------------------------------- | ----------------------------------------------------- |
| `{ type: 'spring' } & WithSpringConfig` | Physically-based motion (default; no config required) |
| `{ type: 'timing' } & WithTimingConfig` | Duration-based slide |
#### ChartTooltipOffset
| prop | type | default | description |
| -------- | -------- | ------- | ------------------------------------------------------------------------------- |
| `top` | `number` | `0` | Vertical inset that pushes the overlay down (positive) |
| `bottom` | `number` | `0` | Vertical inset that pushes the overlay up (positive) |
| `left` | `number` | `0` | Horizontal pixel offset added to `translateX`. Positive values push right |
| `right` | `number` | `0` | Horizontal pixel offset subtracted from `translateX`. Positive values push left |
#### ChartTooltipIndicatorVariant
| type | description |
| ----------------- | ------------------------------------------------------------ |
| `'dot' \| 'line'` | Swatch shape. `'dot'` is circular; `'line'` is a narrow pill |
### ChartTooltip.Anchor
| prop | type | default | description |
| -------------- | --------------------------------- | ------- | ------------------------------------------------------------------------------------------------ |
| `children` | `ReactNode` | - | The chart and sibling `ChartTooltip` overlay |
| `animation` | `ChartTooltipAnchorRootAnimation` | - | Root-level cascade. Pass `"disable-all"` to disable animations on `ChartTooltip` and descendants |
| `chartBounds` | `ChartBounds \| null` | - | Plot bounds from `onChartBoundsChange`. Enables 2D clamping on `ChartTooltip` |
| `isActive` | `SharedValue` | - | Press activity shared value (`state.isActive`) |
| `matchedIndex` | `SharedValue` | - | Matched datum index (`state.matchedIndex`); bridged to JS as `activeIndex` in context |
| `x` | `SharedValue` | - | Horizontal press position in chart space (`state.x.position`) |
| `y` | `SharedValue` | - | Vertical press position in chart space (`state.y[yKey].position`) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ChartTooltip.Header
| prop | type | default | description |
| -------------- | ----------- | ------- | ----------------------------------------- |
| `children` | `ReactNode` | - | Title text (e.g. X-axis category) |
| `className` | `string` | - | Additional classes merged onto the header |
| `...TextProps` | `TextProps` | - | All standard React Native Text props |
### ChartTooltip.Item
| prop | type | default | description |
| -------------- | ----------- | ------- | ------------------------------------------- |
| `children` | `ReactNode` | - | Indicator, label, and value for one series |
| `className` | `string` | - | Additional classes merged onto the item row |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props |
### ChartTooltip.Indicator
| prop | type | default | description |
| -------------- | ------------------------------ | ------- | -------------------------------------------------- |
| `color` | `string` | - | Fill color for the swatch |
| `variant` | `ChartTooltipIndicatorVariant` | `'dot'` | Swatch shape (`'dot'` or `'line'`) |
| `className` | `string` | - | Additional classes merged onto the indicator |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ChartTooltip.Label
| prop | type | default | description |
| -------------- | ----------- | ------- | ---------------------------------------- |
| `children` | `ReactNode` | - | Series name |
| `className` | `string` | - | Additional classes merged onto the label |
| `...TextProps` | `TextProps` | - | All standard React Native Text props |
### ChartTooltip.Value
| prop | type | default | description |
| -------------- | ----------- | ------- | ---------------------------------------- |
| `children` | `ReactNode` | - | Formatted data value |
| `className` | `string` | - | Additional classes merged onto the value |
| `...TextProps` | `TextProps` | - | All standard React Native Text props |
### useChartTooltipAnchor
Returns the anchor context from the nearest `ChartTooltip.Anchor`. Throws when used outside an anchor (strict context).
| field | type | description |
| ------------- | ---------------------- | ------------------------------------------------------------- |
| `activeIndex` | `number \| null` | Active datum index when press is active; `null` when inactive |
| `chartBounds` | `ChartBounds \| null` | Latest plot bounds from the chart |
| `isActive` | `SharedValue` | Press activity shared value |
| `x` | `SharedValue` | Horizontal press position |
| `y` | `SharedValue` | Vertical press position |
# ComposedChart
**Category**: native
**URL**: https://heroui.pro/docs/native/components/composed-chart
> A React Native composed chart that combines bar, line, and area series for multi-metric mobile dashboards.
> `ComposedChart` is built on top of [victory-native](https://nearform.com/open-source/victory-native/docs/), wrapping its `CartesianChart` with HeroUI Native theming and animation cascading. Its series parts reuse the themed Skia implementations from `BarChart`, `LineChart`, and `AreaChart`. For full context on chart props, gestures, scales, and rendering internals, read the [victory-native docs](https://nearform.com/open-source/victory-native/docs/) alongside this page.
## Import
```tsx
import { ComposedChart } from 'heroui-native-pro';
```
## Anatomy
```tsx
{({ points, chartBounds }) => (
<>
>
)}
```
* **ComposedChart**: Root container that wraps `victory-native` `CartesianChart` in a themed outer `View`. Applies a bar-friendly default `domainPadding` and accepts an `animation` prop for cascading `"disable-all"` to animated compound parts through `AnimationSettingsProvider`. Forwards `ref` to the underlying chart.
* **ComposedChart.Bar**: Themed Skia bar series for a single `yKey` (from `BarChart.Bar`). Fill is driven by `colorClassName`; defaults `roundedCorners` to small top radii.
* **ComposedChart.BarGroup**: Clustered (side-by-side) bars for multiple series per category (from `BarChart.BarGroup`). Reads child `ComposedChart.BarGroupItem` props to compute per-series paths.
* **ComposedChart.BarGroupItem**: One series inside a `ComposedChart.BarGroup` (from `BarChart.BarGroupItem`). Same `colorClassName` and `animate` cascade as `ComposedChart.Bar`.
* **ComposedChart.StackedBar**: Stacked columns built from an ordered array of `PointsArray` entries (from `BarChart.StackedBar`). Index `0` is the bottom of the stack.
* **ComposedChart.Line**: Themed Skia line series (from `LineChart.Line`). Stroke color is driven by `colorClassName`.
* **ComposedChart.AnimatedLine**: Replayable draw-on line with a `resetKey` trigger (from `LineChart.AnimatedLine`).
* **ComposedChart.Area**: Themed Skia area series (from `AreaChart.Area`). Fill color is driven by `colorClassName`.
* **ComposedChart.StackedArea**: Stacked area layers from an ordered `points` array (from `AreaChart.StackedArea`).
* **ComposedChart.AreaRange**: Shaded band between an upper and a lower series (from `AreaChart.AreaRange`).
## Usage
### Bar and line on dual Y-axes
Assign each metric to a `yAxis` entry via `yKeys`, and set `axisSide: 'right'` plus a per-axis `domain` on the secondary axis when scales differ.
```tsx
`$${(v / 1000).toFixed(0)}k` },
{ yKeys: ['orders'], axisSide: 'right' },
]}
wrapperClassName="h-52"
>
{({ points, chartBounds }) => (
<>
>
)}
```
### Stacked bar with overlaid line
Stack multiple bar series with `ComposedChart.StackedBar` and overlay a line on a separate right axis. Give each axis its own `domain` so the stacks and the line read on their own scales.
```tsx
{({ points, chartBounds }) => (
<>
>
)}
```
### Area with a dashed reference line
Fill an area to the baseline with `y0={chartBounds.bottom}` and draw a comparison line on top. Nest a Skia `LinearGradient` inside `ComposedChart.Area` for a gradient fill and a `DashPathEffect` inside `ComposedChart.Line` for a dashed stroke.
```tsx
{({ points, chartBounds }) => (
<>
>
)}
```
### Press interaction
Wire `chartPressState` from `useChartPressState` on the root, then render `ChartCrosshair` and `ChartIndicator` inside the render callback. Wrap the chart and the `ChartCrosshair.Value` overlay in `ChartCrosshair.Anchor`.
```tsx
{({ points, chartBounds: bounds }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
```
## Example
```tsx
import { Card } from 'heroui-native';
import { ComposedChart } from 'heroui-native-pro';
import { View } from 'react-native';
const REVENUE_ORDERS_DATA = [
{ month: 'Jan', orders: 320, revenue: 4200 },
{ month: 'Feb', orders: 450, revenue: 5800 },
{ month: 'Mar', orders: 380, revenue: 4900 },
{ month: 'Apr', orders: 520, revenue: 7200 },
{ month: 'May', orders: 480, revenue: 6100 },
{ month: 'Jun', orders: 600, revenue: 8400 },
];
const categoryAxisTickValues = (count: number): number[] =>
Array.from({ length: count }, (_, index) => index);
export default function RevenueOrdersChart() {
return (
Revenue & Orders
`$${(value / 1000).toFixed(0)}k`,
},
{ yKeys: ['orders'], axisSide: 'right' },
]}
wrapperClassName="h-[220px]"
>
{({ points, chartBounds }) => (
<>
>
)}
);
}
```
## API Reference
### ComposedChart
| prop | type | default | description |
| ------------------ | ---------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `wrapperClassName` | `string` | - | Additional Tailwind classes for the outer `View` that wraps the chart. Required for chart height (e.g. `h-52`) |
| `domainPadding` | `SidedNumber` | `{ top: 8, bottom: 8, left: 12, right: 12 }` | Padding (in pixels) added inside the chart bounds. A caller-supplied value replaces the default object in full |
| `animation` | `ComposedChartRootAnimation` | - | Animation configuration for the chart root. Accepts `"disable-all"` to cascade animation skipping to all animated compound parts |
Extends [victory-native `CartesianChart`](https://nearform.com/open-source/victory-native/docs/cartesian/cartesian-chart) — all `CartesianChart` props (`data`, `xKey`, `yKeys`, `children`, `xAxis`, `yAxis`, `domain`, `chartPressState`, `axisOptions`, `ref`, etc.) are supported in addition to the ComposedChart-specific props above. Per-axis `yKeys` and `domain` on `yAxis` entries drive independent dual-axis scaling.
#### ComposedChartRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including animated compound parts
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration with a `state` field for the same disabling semantics
The root does not drive any of its own animated styles; its sole animation responsibility is cascading `isAllAnimationsDisabled` to compound parts that do animate.
### ComposedChart.Bar
| prop | type | default | description |
| ---------------- | --------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `colorClassName` | `string` | `'accent-chart-3'` | Uniwind `accent-*` class for the bar fill. Resolves to the Skia `color` prop; pass `color` directly to bypass Uniwind |
| `roundedCorners` | `RoundedCorners` | `{ topLeft: 4, topRight: 4 }` | Per-corner radii for the bar path |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `points` change. Dropped when cascaded `isAllAnimationsDisabled` is true |
| `className` | `string` | - | Uniwind class forwarded to the underlying Skia `Bar` |
Reuses `BarChart.Bar`. Extends [victory-native `Bar`](https://nearform.com/open-source/victory-native/docs/cartesian/bar/) — refer to the upstream docs for `points`, `chartBounds`, `barWidth`, `innerPadding`, and Skia paint props.
### ComposedChart.BarGroup
| prop | type | default | description |
| --------------------- | ----------- | ------- | ----------------------------------------------------------------- |
| `children` | `ReactNode` | - | One or more `ComposedChart.BarGroupItem` children, one per series |
| `betweenGroupPadding` | `number` | `0.25` | Fractional padding between adjacent groups (`0`–`1`) |
| `withinGroupPadding` | `number` | `0.25` | Fractional padding between bars inside a group (`0`–`1`) |
Reuses `BarChart.BarGroup`. Mirrors the layout contract of [victory-native `BarGroup`](https://nearform.com/open-source/victory-native/docs/cartesian/bar/bar-group) using `useBarGroupPaths` under the hood. Refer to the upstream docs for `chartBounds`, `barWidth`, `barCount`, `roundedCorners`, and `onBarSizeChange`.
### ComposedChart.BarGroupItem
| prop | type | default | description |
| ---------------- | --------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `colorClassName` | `string` | `'accent-chart-3'` | Uniwind `accent-*` class for the bar fill. Resolves to the Skia `color` prop; pass `color` directly to bypass Uniwind |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `points` change. Dropped when cascaded `isAllAnimationsDisabled` is true |
| `className` | `string` | - | Uniwind class forwarded to the underlying Skia `Bar` |
Reuses `BarChart.BarGroupItem`. The `chartBounds`, `barWidth`, and `roundedCorners` props are computed by the parent `ComposedChart.BarGroup` and should not be supplied directly on the item.
### ComposedChart.StackedBar
| prop | type | default | description |
| --------- | --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `points` change. Dropped when cascaded `isAllAnimationsDisabled` is true |
Reuses `BarChart.StackedBar`. Extends [victory-native `StackedBar`](https://nearform.com/open-source/victory-native/docs/cartesian/bar/stacked-bar) — refer to the upstream docs for `points`, `chartBounds`, `colors`, `barWidth`, `innerPadding`, and `barOptions`. For correct stacking, set a `domain` on the owning `yAxis` entry when auto-domain is insufficient.
### ComposedChart.Line
| prop | type | default | description |
| ---------------- | --------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `colorClassName` | `string` | `'accent-chart-3'` | Uniwind `accent-*` class for the stroke color |
| `strokeWidth` | `number` | `2` | Stroke width in logical pixels |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `points` change. Dropped when cascaded `isAllAnimationsDisabled` is true |
| `className` | `string` | - | Uniwind class forwarded to the underlying Skia `Line` |
Reuses `LineChart.Line`. Extends [victory-native `Line`](https://nearform.com/open-source/victory-native/docs/cartesian/line/) — `points`, `curveType`, `connectMissingData`, `children`, and all Skia paint props flow through. Pass `color` directly to bypass Uniwind and supply a raw Skia color.
### ComposedChart.AnimatedLine
| prop | type | default | description |
| -------------------- | -------------------------------------------------------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `points` | `PointsArray` | - | Points for a single series, sourced from `CartesianChart`'s render callback |
| `curveType` | `CurveType` | `'linear'` | d3-shape curve factory name |
| `connectMissingData` | `boolean` | `false` | Whether to visually connect across `null` / missing y values |
| `color` | `Color` | theme `chart-3` | Skia stroke color. Falls back to the `--color-chart-3` CSS variable when omitted |
| `strokeWidth` | `number` | `2` | Stroke width in logical pixels |
| `animation` | `LineChartAnimatedLineAnimation` | `{ type: 'timing', duration: 700 }` | Reanimated config for the draw-on animation. Captured via a ref so inline objects on every render do not re-trigger replay |
| `resetKey` | `number \| string \| boolean \| null` | - | Opaque identity value that re-triggers the draw-on animation when changed. Behaves like a React `key` for the animation only |
| `...SkiaPathProps` | `Omit, 'path' \| 'style' \| 'start' \| 'end'>` | - | Remaining Skia `Path` props. `path`, `style`, `start`, and `end` are controlled internally |
Reuses `LineChart.AnimatedLine`.
### ComposedChart.Area
| prop | type | default | description |
| ---------------- | --------------------- | ------------------ | ---------------------------------------------------------------------------------------------- |
| `colorClassName` | `string` | `'accent-chart-3'` | Uniwind accent class for the fill |
| `opacity` | `number` | `0.2` | Default fill opacity |
| `animate` | `PathAnimationConfig` | - | Path interpolation when points change; dropped when cascaded `isAllAnimationsDisabled` is true |
Reuses `AreaChart.Area`. Extends [victory-native `Area`](https://nearform.com/open-source/victory-native/docs/cartesian/area/) — `points`, `y0`, `curveType`, `connectMissingData`, `children`, and Skia paint props flow through.
### ComposedChart.StackedArea
Reuses `AreaChart.StackedArea`. Extends [victory-native `StackedArea`](https://nearform.com/open-source/victory-native/docs/cartesian/area/stacked-area/). `animate` is dropped when cascaded `isAllAnimationsDisabled` is true.
### ComposedChart.AreaRange
Reuses `AreaChart.AreaRange`. Extends [victory-native `AreaRange`](https://nearform.com/open-source/victory-native/docs/cartesian/area/area-range/). `animate` respects the same cascade.
# LineChart
**Category**: native
**URL**: https://heroui.pro/docs/native/components/line-chart
> A React Native line chart for mobile trend data with multi-series, sparkline, and custom tooltip support.
> `LineChart` is built on top of [victory-native](https://nearform.com/open-source/victory-native/docs/), wrapping its `CartesianChart` and `Line` primitives with HeroUI Native theming and draw-on animations. For press overlays (indicator dot, vertical crosshair), import `ChartIndicator` and `ChartCrosshair` from `heroui-native-pro` — they are Skia primitives in the same canvas as `LineChart` children. For a **React Native** value label centered on the crosshair x-coordinate, wrap the chart and the label in `ChartCrosshair.Anchor` and render `ChartCrosshair.Value` as a sibling **outside** the chart (see below) — do not rely on `renderOutside` for RN labels (`Text` / `TextInput`), since that hook still renders inside the Skia canvas. For full context on chart props, gestures, scales, and rendering internals, read the [victory-native docs](https://nearform.com/open-source/victory-native/docs/) alongside this page.
## Import
```tsx
import { ChartCrosshair, ChartIndicator, LineChart } from 'heroui-native-pro';
```
## Anatomy
```tsx
{({ points, chartBounds }) => (
<>
{/* Optional: useChartPressState + ChartIndicator / ChartCrosshair; RN labels via ChartCrosshair.Value */}
>
)}
```
* **LineChart**: Root container that wraps `victory-native` `CartesianChart` in a themed outer `View`. Accepts an `animation` prop for cascading `"disable-all"` to animated compound parts through `AnimationSettingsProvider`. Forwards `ref` to the underlying chart for access to the Skia canvas and press-actions handle.
* **LineChart.Line**: Themed static line series. Renders a Uniwind-wrapped Skia line path whose stroke color is driven by `colorClassName`. Respects cascaded `isAllAnimationsDisabled`: when disabled, the `animate` prop is dropped so data-change path interpolation is skipped.
* **LineChart.AnimatedLine**: Replayable draw-on line. Sweeps the Skia `Path.end` trim from `animation.progress[0]` to `animation.progress[1]` (default `[0, 1]`) on mount and whenever `resetKey` identity changes, using the provided timing or spring config.
* **ChartIndicator** / **ChartCrosshair** (separate exports): Themed Skia press overlays. Use with `useChartPressState` and `chartPressState` on the chart root; see [Chart gestures](https://nearform.com/open-source/victory-native/docs/cartesian/chart-gestures) in the victory-native docs.
* **ChartCrosshair.Anchor** / **ChartCrosshair.Value** / **ChartCrosshair.ValueLabel**: read-only Reanimated `TextInput` overlay whose string is driven by `SharedValue` (`value` on `ChartCrosshair.Value`), plus a **relative** wrapper (`ChartCrosshair.Anchor`) that supplies crosshair context (`x`, `isActive`, `chartBounds`). **`ChartCrosshair.Value` requires `ChartCrosshair.Anchor`.** The value root measures its own width and centers on `x`; use `onChartBoundsChange` to mirror Skia `chartBounds` — not `renderOutside` for the label.
## Usage
### Basic usage
Provide `data`, `xKey`, and `yKeys`, then render a `LineChart.Line` for each series in the children render function.
```tsx
{({ points }) => }
```
### Multiple series
Render a separate `LineChart.Line` per key. Pass a distinct `colorClassName` to each so the curves are visually separable.
```tsx
{({ points }) => (
<>
>
)}
```
### Curve type
Switch the line interpolation with `curveType`. `natural` produces a smoother cubic spline, `linear` draws straight segments.
```tsx
```
### Dashed line
Nest a Skia `DashPathEffect` as a child of `LineChart.Line` for dashed strokes.
```tsx
import { DashPathEffect } from '@shopify/react-native-skia';
;
```
### Custom axis font
Axis tick labels default to platform-native sans-serifs — **Helvetica** on iOS and **sans-serif** on Android, both at `fontSize: 11`. Override with any Skia `SkFont` via the `xAxis.font` / `yAxis[i].font` props.
Build the font from a bundled `.ttf` asset with `useFont`:
```tsx
import { useFont } from '@shopify/react-native-skia';
import InterMedium from './assets/fonts/Inter-Medium.ttf';
function MyChart() {
const font = useFont(InterMedium, 12);
return (
{({ points }) => }
);
}
```
### Draw-on animation
Use `LineChart.AnimatedLine` with an `animation` config to play a draw-on reveal.
```tsx
{({ points }) => (
)}
```
### Animate data transitions
Pass an `animate` config to `LineChart.Line` to morph between datasets when the underlying `points` change. Each time `data` updates, victory-native's `useAnimatedPath` interpolates the old path into the new one using the provided Reanimated config — useful for timeframe toggles, filter swaps, or live-updating series.
> Skia can only interpolate (and hence animate) paths with the same number of points. If the number of samples changes between renders (e.g. swapping datasets of different lengths), the path snaps instead of animating. Keep each dataset's point count consistent when driving `Line.animate` from variable-length data.
```tsx
const [timeframe, setTimeframe] = useState<'month' | 'year'>('month');
{({ points }) => (
)}
;
```
### Replay animation on demand
Bump `resetKey` with any fresh value (typically a counter) to replay the draw-on animation.
```tsx
const [replayCount, setReplayCount] = useState(0);
{({ points }) => (
)}
;
```
### Custom sweep range
Use `animation.progress` to customize the `[from, to]` range bound to the `Path.end` trim. `[1, 0]` reverses the sweep for a fade-out.
```tsx
```
### Chart-press tooltip
Wire `useChartPressState` to the chart via `chartPressState` and render `ChartIndicator` gated by `isActive`.
```tsx
import { ChartIndicator, LineChart } from 'heroui-native-pro';
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0 },
});
{({ points }) => (
<>
{isActive ? (
) : null}
>
)}
;
```
### Chart-press crosshair
Pair with `ChartCrosshair` for the classic hover-guide look. `top` and `bottom` come from `chartBounds`. The `variant` prop defaults to `"dashed"`; pass `"solid"` for an unbroken rule.
```tsx
import { ChartCrosshair } from 'heroui-native-pro';
{
isActive ? (
<>
>
) : null;
}
```
### ChartCrosshair.Value (RN overlay label)
Skia cannot host React Native text views for this use case. `ChartCrosshair.Value` uses a **read-only** `TextInput` (via internal `ReText`) so the label string can update on the UI thread via `useAnimatedProps`. Wrap the chart **and** the label in `ChartCrosshair.Anchor` (relative positioning + crosshair context); **`ChartCrosshair.Value` must be a descendant.** Pass `chartBounds` from `onChartBoundsChange`, and the same `state.x.position` and `state.isActive` shared values you use for the Skia `ChartCrosshair` rule / `ChartIndicator`. The value root measures its width (`onLayout`) to center on the crosshair; apply minimum width with Uniwind classes (e.g. `min-w-20 px-2`).
Build the string on the UI thread with `useDerivedValue` and pass it to **`value`** on `ChartCrosshair.Value`.
```tsx
import { ChartCrosshair, ChartIndicator, LineChart } from 'heroui-native-pro';
import { useState } from 'react';
import { View } from 'react-native';
import { useDerivedValue } from 'react-native-reanimated';
import type { ChartBounds } from 'victory-native';
import { useChartPressState } from 'victory-native';
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0 },
});
const [chartBounds, setChartBounds] = useState(null);
const labelText = useDerivedValue(
() => `$${(state.y.revenue.value.get() / 1000).toFixed(1)}k`
);
return (
{({ points, chartBounds: b }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
);
```
## Example
```tsx
import { Card } from 'heroui-native';
import { ChartCrosshair, ChartIndicator, LineChart } from 'heroui-native-pro';
import { View } from 'react-native';
import { useChartPressState } from 'victory-native';
const REVENUE_DATA = [
{ month: 'Jan', revenue: 4200 },
{ month: 'Feb', revenue: 5800 },
{ month: 'Mar', revenue: 4900 },
{ month: 'Apr', revenue: 7200 },
{ month: 'May', revenue: 6100 },
{ month: 'Jun', revenue: 8400 },
{ month: 'Jul', revenue: 7800 },
{ month: 'Aug', revenue: 9200 },
{ month: 'Sep', revenue: 8600 },
{ month: 'Oct', revenue: 10200 },
{ month: 'Nov', revenue: 9800 },
{ month: 'Dec', revenue: 11500 },
];
const formatThousandsCurrency = (value: number): string =>
`$${(value / 1000).toFixed(0)}k`;
export default function MonthlyRevenueChart() {
const { state, isActive } = useChartPressState({
x: '' as string,
y: { revenue: 0 },
});
return (
Monthly Revenue
{({ points, chartBounds }) => (
<>
{isActive ? (
<>
>
) : null}
>
)}
);
}
```
## API Reference
### LineChart
| prop | type | default | description |
| ------------------ | ------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `wrapperClassName` | `string` | - | Additional Tailwind classes for the outer `View` that wraps the chart. Required for chart height (e.g. `h-48`) |
| `animation` | `LineChartRootAnimation` | - | Animation configuration for the chart root. Accepts `"disable-all"` to cascade animation skipping to all animated compound parts |
Extends [victory-native `CartesianChart`](https://nearform.com/open-source/victory-native/docs/cartesian/cartesian-chart) — all `CartesianChart` props (`data`, `xKey`, `yKeys`, `children`, `xAxis`, `yAxis`, `domainPadding`, `chartPressState`, `axisOptions`, `ref`, etc.) are supported in addition to the LineChart-specific props above.
#### LineChartRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including animated compound parts
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration with a `state` field for the same disabling semantics
The root does not drive any of its own animated styles; its sole animation responsibility is cascading `isAllAnimationsDisabled` to compound parts that do animate.
### LineChart.Line
| prop | type | default | description |
| ---------------- | --------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `colorClassName` | `string` | `'accent-chart-3'` | Uniwind `accent-*` class for the stroke color |
| `strokeWidth` | `number` | `2` | Stroke width in logical pixels |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `points` change. Dropped when cascaded `isAllAnimationsDisabled` is true |
| `className` | `string` | - | Uniwind class forwarded to the underlying Skia `Line` |
Extends [victory-native `Line`](https://nearform.com/open-source/victory-native/docs/cartesian/line/) — `points`, `curveType`, `connectMissingData`, `children`, and all Skia paint props (`color`, `opacity`, `blendMode`, `strokeJoin`, `strokeCap`, `strokeMiter`, `antiAlias`, `start`, `end`) flow through. `colorClassName` is added by Uniwind's `withUniwind` wrapper and resolves to the Skia `color` prop automatically; pass `color` directly to bypass Uniwind and supply a raw Skia color.
### LineChart.AnimatedLine
| prop | type | default | description |
| -------------------- | -------------------------------------------------------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `points` | `PointsArray` | - | Points for a single series, sourced from `CartesianChart`'s render callback |
| `curveType` | `CurveType` | `'linear'` | d3-shape curve factory name |
| `connectMissingData` | `boolean` | `false` | Whether to visually connect across `null` / missing y values |
| `color` | `Color` | theme `chart-3` | Skia stroke color. Falls back to the `--color-chart-3` CSS variable when omitted |
| `strokeWidth` | `number` | `2` | Stroke width in logical pixels |
| `animation` | `LineChartAnimatedLineAnimation` | `{ type: 'timing', duration: 700 }` | Reanimated config for the draw-on animation. Captured via a ref so inline objects on every render do not re-trigger replay |
| `resetKey` | `number \| string \| boolean \| null` | - | Opaque identity value that re-triggers the draw-on animation when changed. Behaves like a React `key` for the animation only |
| `...SkiaPathProps` | `Omit, 'path' \| 'style' \| 'start' \| 'end'>` | - | Remaining Skia `Path` props. `path`, `style`, `start`, and `end` are controlled internally |
#### LineChartAnimatedLineAnimation
Animation configuration for the draw-on animation. Can be:
* `false` or `"disabled"`: Skip the animation; jump straight to `progress[1]`
* `true` or `undefined`: Use default animation (`{ type: 'timing', duration: 700 }`)
* `{ state: 'disabled', ... }`: Disable the animation while customizing other fields
* `object`: Discriminated animation configuration on the `type` property
`decay` is intentionally excluded since a velocity-based decay has no natural stopping point at the sweep's `to`.
| prop | type | default | description |
| ----------- | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------- |
| `type` | `'timing' \| 'spring'` | - | Animation type. Discriminator that narrows the other fields to `WithTimingConfig` or `WithSpringConfig` |
| `progress` | `[number, number]` | `[0, 1]` | `[from, to]` range bound to the Skia `Path.end` sweep. `[1, 0]` inverts the sweep for a fade-out |
| `...timing` | `WithTimingConfig` | - | Reanimated timing fields (`duration`, `easing`) when `type: 'timing'` |
| `...spring` | `WithSpringConfig` | - | Reanimated spring fields (`damping`, `stiffness`, `mass`, ...) when `type: 'spring'` |
## Hooks
### useLinePath
Re-exported from `victory-native` so consumers can build custom Skia `` renderings on the same `PointsArray` the compound parts consume — useful for layering fills, gradients, or secondary strokes on top of the standard `LineChart.Line`.
```tsx
import { useLinePath } from 'heroui-native-pro';
import { Path } from '@shopify/react-native-skia';
function CustomLine({ points }: { points: PointsArray }) {
const { path } = useLinePath(points, { curveType: 'natural' });
return ;
}
```
See the full reference, including all supported `curveType` values and the `connectMissingData` option, in the [victory-native `useLinePath` docs](https://nearform.com/open-source/victory-native/docs/cartesian/line/use-line-path).
# PieChart
**Category**: native
**URL**: https://heroui.pro/docs/native/components/pie-chart
> A polar chart for visualizing categorical proportions with pie, donut, and segmented donut layouts.
> `PieChart` is built on top of [victory-native](https://nearform.com/open-source/victory-native/docs/), wrapping its `PolarChart` and `Pie.Chart` primitives with HeroUI Native theming and animation cascading. Slice fills come from each data row's `colorKey` field (a Skia `Color`). For full context on chart props, scales, and rendering internals, read the [victory-native docs](https://nearform.com/open-source/victory-native/docs/) alongside this page.
## Import
```tsx
import { PieChart } from 'heroui-native-pro';
```
## Anatomy
```tsx
{({ slice }) => (
<>
>
)}
```
* **PieChart**: Root container that wraps `victory-native` `PolarChart` in a themed outer `View`. Accepts an `animation` prop for cascading `"disable-all"` to animated compound parts through a private context plus `AnimationSettingsProvider`. Expects a single `PieChart.Pie` child.
* **PieChart.Pie**: Wraps `victory-native` `Pie.Chart`. Owns the layout props (`innerRadius`, `circleSweepDegrees`, `startAngle`, `size`) and the per-slice render callback. Bridges the root's animation cascade into the Skia canvas via `AnimationSettingsProvider`.
* **PieChart.Slice**: A single pie/donut slice. The fill is sourced from `data[colorKey]` and the path is computed from slice geometry, so neither is settable directly; pass other Skia paint props (`opacity`, `blendMode`, etc.) or nest a Skia shader child. Respects cascaded `isAllAnimationsDisabled`: when disabled, the `animate` prop is dropped.
* **PieChart.SliceAngularInset**: Stroke painted between adjacent slices for a "segmented" donut look. Configured via `angularInset={{ angularStrokeWidth, angularStrokeColor }}`. Respects cascaded `isAllAnimationsDisabled`.
* **PieChart.Label**: Text label rendered inside a slice. Place as a child of `PieChart.Slice`. Accepts a Skia `font`, `radiusOffset`, `color`, an explicit `text` override (defaults to `slice.label`), and a render-function `children` for fully custom content.
## Usage
### Basic usage
Provide `data`, `labelKey`, `valueKey`, and `colorKey`, then render a `PieChart.Slice` for each slice in the children render function. Height is supplied through `wrapperClassName`.
```tsx
{() => }
```
### Donut
Set `innerRadius` (number of pixels or percentage string) on `PieChart.Pie` to cut out the center.
```tsx
{() => }
```
### Segmented donut
Render `PieChart.SliceAngularInset` alongside `PieChart.Slice` and paint the inset stroke in your chart background color for a "segmented" donut look.
```tsx
{() => (
<>
>
)}
```
### Partial arc
Combine `startAngle` and `circleSweepDegrees` on `PieChart.Pie` to render a partial-arc gauge. Angles are degrees measured clockwise from 12 o'clock (per victory-native).
```tsx
{() => }
```
### Slice labels
Nest `PieChart.Label` as a child of `PieChart.Slice` and supply a Skia `SkFont` via `useFont`. The label text defaults to `slice.label`; pass `text` to override.
```tsx
import { useFont } from '@shopify/react-native-skia';
import InterMedium from './assets/fonts/Inter-Medium.ttf';
function MyChart() {
const font = useFont(InterMedium, 12);
return (
{() => (
)}
);
}
```
### Custom label content
Use the render-function `children` on `PieChart.Label` to render any Skia content at the resolved label position. Receives `{ x, y, midAngle }`.
```tsx
{({ x, y }) => }
```
### Gradient slice fill
Nest a Skia shader as a child of `PieChart.Slice` to overlay a gradient on top of the data-driven fill. The slice path's bounds are used as the shader's coordinate space.
```tsx
import { RadialGradient, vec } from '@shopify/react-native-skia';
;
```
### Animate data transitions
Pass an `animate` config to `PieChart.Slice` (or `PieChart.SliceAngularInset`) to interpolate the Skia slice paths when the underlying `data` changes. Useful for filter swaps, timeframe toggles, or live-updating values.
> Skia can only interpolate paths with the same number of points. If the number of slices changes between renders, the path snaps instead of animating. Keep each dataset's slice count consistent when driving `animate` from variable-length data.
```tsx
{() => }
```
## Example
```tsx
import type { Color } from '@shopify/react-native-skia';
import { Card } from 'heroui-native';
import { PieChart } from 'heroui-native-pro';
import { View } from 'react-native';
type BrowserDatum = {
name: string;
value: number;
color: Color;
};
const BROWSER_DATA: BrowserDatum[] = [
{ name: 'Chrome', value: 62, color: '#6366f1' },
{ name: 'Safari', value: 19, color: '#7c3aed' },
{ name: 'Firefox', value: 10, color: '#8b5cf6' },
{ name: 'Edge', value: 9, color: '#a78bfa' },
];
export default function BrowserUsageChart() {
return (
Browser Usage
{() => (
<>
>
)}
{BROWSER_DATA.map((entry) => (
{entry.name}
))}
);
}
```
## API Reference
### PieChart
| prop | type | default | description |
| ------------------ | ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | Compound subcomponents rendered inside the chart canvas. Expected to be a single `PieChart.Pie` element |
| `wrapperClassName` | `string` | - | Additional Tailwind classes for the outer `View` that wraps `PolarChart`. Required for chart height (e.g. `h-[220px]`) |
| `animation` | `PieChartRootAnimation` | - | Animation configuration for the chart root. Accepts `"disable-all"` to cascade animation skipping to all animated compound parts |
Extends [victory-native `PolarChart`](https://nearform.com/open-source/victory-native/docs/polar/polar-chart) — all `PolarChart` props (`data`, `labelKey`, `valueKey`, `colorKey`, `canvasStyle`, etc.) are supported in addition to the PieChart-specific props above.
#### PieChartRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including animated compound parts
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration with a `state` field for the same disabling semantics
The root does not drive any of its own animated styles; its sole animation responsibility is cascading `isAllAnimationsDisabled` to compound parts that do animate.
### PieChart.Pie
| prop | type | default | description |
| -------------------- | ----------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `(args: { slice: PieSliceData }) => Node` | - | Render function invoked once per slice. Compose `PieChart.Slice`, `PieChart.SliceAngularInset`, and `PieChart.Label` inside the returned tree |
| `innerRadius` | `number \| string` | `0` | Radius of the inner cutout. Numbers are pixels; strings are percentages of the outer radius (e.g. `"60%"`) |
| `circleSweepDegrees` | `number` | `360` | Total sweep of the pie in degrees |
| `startAngle` | `number` | `0` | Starting angle in degrees, measured clockwise from 12 o'clock |
| `size` | `number` | - | Explicit outer diameter in pixels. Defaults to the chart canvas size |
Mirrors [victory-native `Pie.Chart`](https://nearform.com/open-source/victory-native/docs/polar/pie/pie-charts) — only props affected by the HeroUI Native wrapper are listed above. The `animation` prop lives on the `PieChart` root and is bridged into this subcomponent via a private context, so there is no `animation` prop here.
#### PieSliceData
Slice argument passed to `PieChart.Pie`'s children render function. Re-exported from `victory-native`.
| field | type | description |
| --------------------- | --------- | --------------------------------------------------------- |
| `center` | `SkPoint` | Center point of the chart canvas (`{ x, y }`) |
| `color` | `Color` | Resolved Skia color for the slice (from `data[colorKey]`) |
| `startAngle` | `number` | Slice start angle in degrees |
| `endAngle` | `number` | Slice end angle in degrees |
| `sweepAngle` | `number` | Slice sweep (`endAngle − startAngle`) |
| `innerRadius` | `number` | Resolved inner radius in pixels |
| `radius` | `number` | Resolved outer radius in pixels |
| `label` | `string` | Slice label sourced from `data[labelKey]` |
| `value` | `number` | Slice value sourced from `data[valueKey]` |
| `sliceIsEntireCircle` | `boolean` | `true` when the slice is the only slice (full 360° sweep) |
### PieChart.Slice
| prop | type | default | description |
| ------------------ | --------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | Optional `PieChart.Label` child plus Skia shader/effect children (`LinearGradient`, `RadialGradient`, etc.) painted on the slice |
| `label` | `PieLabelProps` | - | Alternative to a `PieChart.Label` child — pass label props directly |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `data` changes. Dropped when cascaded `isAllAnimationsDisabled` is true |
| `...SkiaPathProps` | `Partial>` | - | Remaining Skia `Path` props (`opacity`, `blendMode`, `strokeJoin`, etc.). `color` and `path` are controlled internally |
Extends [victory-native `Pie.Slice`](https://nearform.com/open-source/victory-native/docs/polar/pie/pie-slice). The fill color is sourced from `data[colorKey]` and the slice path is computed from slice geometry, so both are removed from the prop surface; pass other Skia paint props directly.
### PieChart.SliceAngularInset
| prop | type | default | description |
| ------------------ | ----------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `angularInset` | `{ angularStrokeWidth: number; angularStrokeColor: Color }` | - | Stroke configuration drawn between adjacent slices. Use the chart background color for a "segmented" donut look |
| `animate` | `PathAnimationConfig` | - | victory-native path-interpolation config applied when `data` changes. Dropped when cascaded `isAllAnimationsDisabled` is true |
| `...SkiaPathProps` | `Partial>` | - | Remaining Skia `Path` props. `color` and `path` are controlled internally |
Extends [victory-native `Pie.SliceAngularInset`](https://nearform.com/open-source/victory-native/docs/polar/pie/pie-slice-angular-inset).
### PieChart.Label
| prop | type | default | description |
| -------------- | ---------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------- |
| `children` | `(position: LabelPosition) => ReactNode` | - | Render function for fully custom label content. Receives the resolved `{ x, y, midAngle }` |
| `radiusOffset` | `number` | `0.5` | Fractional position along the slice radius (`0` = inner edge, `1` = outer edge) |
| `text` | `string` | `slice.label` | Explicit label text. Defaults to the slice's `labelKey` value |
| `color` | `Color` | `"white"` | Skia text color |
| `font` | `SkFont \| null` | - | Skia font used to render the label. Build one with `useFont` from `@shopify/react-native-skia` |
#### LabelPosition
Position argument passed to the `PieChart.Label` children render function.
| field | type | description |
| ---------- | -------- | ------------------------------------------------------------ |
| `x` | `number` | Resolved x coordinate inside the slice |
| `y` | `number` | Resolved y coordinate inside the slice |
| `midAngle` | `number` | Mid angle of the slice in degrees (useful for rotating text) |
## Hooks
### useSlicePath
Re-exported from `victory-native` so consumers can build custom Skia `` renderings on the same `PieSliceData` the compound parts consume — useful for layering fills, gradients, or secondary strokes on top of the standard `PieChart.Slice`.
```tsx
import { Path } from '@shopify/react-native-skia';
import { useSlicePath } from 'heroui-native-pro';
function CustomSlice({ slice }: { slice: PieSliceData }) {
const path = useSlicePath({ slice });
return ;
}
```
See the full reference in the [victory-native `useSlicePath` docs](https://nearform.com/open-source/victory-native/docs/polar/pie/use-slice-path).
### useSliceAngularInsetPath
Re-exported from `victory-native` so consumers can compute the angular-inset stroke path outside of `PieChart.SliceAngularInset` — useful when rendering a custom Skia primitive for the inter-slice gap.
```tsx
import { Path } from '@shopify/react-native-skia';
import { useSliceAngularInsetPath } from 'heroui-native-pro';
const path = useSliceAngularInsetPath({
slice,
angularInset: { angularStrokeWidth: 4, angularStrokeColor: '#ffffff' },
});
```
See the full reference in the [victory-native `useSliceAngularInsetPath` docs](https://nearform.com/open-source/victory-native/docs/polar/pie/use-slice-angular-inset-path).
# RadarChart
**Category**: native
**URL**: https://heroui.pro/docs/native/components/radar-chart
> A React Native radar chart for comparing multivariate mobile data with fill, dots, and multiple series.
> `RadarChart` is built on top of [victory-native](https://nearform.com/open-source/victory-native/docs/), reusing its `PolarChart` for canvas + measurement and `useAnimatedPath` for polygon interpolation. victory-native does not ship a radar primitive — every visual part (grid rings, spokes, axis labels, radar polygon, vertex dots) is rendered with `@shopify/react-native-skia` `Path`, `Circle`, and `Text`. For full context on chart props and rendering internals, read the [victory-native docs](https://nearform.com/open-source/victory-native/docs/) alongside this page.
## Import
```tsx
import { RadarChart } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **RadarChart**: Root container that wraps `victory-native` `PolarChart` in a themed outer `View`. Publishes `data`, `labelKey`, `dataKey`, `maxValue`, and the measured canvas size to compound subcomponents through an internal layout context that crosses the Skia reconciler via `useContextBridge`.
* **RadarChart.Grid**: Concentric rings (polygons by default) plus one radial spoke per category. Renders nothing until the canvas has been measured.
* **RadarChart.AngleAxis**: Category labels rendered around the chart perimeter, one per row, using `data[i][labelKey]`. Skia text via the default system font (`matchFont`).
* **RadarChart.RadiusAxis**: Numeric tick labels rendered along a single spoke (defaults to 12 o'clock). One label per grid ring, optionally including a `0` label at the chart center. Each tick rotates with the spoke so labels always read along its direction.
* **RadarChart.Radar**: Filled, stroked polygon for a single series. Render multiple siblings (each with its own `dataKey`) for a multi-series radar. Supports victory-native's `animate` config for smooth path interpolation when the underlying data changes.
## Usage
### Basic usage
Provide `data`, `labelKey`, and `dataKey`. The default `RadarChart.Radar` reads the root's `dataKey` and uses the theme `chart-3` color. Height is supplied through `wrapperClassName`.
```tsx
```
### Multi-series
Render multiple `RadarChart.Radar` siblings with distinct `dataKey` overrides to overlay several numeric fields on the same set of categories.
```tsx
```
### Dots only
Set `fillOpacity={0}` and `showDots` to emphasize each vertex as a discrete marker rather than reading the chart as filled areas.
```tsx
```
### Circle grid
Pass `shape="circle"` to `RadarChart.Grid` to draw perfectly round rings instead of regular polygons — useful when the chart reads as a continuous radial gauge rather than a categorical comparison.
```tsx
```
### With radius axis
Render `RadarChart.RadiusAxis` to label each ring with a numeric tick. Pass `tickFormatter` to format values (units, rounding) and `includeZero` to render the origin tick at the chart center.
```tsx
`${Math.round(value)}%`}
/>
```
### Fixed scale
Pass `maxValue` on the root to fix the radial scale across all series. This keeps multi-series radars on a shared scale and the chart stable when underlying values change. Without it each series re-normalizes to the local data max.
```tsx
```
### Custom radius-axis spoke
Move `RadarChart.RadiusAxis` to a different spoke with `angle` (degrees, clockwise from 12 o'clock) and pick a horizontal alignment relative to that spoke with `orientation`. Labels rotate with the spoke so they always read along its direction.
```tsx
```
### Custom axis font
Pass an `SkFont` built with `useFont` to either axis to override the default `matchFont` system font.
```tsx
import { useFont } from '@shopify/react-native-skia';
import InterMedium from './assets/fonts/Inter-Medium.ttf';
function MyChart() {
const font = useFont(InterMedium, 12);
return (
);
}
```
### Animate data transitions
Pass an `animate` config to `RadarChart.Radar` to interpolate the Skia polygon path when the underlying `data` (or `dataKey`) changes. Useful for filter swaps, timeframe toggles, or preset switchers.
> Skia can only interpolate paths with the same number of points. If the number of categories changes between renders, the path snaps instead of animating. Keep each dataset's category count consistent when driving `animate` from variable-length data.
```tsx
```
## Example
```tsx
import { Card } from 'heroui-native';
import { RadarChart } from 'heroui-native-pro';
import { View } from 'react-native';
type SkillRow = {
category: string;
score: number;
};
const SKILLS_DATA: SkillRow[] = [
{ category: 'Design', score: 86 },
{ category: 'Frontend', score: 92 },
{ category: 'Backend', score: 74 },
{ category: 'DevOps', score: 65 },
{ category: 'Testing', score: 78 },
{ category: 'Leadership', score: 70 },
];
export default function SkillAssessmentChart() {
return (
Skill Assessment
);
}
```
## API Reference
### RadarChart
| prop | type | default | description |
| ------------------ | ------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | Compound subcomponents rendered inside the chart canvas (`Grid`, `AngleAxis`, `RadiusAxis`, one or more `Radar`) |
| `data` | `RawData[]` | - | Categorical-axis data rows. Each row contributes one spoke; the label comes from `row[labelKey]` and series values from `dataKey` |
| `labelKey` | `keyof RawData` | - | Key on each row whose value is the axis label rendered by `RadarChart.AngleAxis` |
| `dataKey` | `keyof RawData` | - | Default numeric key plotted by `RadarChart.Radar` when no per-series override is provided |
| `maxValue` | `number` | `Math.max(...data[dataKey])` | Upper bound of the radial scale. Pass an explicit value when comparing charts or when the domain is fixed |
| `wrapperClassName` | `string` | - | Additional Tailwind classes for the outer `View`. Required for chart height (e.g. `h-[280px]`) |
| `animation` | `RadarChartRootAnimation` | - | Animation configuration for the chart root. Accepts `"disable-all"` to cascade animation skipping to every animated compound part |
#### RadarChartRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including animated compound parts
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration with a `state` field for the same disabling semantics
The root does not drive any of its own animated styles; its sole animation responsibility is cascading `isAllAnimationsDisabled` to compound parts that do animate (`RadarChart.Radar`).
### RadarChart.Grid
| prop | type | default | description |
| ------------- | --------------------- | -------------------------- | ---------------------------------------------------------------------------------------------- |
| `numTicks` | `number` | `5` | Number of concentric rings (excluding the center). Matches `RadarChart.RadiusAxis.numTicks` |
| `shape` | `RadarChartGridShape` | `"polygon"` | Shape of each concentric ring. `"polygon"` matches `data.length` vertices, `"circle"` is round |
| `showSpokes` | `boolean` | `true` | Whether to render the radial spokes (one per category) in addition to the rings |
| `strokeColor` | `Color` | theme `muted` at 30% alpha | Stroke color for both the rings and the spokes |
| `strokeWidth` | `number` | `1` | Stroke width for both the rings and the spokes |
#### RadarChartGridShape
Concentric grid shape.
| value | description |
| ----------- | ----------------------------------------------------------------------------------------- |
| `"polygon"` | Each ring is a closed polygon whose vertex count matches `data.length` (Recharts default) |
| `"circle"` | Each ring is a true circle, useful for continuous radial gauges |
### RadarChart.AngleAxis
| prop | type | default | description |
| -------------- | ---------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `font` | `SkFont \| null` | system `matchFont` | Skia font used to render the labels. Build a custom font with `useFont` from `@shopify/react-native-skia` |
| `color` | `Color` | theme `muted` | Text color for the labels |
| `radiusOffset` | `number` | `1.05` | Fractional position of each label along the spoke. `1` sits on the outer ring; `> 1` pushes the labels outside the ring |
### RadarChart.RadiusAxis
| prop | type | default | description |
| --------------- | --------------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `angle` | `number` | `0` | Spoke angle in **degrees, clockwise from 12 o'clock**. Labels rotate with the spoke so they always read along its direction |
| `orientation` | `RadarChartRadiusAxisOrientation` | `"right"` | Horizontal alignment of the tick labels relative to the spoke |
| `numTicks` | `number` | `5` | Number of tick values to render. Defaults to match `RadarChart.Grid.numTicks` so labels line up with rings |
| `includeZero` | `boolean` | `false` | Whether to render an additional `0` label at the chart center. Opt in to match Recharts' default behaviour |
| `dataKey` | `string` | root `dataKey` | Numeric key used to auto-derive the radial scale when `maxValue` is omitted on the root. Override to align ticks with a specific `Radar` key |
| `font` | `SkFont \| null` | system `matchFont` | Skia font used to render the tick values |
| `color` | `Color` | theme `muted` | Text color for the tick values |
| `tickFormatter` | `(value: number) => string` | `String(Math.round(value))` | Formatter applied to each tick value before rendering. Use to add unit suffixes (`"%"`, `"k"`) or round fractional ticks |
> When the chart has multiple `RadarChart.Radar` siblings on different keys (or a single `Radar` whose `dataKey` differs from the root's), set `maxValue` on the root so the axis ticks remain truthful across every polygon. Auto-derived scales can only follow one key at a time — `RadarChart.RadiusAxis` defaults to the root's `dataKey` and accepts a `dataKey` override to point at a different series.
#### RadarChartRadiusAxisOrientation
Horizontal alignment of tick labels **relative to the spoke** they sit on.
| value | description |
| ---------- | ----------------------------------------------------------------------------------------------- |
| `"left"` | Labels sit to the left of the spoke (text's right edge anchored at the spoke) |
| `"middle"` | Labels are centered on the spoke (text's center anchored at the spoke) |
| `"right"` | Labels sit to the right of the spoke (text's left edge anchored at the spoke). Recharts default |
### RadarChart.Radar
| prop | type | default | description |
| ------------- | ------------------------------ | --------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `dataKey` | `string` | root `dataKey` | Numeric key on each row plotted by this series. Override for multi-series radars |
| `color` | `Color` | theme `chart-3` | Stroke + fill color of the polygon |
| `fillOpacity` | `number` | `0.3` | Alpha applied to the polygon fill (the stroke stays fully opaque) |
| `showStroke` | `boolean` | `true` | Whether to draw a stroked outline around the polygon |
| `strokeWidth` | `number` | `2` | Stroke width applied when `showStroke` is `true` |
| `showDots` | `boolean` | `false` | Whether to render a small filled circle at each polygon vertex (one per category) |
| `dotRadius` | `number` | `3` | Radius of each vertex dot when `showDots` is `true` |
| `animate` | `RadarChartRadarAnimateConfig` | - | victory-native path-interpolation config applied when `data` changes. Dropped when cascaded `isAllAnimationsDisabled` is true |
#### RadarChartRadarAnimateConfig
Reanimated config carried via the `animate` prop. Mirrors victory-native's `PathAnimationConfig` — a discriminated union on `type`.
| field | type | description |
| ------ | -------------------------------------- | --------------------------------------------------------- |
| `type` | `"timing" \| "spring"` | Animation kind |
| `...` | `WithTimingConfig \| WithSpringConfig` | Remaining fields from the corresponding Reanimated config |
When the polygon's `data` changes, the new Skia path is interpolated from the previous frame's path according to this config. Dropped when the root cascade sets `animation="disable-all"`.
# RadialChart
**Category**: native
**URL**: https://heroui.pro/docs/native/components/radial-chart
> A React Native radial chart for mobile gauges, progress rings, and circular data with customizable arcs and labels.
> `RadialChart` is built on top of [victory-native](https://nearform.com/open-source/victory-native/docs/), reusing its `PolarChart` for canvas + measurement. victory-native does not ship a radial-bar primitive — every ring (value arc + optional background track) is rendered with `@shopify/react-native-skia` stroked arc paths. For full context on chart props and rendering internals, read the [victory-native docs](https://nearform.com/open-source/victory-native/docs/) alongside this page.
## Import
```tsx
import { RadialChart } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **RadialChart**: Root container that wraps `victory-native` `PolarChart` in a themed outer `View`. Publishes `data`, keys, angles, radii, and measured canvas size to compound subcomponents through an internal layout context.
* **RadialChart.Bar**: Renders all concentric rounded arc rings. Row `0` is the innermost ring. Optional `background` draws a full-domain track behind each ring (theme `default` by default).
Center labels and side legends are composed by consumers with absolute overlays — see the example screen's "Energy Activity" variant.
## Usage
### Basic usage
Provide `data`, `labelKey`, `valueKey`, and `colorKey`. Height and width are supplied through `wrapperClassName`.
By default the angle domain is `[0, "auto"]` — the upper bound resolves to the maximum `valueKey` in `data`, so the largest value fills the full angle span.
```tsx
```
### Fixed scale (gauge / progress)
Pass `domain={[0, 100]}` on the root to fix the angle-axis domain so arc sweep maps directly to a percentage.
```tsx
```
### Even band distribution
By default `barGap={4}` stacks rings with a fixed pixel gap and `barSize`. Pass `barGap="auto"` to distribute bands evenly from `innerRadius` to `outerRadius`.
```tsx
```
### Progress ring (single row)
A single data row with `innerRadius` tuned for a thick ring reads as a progress indicator.
```tsx
```
### Animated sweep
Pass `animate` to `RadialChart.Bar` so ring fills interpolate when `data` changes.
```tsx
```
### Disable animations
Cascade `"disable-all"` from the root to snap rings to their target sweep instantly.
```tsx
```
## Example
```tsx
import { Card } from 'heroui-native';
import { RadialChart } from 'heroui-native-pro';
import { View } from 'react-native';
import { AppText } from './app-text';
const energyData = [
{
name: 'Calories',
value: 200,
color: '#a78bfa',
valueText: '1,623/2,000 kcal',
},
{
name: 'Steps',
value: 350,
color: '#8b5cf6',
valueText: '8,328/10,000 steps',
},
{ name: 'Exercise', value: 250, color: '#7c3aed', valueText: '25/120 min' },
];
export default function EnergyActivityCard() {
return (
Energy Activity
{energyData.map((item) => (
{item.name}
{item.valueText}
))}
Calories
700 kcal
);
}
```
## API Reference
### RadialChart
Extends [victory-native `PolarChart`](https://nearform.com/open-source/victory-native/docs/polar/polar-chart) — all `PolarChart` props (`data`, `labelKey`, `valueKey`, `colorKey`, `canvasStyle`, etc.) are supported in addition to the RadialChart-specific props below.
| prop | type | default | description |
| ------------------ | -------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `children` | `ReactNode` | - | Compound subcomponents rendered inside the chart canvas (typically `.Bar`) |
| `domain` | `[number \| "auto", number \| "auto"]` | `[0, "auto"]` | Angle-axis domain; `"auto"` bounds resolve from data |
| `startAngle` | `number` | `90` | Start angle in degrees (clockwise from 12 o'clock) |
| `endAngle` | `number` | `-270` | End angle in degrees — with default `startAngle`, spans a full circle |
| `innerRadius` | ``number \| `${number}%` `` | `"40%"` | Inner bound of the bar area |
| `outerRadius` | ``number \| `${number}%` `` | `"100%"` | Outer bound of the bar area |
| `barSize` | `number` | `10` | Default bar thickness in pixels |
| `barGap` | `number \| "auto"` | `4` | Gap in pixels between rings, or `"auto"` to distribute evenly across the annulus |
| `wrapperClassName` | `string` | - | Tailwind classes for the outer `View`; constrains sizing on top of the default `w-full aspect-square` (e.g. `w-[200px]`) |
| `animation` | `RadialChartRootAnimation` | - | Root animation config; cascades `"disable-all"` to `RadialChart.Bar` |
#### RadialChartRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including `RadialChart.Bar` sweep fills
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration with a `state` field for the same disabling semantics
### RadialChart.Bar
| prop | type | default | description |
| -------------- | ----------------------------- | --------------- | ----------------------------------------------------------------------------------------------------- |
| `background` | `boolean` | `true` | Draw a full-domain background track behind each ring |
| `barSize` | `number` | root `barSize` | Bar thickness in pixels |
| `cornerRadius` | `number` | `12` | Values `> 0` enable round stroke caps; `0` uses butt caps |
| `trackColor` | `Color` | theme `default` | Stroke color for background tracks when `background` is enabled |
| `animate` | `RadialChartBarAnimateConfig` | - | Reanimated timing/spring config for sweep-fill animation; dropped when root `animation="disable-all"` |
#### RadialChartBarAnimateConfig
Reanimated config carried via the `animate` prop. Mirrors victory-native's `PathAnimationConfig` — a discriminated union on `type` (`"timing"` or `"spring"`).
| field | type | description |
| ------ | -------------------------------------- | --------------------------------------------------------- |
| `type` | `"timing" \| "spring"` | Animation kind |
| `...` | `WithTimingConfig \| WithSpringConfig` | Remaining fields from the corresponding Reanimated config |
# Badge
**Category**: native
**URL**: https://heroui.pro/docs/native/components/badge
> Displays a small indicator positioned relative to another element, commonly used for notification counts, status dots, and labels
## Import
```tsx
import { Badge } from 'heroui-native-pro';
```
## Anatomy
```tsx
...
...
```
* **Badge**: Root container. Renders as a dot when no children are passed, or as a pill with content otherwise. Supports `color`, `variant`, `size`, and `placement` props. When used inside `Badge.Anchor`, it is absolutely positioned at the specified corner.
* **Badge.Background**: Optional theme-aware background container rendered behind the badge surface. Mounted automatically for the variants whose background resolves to the default color when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **Badge.Anchor**: Relative wrapper that positions the `Badge` over another element (e.g. `Avatar`, `Icon`).
* **Badge.Label**: Text content inside the badge. Automatically used when string or number children are passed to `Badge`.
## Usage
### Basic usage
Wrap the anchored element and the badge in `Badge.Anchor`. Pass a string or number as children to render a pill.
```tsx
...
5
```
### Dot badge
Omit children to render a dot indicator.
```tsx
...
```
### Colors
Switch the badge color with the `color` prop.
```tsx
55555
```
### Variants
Change the visual style with the `variant` prop.
```tsx
555
```
### Sizes
Control the badge size with the `size` prop.
```tsx
555
```
### Placements
Position the badge at any corner of its anchor with the `placement` prop. Only takes effect when used inside `Badge.Anchor`.
```tsx
............
```
### Custom label
Compose the badge content explicitly with `Badge.Label` for full control over the text element.
```tsx
99+
```
## Example
```tsx
import { Avatar } from 'heroui-native';
import { Badge } from 'heroui-native-pro';
import { View } from 'react-native';
export default function BadgeExample() {
return (
JD
5
AB
New
CD
);
}
```
## API Reference
### Badge
| prop | type | default | description |
| -------------- | ------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to display inside the badge (text, number, or icon). When omitted, renders as a dot |
| `size` | `BadgeSize` | `'md'` | Size of the badge |
| `color` | `BadgeColor` | `'default'` | Color variant of the badge |
| `variant` | `BadgeVariant` | `'primary'` | Visual style variant |
| `placement` | `BadgePlacement` | `'top-right'` | Position of the badge relative to its anchor. Only takes effect when used inside a `Badge.Anchor` |
| `className` | `string` | - | Additional CSS classes for the badge container |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the badge subtree |
| `background` | `React.ReactNode` | - | Background layer behind the surface. `undefined` renders the theme-aware default for the `secondary` variant and `primary`/`soft` variants with `color="default"`; custom node replaces it; `null` removes it |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### BadgeSize
| type | description |
| ---------------------- | -------------------------- |
| `'sm' \| 'md' \| 'lg'` | Size variants of the badge |
#### BadgeColor
| type | description |
| ------------------------------------------------------------- | --------------------------- |
| `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | Color variants of the badge |
#### BadgeVariant
| type | description |
| ------------------------------------ | ---------------------------------- |
| `'primary' \| 'secondary' \| 'soft'` | Visual style variants of the badge |
#### BadgePlacement
| type | description |
| -------------------------------------------------------------- | --------------------------------------------- |
| `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left'` | Placement of the badge relative to its anchor |
#### AnimationRootDisableAll
Animation configuration for the Badge root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### Badge.Background
Absolute-fill container rendered behind the badge surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Badge.Anchor
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------------------------------------- |
| `children` | `React.ReactNode` | - | The element to anchor the badge to, plus the `Badge` itself |
| `className` | `string` | - | Additional CSS classes for the anchor wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Badge.Label
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Label text content |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useBadge
Hook to access the Badge context. Must be used within a `Badge` component.
```tsx
import { useBadge } from 'heroui-native-pro';
const { size, color, variant, isDot } = useBadge();
```
#### Returns: BadgeContextValue
| property | type | description |
| --------- | -------------- | ----------------------------------------------- |
| `size` | `BadgeSize` | Current size variant |
| `color` | `BadgeColor` | Current color variant |
| `variant` | `BadgeVariant` | Current visual variant |
| `isDot` | `boolean` | Whether the badge renders as a dot (no content) |
# Carousel
**Category**: native
**URL**: https://heroui.pro/docs/native/components/carousel
> A horizontal snap pager with navigation buttons, dot indicators, and a thumbnail strip.
## Import
```tsx
import { Carousel, useCarousel } from 'heroui-native-pro';
```
## Anatomy
```tsx
......
```
* **Carousel**: Root container. Owns the snap engine — the selected index, the snap offsets computed from the measured viewport, and autoplay — and provides it to all parts via context.
* **Carousel.Content**: Measured slide viewport hosting the horizontal snap FlatList with the `Carousel.Item` children (fed to the list as data).
* **Carousel.Item**: One slide. Its width is computed by the engine from the measured viewport, `itemsPerView`, `gap`, and `sidePadding`.
* **Carousel.Previous** / **Carousel.Next**: Navigation buttons, disabled at the ends. Positioned per the root `type`: overlaid on the slide area (`in-place`), outside it (`modal`), or inline (`miniatures`). Default chevrons mirror in RTL.
* **Carousel.Dots**: Indication-only dots (not pressable), one per snap point. The selected pill interpolates against root `progress` as the strip is dragged. Hidden when there is at most one snap point.
* **Carousel.Thumbnails**: Horizontal FlatList strip of thumbnails that auto-scrolls the selection toward its center.
* **Carousel.Thumbnail**: Pressable thumbnail navigating to a slide (`index` required). Renders an image `source` or custom children, plus an animated selection ring.
## Usage
### Basic usage
One full-width slide per view with navigation buttons and dots.
```tsx
{SLIDES.map((slide) => (
))}
```
### Multiple slides per view
Fractional `itemsPerView` values peek the next slide. `gap` controls the spacing between slides and `align` where the selected slide sits.
```tsx
{items}
```
### Full-bleed with side padding
For an edge-to-edge carousel with breathing room, use `sidePadding` instead of padding the container (which clips the strip) or the content row (which desyncs the snap offsets). The engine owns it: the first and last slides rest inset by the padding, intermediate slides keep their `align` position on screen, and the overlaid navigation buttons keep their inset relative to the padded slide area. Without an explicit `gap`, the gap defaults to `sidePadding`.
```tsx
{items}
```
### Autoplay
Autoplay advances to the next snap point on an interval and wraps back to the first at the end. By default it stops permanently on the first user interaction; set `stopAutoPlayOnInteraction={false}` to only pause while dragging.
```tsx
{items}
```
### Thumbnails
The `miniatures` type renders the navigation buttons inline, typically in a row with the thumbnail strip.
```tsx
{items}
{SLIDES.map((slide, index) => (
))}
```
### Custom dots
`renderDot` replaces each default dot. Dots are indication-only; interpolate against the `progress` shared value (continuous snap index) so the indicator follows the drag on the UI thread.
```tsx
{items} (
)}
/>
```
```tsx
const MyDot = ({
index,
progress,
}: {
index: number;
progress: SharedValue;
}) => {
const rStyle = useAnimatedStyle(() => {
const amount = interpolate(
progress.get(),
[index - 1, index, index + 1],
[0, 1, 0],
Extrapolation.CLAMP
);
return { opacity: 0.35 + amount * 0.65 };
});
return (
);
};
```
### Scroll-driven effects
`useCarousel()` exposes `scrollX` (physical offset) and `progress` (continuous snap index) as shared values for parallax and indicator effects. `progress` is logical: interpolating a physical `translateX` from it must be mirrored off `useIsRTL()`, while interpolating `start` needs no mirroring.
```tsx
const { progress, snapCount } = useCarousel();
const rStyle = useAnimatedStyle(() => ({
transform: [
{
translateX: interpolate(
progress.get(),
[0, snapCount - 1],
[0, trackWidth]
),
},
],
}));
```
## Example
```tsx
import { Carousel } from 'heroui-native-pro';
import { Image, View } from 'react-native';
const SLIDE_URIS = [
'https://example.com/photo-1.jpg',
'https://example.com/photo-2.jpg',
'https://example.com/photo-3.jpg',
'https://example.com/photo-4.jpg',
];
export default function GalleryCarousel() {
return (
{SLIDE_URIS.map((uri) => (
))}
{SLIDE_URIS.map((uri, index) => (
))}
);
}
```
## API Reference
### Carousel
| prop | type | default | description |
| --------------------------- | --------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Compound parts rendered inside the carousel |
| `itemsPerView` | `number` | `1` | Slides visible per view; fractional values peek the next slide |
| `gap` | `number` | `16` / `sidePadding` | Gap between adjacent slides in pixels; defaults to `sidePadding` when that is provided |
| `sidePadding` | `number` | `0` | Engine-owned breathing room at both ends of the strip; edge slides rest inset by it and overlaid nav buttons keep their inset relative to the padded slide area |
| `align` | `'start' \| 'center' \| 'end'` | `'start'` | Alignment of the selected slide within the viewport |
| `type` | `'in-place' \| 'modal' \| 'miniatures'` | `'in-place'` | Where the navigation buttons sit |
| `defaultIndex` | `number` | `0` | Snap index the carousel starts on |
| `autoPlay` | `boolean` | `false` | Advance automatically; wraps to the first snap point at the end |
| `autoPlayInterval` | `number` | `4000` | Interval between autoplay advances in milliseconds |
| `stopAutoPlayOnInteraction` | `boolean` | `true` | Stop autoplay permanently on the first user interaction |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onSelectedIndexChange` | `(index: number) => void` | - | Fires when the selected snap index changes. Navigation presses commit immediately; a swipe commits as soon as it crosses the halfway point between two snap points |
| `animation` | `'disable-all' \| undefined` | - | `'disable-all'` disables all animations including children; `undefined` uses default animations |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Carousel.Content
The engine owns `horizontal`, `snapToOffsets`, `onScroll`, `contentOffset`, `data`, `renderItem`, `keyExtractor`, `getItemLayout`, and `contentContainerClassName`; style the slide row through `className` / `classNames.content` instead. `CellRendererComponent` is reserved by Reanimated's animated FlatList.
| prop | type | default | description |
| ------------------ | -------------------------------------------------- | ------- | --------------------------------------------------- |
| `children` | `React.ReactNode` | - | `Carousel.Item` children, one per slide |
| `className` | `string` | - | Additional CSS classes for the slide row |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Inline styles for individual slots |
| `...FlatListProps` | `FlatListProps` | - | Remaining FlatList props are forwarded to the strip |
#### ElementSlots\
| slot | description |
| ---------- | ------------------------------------------------------ |
| `viewport` | Measured content-box wrapper hosting the snap FlatList |
| `content` | Slide row inside the FlatList |
#### styles
| slot | type | description |
| ---------- | ----------- | ------------------------------------- |
| `viewport` | `ViewStyle` | Inline style for the viewport wrapper |
| `content` | `ViewStyle` | Inline style for the slide row |
### Carousel.Item
The slide `width` is owned by the engine and cannot be set via `className` or `style`.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Slide content |
| `className` | `string` | - | Additional CSS classes for the slide container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Carousel.Previous / Carousel.Next
| prop | type | default | description |
| ------------------- | ---------------------------------------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | Chevron | Custom icon replacing the default chevron |
| `className` | `string` | - | Additional CSS classes for the button element |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Inline styles for individual slots |
| `style` | `StyleProp` | - | Style applied to the button element |
| `...PressableProps` | `Omit` | - | All standard React Native Pressable props are supported |
#### ElementSlots\
| slot | description |
| ----------- | ------------------------------------------------------------------------------ |
| `container` | Positioning shell (absolute for `in-place` / `modal`, inline for `miniatures`) |
| `button` | The pressable button |
#### styles
| slot | type | description |
| ----------- | ----------- | -------------------------------------- |
| `container` | `ViewStyle` | Inline style for the positioning shell |
| `button` | `ViewStyle` | Inline style for the pressable button |
### Carousel.Dots
Indication-only (not pressable). Hidden when there is at most one snap point.
| prop | type | default | description |
| ----------------------- | ----------------------------------------------- | ------- | ------------------------------------------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes for the dots container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots (see animated property notes below) |
| `styles` | `Partial>` | - | Inline styles for individual slots |
| `renderDot` | `(props: CarouselDotRenderProps) => ReactNode` | - | Replaces each default dot |
| `animation` | `CarouselDotAnimation` | - | Width / background-color ranges interpolated from root `progress` |
| `isAnimatedStyleActive` | `boolean` | `true` | When `false`, the selected state is styled through the `--is-selected` modifier class |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### CarouselDotRenderProps
Argument passed to `renderDot`.
| prop | type | description |
| ------------ | --------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `index` | `number` | Snap index this dot represents |
| `isSelected` | `boolean` | Whether this dot's snap point is currently selected |
| `progress` | `SharedValue` | Continuous snap-index progress (same numeric scale as `index`); interpolate against it inside `useAnimatedStyle` |
#### ElementSlots\
| slot | description |
| ----------- | --------------------------------------------------- |
| `container` | Row hosting the dots |
| `dot` | One default dot (see animated property notes below) |
The `dot` slot animates `width` and `backgroundColor`, interpolated from root `progress`. These properties cannot be overridden via `className`; use the `animation` prop to customize, or `isAnimatedStyleActive={false}` to remove them entirely.
#### styles
| slot | type | description |
| ----------- | ----------- | -------------------------------- |
| `container` | `ViewStyle` | Inline style for the dots row |
| `dot` | `ViewStyle` | Inline style for one default dot |
#### CarouselDotAnimation
Animation configuration for the default dots. Can be:
* `false` or `"disabled"`: Disable the width and background-color interpolation (dots snap through the `--is-selected` modifier class)
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ----------------------- | ------------------ | -------------------------------- | ---------------------------------------------- |
| `width.value` | `[number, number]` | `[8, 20]` | `[unselected, selected]` dot widths in pixels |
| `backgroundColor.value` | `[string, string]` | Theme `[default, accent]` colors | `[unselected, selected]` dot background colors |
### Carousel.Thumbnails
The strip owns `horizontal`, `data`, `renderItem`, `keyExtractor`, `onScrollToIndexFailed`, and `contentContainerClassName`; style the content row through `classNames.content` instead. Auto-scrolls the selected thumbnail toward its center.
| prop | type | default | description |
| ------------------ | ----------------------------------------------------- | ------- | --------------------------------------------------- |
| `children` | `React.ReactNode` | - | `Carousel.Thumbnail` children |
| `className` | `string` | - | Additional CSS classes for the strip container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Inline styles for individual slots |
| `...FlatListProps` | `FlatListProps` | - | Remaining FlatList props are forwarded to the strip |
#### ElementSlots\
| slot | description |
| ----------- | ---------------------------- |
| `container` | Horizontal FlatList strip |
| `content` | Content row inside the strip |
#### styles
| slot | type | description |
| ----------- | ----------- | ------------------------------------ |
| `container` | `ViewStyle` | Inline style for the strip container |
| `content` | `ViewStyle` | Inline style for the content row |
### Carousel.Thumbnail
| prop | type | default | description |
| ----------------------- | --------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom thumbnail content, replacing the default image |
| `index` | `number` | - | Snap index this thumbnail navigates to (0-based). Required |
| `source` | `ImageSourcePropType` | - | Image rendered when no custom children are provided |
| `className` | `string` | - | Additional CSS classes for the thumbnail container (see animated property notes below) |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `{ container?, image?, ring? }` | - | Inline styles for individual slots |
| `style` | `StyleProp` | - | Style applied to the thumbnail container |
| `imageProps` | `Omit` | - | Props forwarded to the default image. Ignored when custom children are provided |
| `animation` | `CarouselThumbnailAnimation` | - | Scale / ring-opacity animation configuration |
| `isAnimatedStyleActive` | `boolean` | `true` | When `false`, the selection ring is styled through the `--is-selected` modifier class and press feedback is removed |
| `...PressableProps` | `Omit` | - | All standard React Native Pressable props are supported |
#### ElementSlots\
| slot | description |
| ----------- | ---------------------------------------------------------- |
| `container` | Pressable thumbnail (see animated property notes below) |
| `image` | Default image element |
| `ring` | Selection ring overlay (see animated property notes below) |
The `container` slot animates `transform` (scale) for press feedback and the `ring` slot animates `opacity` for the selection transition. These properties cannot be overridden via `className`; use the `animation` prop to customize, or `isAnimatedStyleActive={false}` to remove them entirely.
#### styles
| slot | type | description |
| ----------- | ------------ | ---------------------------------------- |
| `container` | `ViewStyle` | Inline style for the pressable container |
| `image` | `ImageStyle` | Inline style for the default image |
| `ring` | `ViewStyle` | Inline style for the selection ring |
#### CarouselThumbnailAnimation
Animation configuration for the thumbnail. Can be:
* `false` or `"disabled"`: Disable press scale and ring opacity animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------------------- | ------------------ | ------------------- | ------------------------------------------------- |
| `scale.value` | `[number, number]` | `[1, 0.95]` | `[idle, pressed]` thumbnail scale values |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Timing configuration for the press scale |
| `ringOpacity.value` | `[number, number]` | `[0, 1]` | `[unselected, selected]` selection-ring opacities |
| `ringOpacity.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Timing configuration for the ring opacity |
## Hooks
### useCarousel
Hook to access the carousel context. Must be used within a `Carousel` component.
```tsx
import { useCarousel } from 'heroui-native-pro';
const { selectedIndex, snapCount, scrollTo, progress } = useCarousel();
```
#### Returns: CarouselContextValue
| property | type | description |
| ---------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | `'in-place' \| 'modal' \| 'miniatures'` | Active layout type |
| `align` | `'start' \| 'center' \| 'end'` | Alignment of the selected slide within the viewport |
| `gap` | `number` | Gap between adjacent slides in pixels |
| `sidePadding` | `number` | Breathing room at both ends of the slide strip in pixels |
| `itemsPerView` | `number` | Number of slides visible per view |
| `itemWidth` | `number` | Computed slide width in pixels (`0` until the viewport is measured) |
| `viewportWidth` | `number` | Measured width of the slide viewport (`0` until measured) |
| `viewportHeight` | `number` | Measured height of the slide viewport (`0` until measured) |
| `selectedIndex` | `number` | Currently selected snap index. Navigation presses commit immediately; a swipe commits at the halfway point |
| `snapCount` | `number` | Number of distinct snap points |
| `canScrollPrev` | `boolean` | Whether a previous snap point exists |
| `canScrollNext` | `boolean` | Whether a next snap point exists |
| `scrollTo` | `(index: number, animated?: boolean) => void` | Scroll to the given snap index |
| `scrollPrev` | `() => void` | Scroll to the previous snap point |
| `scrollNext` | `() => void` | Scroll to the next snap point |
| `scrollX` | `SharedValue` | Physical scroll offset of the strip in pixels. Grows toward the physical end regardless of RTL |
| `progress` | `SharedValue` | Continuous snap-index progress (`0` to `snapCount - 1`), interpolating while the strip is dragged. Logical in both layout directions; mirror physical `translateX` interpolations off `useIsRTL()` |
# EmptyState
**Category**: native
**URL**: https://heroui.pro/docs/native/components/empty-state
> A React Native empty-state view with icon, title, description, and action for guiding mobile users.
## Import
```tsx
import { EmptyState } from 'heroui-native-pro';
```
## Anatomy
```tsx
............
```
* **EmptyState**: Root container. Centers compound parts vertically with consistent spacing and padding, and cascades `disable-all` to animated descendants. Sub-components are fully optional.
* **EmptyState.Header**: Groups `Media`, `Title`, and `Description` in a centered column.
* **EmptyState.Media**: Optional container for an icon, avatar, or custom media block. Supports `variant="default"` (render as-is) and `variant="icon"` (circular muted surface).
* **EmptyState.MediaBackground**: Optional theme-aware background container rendered behind the icon media circle. Mounted automatically for the `icon` variant when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **EmptyState.Title**: Primary heading text rendered with `accessibilityRole="header"`.
* **EmptyState.Description**: Secondary muted text below the title.
* **EmptyState.Content**: Optional action area below the header for buttons, inputs, or other controls.
## Usage
### Basic usage
Compose a header with title and description for a minimal empty state.
```tsx
Inbox zero...
```
### With icon media
Use `variant="icon"` to wrap the media in a circular muted surface.
```tsx
No notifications yet...
```
### With actions
Add an action area below the header with `EmptyState.Content`.
```tsx
No matches found...
```
### With custom media
Use `variant="default"` (the default) to render media content as-is, such as an avatar or stacked avatars.
```tsx
...User is offline...
```
### Bordered container
Apply a dashed border on the root to render an outlined empty state.
```tsx
Start your first automation...
```
## Example
```tsx
import { Button } from 'heroui-native';
import { EmptyState } from 'heroui-native-pro';
import { View } from 'react-native';
import { BellIcon } from './icons/bell';
export default function EmptyStateExample() {
return (
No notifications yet
Stay in the loop by enabling push alerts for account activity and
reminders.
);
}
```
## API Reference
### EmptyState
| prop | type | default | description |
| -------------- | ------------------------- | ------- | ----------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound parts rendered inside the empty state container |
| `className` | `string` | - | Additional CSS classes for the root container |
| `animation` | `EmptyStateRootAnimation` | - | Animation configuration for the empty state root (cascades to animated descendants) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### EmptyStateRootAnimation
Animation configuration for the EmptyState root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down through `AnimationSettingsProvider`)
* `undefined`: Use default animations
### EmptyState.Header
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Header content (media, title, description) |
| `className` | `string` | - | Additional CSS classes for the header wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### EmptyState.Media
| prop | type | default | description |
| -------------- | ------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Media content to render (icon, avatar, etc.) |
| `variant` | `EmptyStateMediaVariant` | `'default'` | Media visual treatment |
| `className` | `string` | - | Additional CSS classes for the media container |
| `background` | `React.ReactNode` | - | Background layer behind the icon media circle. `undefined` renders the theme-aware default for the `icon` variant; custom node replaces it; `null` removes it |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### EmptyStateMediaVariant
| type | description |
| --------------------- | -------------------------------------------------------------------------- |
| `'default' \| 'icon'` | `default` renders media as-is. `icon` wraps it in a circular muted surface |
### EmptyState.MediaBackground
Absolute-fill container rendered behind the icon media circle. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### EmptyState.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Title text content |
| `className` | `string` | - | Additional CSS classes for the title text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### EmptyState.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Description text content |
| `className` | `string` | - | Additional CSS classes for the description text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### EmptyState.Content
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Action content rendered below the header |
| `className` | `string` | - | Additional CSS classes for the action container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
# FlipCard
**Category**: native
**URL**: https://heroui.pro/docs/native/components/flip-card
> A pressable card that flips between a front and back face with a spring-driven 3D rotation.
## Import
```tsx
import { FlipCard } from 'heroui-native-pro';
```
## Anatomy
```tsx
......
```
* **FlipCard**: Pressable root container that drives the flip. Owns the shared spring progress, cascades `disable-all` to animated descendants, and supports controlled (`isFlipped` + `onFlipChange`) and uncontrolled (`defaultFlipped`) usage. Tapping toggles the flip unless `isPressDisabled` is set.
* **FlipCard.Front**: Face visible at rest (progress 0). Rotates from `0deg` to `180deg` as the card flips and hides mid-flip via `backfaceVisibility`. Removed from the accessibility tree and excluded from hit testing once the card is flipped.
* **FlipCard.Back**: Face revealed when flipped (progress 1). Positioned absolutely over the front face and rotates from `180deg` to `360deg`. Removed from the accessibility tree and excluded from hit testing while the front is visible, so hidden interactive content (e.g. a button on the back) cannot intercept touches.
## Usage
### Basic usage
Compose a tap-to-flip card with a front and a back face. The card animates with a spring on every tap.
```tsx
......
```
### Vertical direction
Flip around the X axis (top-over-bottom) with `direction="vertical"`.
```tsx
......
```
### Reverse rotation
Spin the opposite way around the chosen axis with `rotation="reverse"`.
```tsx
......
```
### Controlled
Drive the flip externally with `isFlipped` and `onFlipChange`. Set `isPressDisabled` when the card should not toggle on tap.
```tsx
const [isFlipped, setIsFlipped] = useState(false);
......;
```
### Custom spring
Customize the flip spring through the `animation` prop.
```tsx
......
```
## Example
```tsx
import { LinearGradient } from 'expo-linear-gradient';
import { Button } from 'heroui-native';
import { FlipCard } from 'heroui-native-pro';
import { Image, StyleSheet, Text, View } from 'react-native';
export default function DestinationFlipCard() {
return (
Kyoto Getaway
5 nights · Tap for trip details
Kyoto Getaway
$1,240
);
}
```
## API Reference
### FlipCard
| prop | type | default | description |
| ------------------- | ------------------------------ | -------------- | ---------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound parts to render inside the card (typically `FlipCard.Front` and `FlipCard.Back`) |
| `direction` | `FlipCardDirection` | `"horizontal"` | Axis around which the card flips (`"horizontal"` = rotateY, `"vertical"` = rotateX) |
| `rotation` | `FlipCardRotation` | `"normal"` | Spin direction around the chosen axis. `"reverse"` negates the rotation range so the card spins back |
| `isFlipped` | `boolean` | - | Whether the card shows its back face (controlled mode) |
| `defaultFlipped` | `boolean` | `false` | Default flipped state for uncontrolled mode |
| `isPressDisabled` | `boolean` | `false` | When `true`, tapping the card does not toggle the flip. Use to drive the flip externally |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onFlipChange` | `(isFlipped: boolean) => void` | - | Callback fired when the flipped state changes |
| `animation` | `FlipCardRootAnimation` | - | Animation configuration for the flip (spring config and `disable-all` cascade) |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### FlipCardDirection
Axis around which the card flips.
* `"horizontal"`: Rotates around the Y axis (left/right flip)
* `"vertical"`: Rotates around the X axis (top/bottom flip)
#### FlipCardRotation
Spin direction of the flip around the chosen axis. Follows the CSS `animation-direction` naming convention.
* `"normal"`: Rotates towards positive angles (front `0deg` to `180deg`)
* `"reverse"`: Rotates the opposite way (front `0deg` to `-180deg`)
#### FlipCardRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations (flip snaps instantly; children can still animate)
* `"disable-all"`: Disable all animations including children (cascades down through `AnimationSettingsProvider`)
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------- | ---------------- | ------- | ------------------------------------------ |
| `progress` | `AnimationValue` | - | Configuration for the flip progress spring |
##### progress
| prop | type | default | description |
| -------------- | ------------------ | ------------------------------------------- | -------------------------------------------------------------------- |
| `springConfig` | `WithSpringConfig` | `{ mass: 1.2, stiffness: 60, damping: 12 }` | Spring configuration driving the flip progress (0 = front, 1 = back) |
### FlipCard.Front
| prop | type | default | description |
| ----------------------- | ----------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Content rendered on the front face |
| `className` | `string` | - | Additional CSS classes for the front face |
| `animation` | `FlipCardFaceAnimation` | - | Animation configuration for the front face |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles are active. When `false`, the animated flip transform is removed from the face |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### FlipCardFaceAnimation
Animation configuration for a face. Can be:
* `false` or `"disabled"`: Snap between resting and flipped rotation without interpolating against the shared flip progress
* `undefined`: Use default animations
### FlipCard.Back
| prop | type | default | description |
| ----------------------- | ----------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Content rendered on the back face |
| `className` | `string` | - | Additional CSS classes for the back face |
| `animation` | `FlipCardFaceAnimation` | - | Animation configuration for the back face |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles are active. When `false`, the animated flip transform is removed from the face |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
## Hooks
### useFlipCard
Hook to access the flip card state context. Must be used within a `FlipCard` component.
```tsx
import { useFlipCard } from 'heroui-native-pro';
const { isFlipped, direction, rotation, toggle } = useFlipCard();
```
#### Returns
| property | type | description |
| ----------- | ------------------- | ---------------------------------------------------- |
| `isFlipped` | `boolean` | Whether the card currently shows its back face |
| `direction` | `FlipCardDirection` | Axis around which the card flips |
| `rotation` | `FlipCardRotation` | Spin direction of the flip around the chosen axis |
| `toggle` | `() => void` | Toggles the flipped state (respects controlled mode) |
### useFlipCardAnimation
Hook to access the shared flip progress. Use it to build custom progress-driven animated styles inside compound parts. Must be used within a `FlipCard` component.
```tsx
import { useFlipCardAnimation } from 'heroui-native-pro';
const { progress } = useFlipCardAnimation();
```
#### Returns
| property | type | description |
| ---------- | --------------------- | ---------------------------------------------------- |
| `progress` | `SharedValue` | Animated flip progress (0 = front visible, 1 = back) |
# Table
**Category**: native
**URL**: https://heroui.pro/docs/native/components/table
> A data table for structured tabular content with row selection, controlled sorting, and an opt-in virtualized body.
## Import
```tsx
import { Table } from 'heroui-native-pro';
```
## Anatomy
```tsx
.........
```
* **Table**: Root shell. Owns the visual variant, selection state, and sort descriptor. Cascades `disable-all` to animated descendants. The table never reorders data itself.
* **Table.Background**: Absolute-fill layer behind the shell. With no children, the active library theme decides the content (glass renders a blur layer). Mounted for the primary variant only; replaceable via the `background` prop on Table.
* **Table.ScrollContainer**: Horizontal `ScrollView` so wide tables scroll while the shell and footer keep the available width.
* **Table.Content**: Vertical column hosting the header row and the body. Grows to fill the scroll content width.
* **Table.Header**: Header row hosting `Table.Column` and optionally `Table.SelectAllCell` parts. Injects column positions so body cells align with their columns.
* **Table.Column**: Header cell. Declares the column width behavior (`width`, or `flex` + `minWidth`). With `allowsSorting`, pressing it toggles the sort descriptor and an animated chevron reflects the direction.
* **Table.Body**: Body container. Renders static `Table.Row` children, an `items` collection through a render function, or a virtualized `FlatList`. Shows `renderEmptyState` when there are no rows.
* **Table.Row**: Body row. Pressing it toggles selection when `selectionMode` is not `"none"`. `disabledKeys` and `isDisabled` block interaction and dim the row.
* **Table.Cell**: Body cell. Resolves its width from the header column at the same position. Plain string/number children are wrapped in a styled `Text`.
* **Table.SelectAllCell**: Header checkbox cell for `selectionMode="multiple"` tables. Registers a fixed-width selection column.
* **Table.SelectionCell**: Row checkbox cell bound to the row's selection state. Place it at the same position as `Table.SelectAllCell`.
* **Table.Footer**: Row below the table content, outside the horizontal scroll area, for load-more actions or summaries.
## Usage
### Basic usage
Compose a header of columns and a body of rows. Wrap the content in `Table.ScrollContainer` so wide tables can scroll horizontally.
```tsx
```
### Single selection
Set `selectionMode="single"`. Pressing a row selects it. Checkbox cells are not required.
```tsx
...
```
### Column widths
Columns are flexible (`flex: 1`) by default. Fixed and minimum widths push wide tables into horizontal scrolling.
```tsx
NameRoleNotes
```
### Empty state
Pass `renderEmptyState` to show centered content when the body has no rows.
```tsx
...} />
```
### Virtualized body
For large collections, render rows through a `FlatList`. Requires `items` with the render function form of `children` and a bounded height on the body.
```tsx
item.id}
>
{(item) => (
{item.name}
)}
```
### Footer
`Table.Footer` sits outside the horizontal scroll area. Compose load-more actions or summary content inside it.
```tsx
......
```
## Example
```tsx
import { Chip } from 'heroui-native';
import { Table } from 'heroui-native-pro';
import { View } from 'react-native';
const MEMBERS = [
{
id: '1',
name: 'Ava Thompson',
role: 'Design',
status: 'Active',
statusColor: 'success' as const,
},
{
id: '2',
name: 'Liam Nguyen',
role: 'Engineering',
status: 'Paused',
statusColor: 'warning' as const,
},
{
id: '3',
name: 'Maya Patel',
role: 'Product',
status: 'Active',
statusColor: 'success' as const,
},
];
export default function TableExample() {
return (
);
}
```
## API Reference
### Table
| prop | type | default | description |
| ------------------------ | ------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound parts rendered inside the table shell |
| `variant` | `TableVariant` | `'primary'` | Visual variant |
| `selectionMode` | `TableSelectionMode` | `'none'` | Row selection behavior |
| `selectedKeys` | `Iterable` | - | Controlled selected row keys |
| `defaultSelectedKeys` | `Iterable` | - | Initially selected row keys (uncontrolled) |
| `disabledKeys` | `Iterable` | - | Row keys that cannot be selected or pressed |
| `disallowEmptySelection` | `boolean` | `false` | Prevents deselecting the last selected row |
| `sortDescriptor` | `TableSortDescriptor` | - | Controlled sort descriptor |
| `defaultSortDescriptor` | `TableSortDescriptor` | - | Initial sort descriptor (uncontrolled) |
| `className` | `string` | - | Additional CSS classes for the outer shell |
| `background` | `React.ReactNode` | - | Background layer behind the shell (`undefined` theme default, node replaces, `null` removes) |
| `onSelectionChange` | `(keys: Set) => void` | - | Called with the new set of selected keys |
| `onSortChange` | `(descriptor: TableSortDescriptor) => void` | - | Called with the next descriptor when a sortable column is pressed |
| `animation` | `TableRootAnimation` | - | `"disable-all"` cascades to animated descendants |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### TableVariant
| type | description |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `'primary' \| 'secondary'` | `primary` is a gray shell with the body as an elevated card. `secondary` is a flat root with a rounded header band |
#### TableKey
| type | description |
| ------------------ | ------------------------------------- |
| `string \| number` | Unique identifier for a row or column |
#### TableSelectionMode
| type | description |
| ---------------------------------- | ---------------------- |
| `'none' \| 'single' \| 'multiple'` | Row selection behavior |
#### TableSortDescriptor
| prop | type | description |
| ----------- | -------------------- | ---------------------------------- |
| `column` | `TableKey` | Key of the column driving the sort |
| `direction` | `TableSortDirection` | Direction the column is sorted in |
#### TableSortDirection
| type | description |
| ----------------------------- | ---------------------------------- |
| `'ascending' \| 'descending'` | Direction of an active column sort |
#### TableRootAnimation
Animation configuration for the Table root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down through `AnimationSettingsProvider`)
* `undefined`: Use default animations
### Table.Background
Absolute-fill container rendered behind the table shell. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom background content; theme decides the default when omitted |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Table.ScrollContainer
| prop | type | default | description |
| --------------------------- | ----------------- | ------- | -------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Header and body content (typically `Table.Content`) |
| `className` | `string` | - | Additional CSS classes for the scroll view |
| `contentContainerClassName` | `string` | - | Additional CSS classes for the scroll content container |
| `...ScrollViewProps` | `ScrollViewProps` | - | All standard React Native ScrollView props are supported |
### Table.Content
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Header and body parts |
| `className` | `string` | - | Additional CSS classes for the content column |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Table.Header
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Column parts |
| `className` | `string` | - | Additional CSS classes for the header row |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Table.Column
| prop | type | default | description |
| ----------------------- | --------------------------- | ------- | ------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Column label; plain strings are wrapped in a styled `Text` |
| `id` | `TableKey` | index | Column key used by the sort descriptor |
| `width` | `number` | - | Fixed column width in pixels (wins over `flex`) |
| `minWidth` | `number` | - | Minimum column width in pixels (used with flexible columns) |
| `flex` | `number` | `1` | Flex grow factor when no fixed `width` is set |
| `allowsSorting` | `boolean` | `false` | Pressing the column toggles sorting |
| `className` | `string` | - | Additional CSS classes for the column container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `TableColumnStyles` | - | Inline style overrides for individual slots |
| `indicator` | `React.ReactNode` | - | Custom sort indicator replacing the default chevron |
| `textProps` | `TextProps` | - | Additional props forwarded to the inner label `Text` |
| `animation` | `TableColumnAnimation` | - | Sort indicator animation configuration (rotation / opacity) |
| `isAnimatedStyleActive` | `boolean` | `true` | When `false`, animated styles are not applied to the sort indicator |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### ElementSlots\
| slot | description |
| ----------- | ------------------------------------------- |
| `container` | Column pressable container |
| `label` | Column label text |
| `indicator` | Sort indicator wrapper (animated) |
| `separator` | Trailing vertical separator between columns |
#### styles
| slot | type | description |
| ----------- | ----------- | ----------------------------------------- |
| `container` | `ViewStyle` | Style for the column pressable container |
| `label` | `TextStyle` | Style for the column label text |
| `indicator` | `ViewStyle` | Style for the sort indicator wrapper |
| `separator` | `ViewStyle` | Style for the trailing vertical separator |
The `indicator` slot has animated style properties that cannot be set via `className`: `opacity` (visibility) and `transform` (rotate, for the sort direction flip). To customize, use the `animation` prop. To disable animated styles, set `isAnimatedStyleActive={false}`.
#### TableColumnAnimation
Animation configuration for the column sort indicator. Can be:
* `false` or `"disabled"`: Disable the sort indicator animation
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------- | ---------------- | ------- | -------------------------------------------------------------------------- |
| `rotation` | `AnimationValue` | - | Rotation of the indicator chevron in degrees for `[ascending, descending]` |
| `opacity` | `AnimationValue` | - | Opacity of the indicator for `[hidden, visible]` |
##### rotation
| prop | type | default | description |
| -------------- | ------------------ | ------------------- | ---------------------------------------------------- |
| `value` | `[number, number]` | `[0, 180]` | Rotation values `[ascending, descending]` in degrees |
| `timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Animation timing configuration |
##### opacity
| prop | type | default | description |
| -------------- | ------------------ | ------------------- | ---------------------------------- |
| `value` | `[number, number]` | `[0, 1]` | Opacity values `[hidden, visible]` |
| `timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Animation timing configuration |
### Table.Body
| prop | type | default | description |
| ------------------ | ----------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------ |
| `children` | `React.ReactNode \| (item: TItem, index: number) => React.ReactElement` | - | Static rows, or a render function when `items` is provided |
| `items` | `readonly TItem[]` | - | Dynamic collection rendered through the render function |
| `keyExtractor` | `(item: TItem, index: number) => TableKey` | - | Resolves the row key for an item (required for virtualized select-all) |
| `virtualized` | `boolean` | `false` | Renders rows through a `FlatList`; requires `items` and a bounded height |
| `renderEmptyState` | `() => React.ReactNode` | - | Rendered centered inside the body when there are no rows |
| `className` | `string` | - | Additional CSS classes for the body container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Inline style overrides for individual slots |
| `flatListProps` | `Omit, 'data' \| 'renderItem' \| 'keyExtractor'>` | - | Extra props for the virtualized `FlatList` |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ----------- | ----------------------------------- |
| `container` | Body container |
| `empty` | Empty state wrapper inside the body |
#### styles
| slot | type | description |
| ----------- | ----------- | --------------------------------- |
| `container` | `ViewStyle` | Style for the body container |
| `empty` | `ViewStyle` | Style for the empty state wrapper |
### Table.Row
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Cell parts |
| `id` | `TableKey` | index | Row key used by selection and `disabledKeys` |
| `isDisabled` | `boolean` | `false` | Disables the row regardless of `disabledKeys` |
| `className` | `string` | - | Additional CSS classes for the row |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### Table.Cell
| prop | type | default | description |
| -------------- | ------------------------- | ------- | ---------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Cell content; plain strings are wrapped in a styled `Text` |
| `className` | `string` | - | Additional CSS classes for the cell container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `TableCellStyles` | - | Inline style overrides for individual slots |
| `textProps` | `TextProps` | - | Additional props forwarded to the inner `Text` |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ----------- | -------------------------------------------------------- |
| `container` | Cell container |
| `text` | Cell text (only when children are plain strings/numbers) |
#### styles
| slot | type | description |
| ----------- | ----------- | ---------------------------- |
| `container` | `ViewStyle` | Style for the cell container |
| `text` | `TextStyle` | Style for the cell text |
### Table.SelectAllCell
| prop | type | default | description |
| --------------- | ----------------------------- | ------- | ----------------------------------------------------- |
| `width` | `number` | `48` | Fixed width of the selection column in pixels |
| `className` | `string` | - | Additional CSS classes for the cell container |
| `checkboxProps` | `TableSelectionCheckboxProps` | - | Additional props forwarded to the select-all checkbox |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### TableSelectionCheckboxProps
Props forwarded to the checkbox rendered by `Table.SelectAllCell` and `Table.SelectionCell`. Selection state and change handling are owned by the table. Omits `isSelected`, `onSelectedChange`, and `isDisabled` from `CheckboxProps`.
The table renders the checkboxes at a compact 20pt size with a reduced corner radius. The header select-all checkbox defaults to the `primary` variant; row checkboxes default to `secondary`. Override via `variant`, `className`, or `children`.
### Table.SelectionCell
| prop | type | default | description |
| --------------- | ----------------------------- | ------- | -------------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes for the cell container |
| `checkboxProps` | `TableSelectionCheckboxProps` | - | Additional props forwarded to the row selection checkbox |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Table.Footer
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Footer content |
| `className` | `string` | - | Additional CSS classes for the footer row |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
# Timeline
**Category**: native
**URL**: https://heroui.pro/docs/native/components/timeline
> A composable React Native timeline for mobile activity feeds, event histories, and milestones.
## Import
```tsx
import { Timeline } from 'heroui-native-pro';
```
## Anatomy
```tsx
.........
```
* **Timeline**: Root container. Manages size, density, and alignment context and renders items as a vertical, rail-on-left chronology.
* **Timeline.Item**: A single event row. Owns its marker `status` and optional `align`.
* **Timeline.Leading**: Optional left column, rendered to the left of the rail. Suited to timestamps or short metadata.
* **Timeline.Rail**: Relative wrapper for the marker and connector. Renders `Marker` and `Connector` by default when children are omitted; `Connector` is omitted on item index `0`.
* **Timeline.Marker**: Dot or circle for each item. Tone derives from the item `status`. Accepts icon children.
* **Timeline.Connector**: Static line bridging adjacent markers. Positioned via layout measurements so it spans variable-height items.
* **Timeline.Content**: Container for the item title, description, and any additional content.
* **Timeline.Title**: Title text for an item.
* **Timeline.Description**: Description text for an item.
## Usage
### Basic usage
Compose items with a `Rail` and a `Content` block. Omit `Rail` children to render the default marker and connector.
```tsx
Order placedWe received your order.ProcessingPreparing your items.
```
### Statuses
Set `status` per item to tint its marker. Each item owns its status independently.
```tsx
CreatedGuardrail trippedVerified
```
### Leading column
Add `Timeline.Leading` to place timestamps or metadata to the left of the rail.
```tsx
09:12Feature flag created
```
### Custom marker
Pass icon children to `Timeline.Marker` and keep the default `Connector`.
```tsx
Regional guardrail tripped
```
### Sizes and density
Use `size` to scale markers and text, and `density` to control vertical rhythm.
```tsx
...
```
## Example
```tsx
import { Chip, useThemeColor } from 'heroui-native';
import { Timeline } from 'heroui-native-pro';
import { Text, View } from 'react-native';
import { BellIcon } from './icons/bell';
import { ShieldCheckIcon } from './icons/shield-check';
import { ShieldExclamationIcon } from './icons/shield-exclamation';
const EVENTS = [
{
title: 'Canary rollout started',
description: 'Enabled for 5% of workspaces.',
meta: 'Canary',
metaColor: 'accent',
status: 'current',
time: '09:34',
Icon: BellIcon,
},
{
title: 'Regional guardrail tripped',
description: 'Latency climbed in eu-central-1.',
meta: 'Paused',
metaColor: 'warning',
status: 'warning',
time: '09:51',
Icon: ShieldExclamationIcon,
},
{
title: 'Release checklist verified',
description: 'Rollback owner and dashboard checks are recorded.',
meta: 'Ready',
metaColor: 'success',
status: 'success',
time: '10:42',
Icon: ShieldCheckIcon,
},
] as const;
export default function RolloutTimeline() {
const [accent, warning, success] = useThemeColor([
'accent',
'warning',
'success',
]);
const iconColorByStatus = {
current: accent,
warning,
success,
} as const;
return (
{EVENTS.map((event) => {
const Icon = event.Icon;
return (
{event.time}{event.title}
{event.meta}
{event.description}
);
})}
);
}
```
## API Reference
### Timeline
| prop | type | default | description |
| -------------- | ----------------------- | --------------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Item elements to render inside the timeline |
| `size` | `TimelineSize` | `'md'` | Visual size scale for markers and text |
| `density` | `TimelineDensity` | `'comfortable'` | Vertical rhythm between items |
| `itemAlign` | `TimelineItemAlign` | `'start'` | Default vertical alignment of item content |
| `className` | `string` | - | Additional CSS classes for the root container |
| `animation` | `TimelineRootAnimation` | - | Root animation configuration (disable-all cascade) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### TimelineSize
| value | description |
| ------ | ----------------- |
| `'sm'` | Compact size tier |
| `'md'` | Default size tier |
| `'lg'` | Large size tier |
#### TimelineDensity
| value | description |
| --------------- | ----------------------- |
| `'compact'` | Tighter vertical rhythm |
| `'comfortable'` | Default vertical rhythm |
#### TimelineItemAlign
| value | description |
| ---------- | ----------------------------------- |
| `'start'` | Align content to the top of the row |
| `'center'` | Center content against the marker |
#### TimelineRootAnimation
| value | description |
| --------------- | ------------------------------------------------------ |
| `'disable-all'` | Disable all animations, including animated descendants |
| `undefined` | Use default animations |
### Timeline.Item
| prop | type | default | description |
| -------------- | ------------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Item content (Leading, Rail, Content, etc.) |
| `status` | `TimelineStatus` | `'default'` | Marker tone for this item |
| `align` | `TimelineItemAlign` | inherited | Vertical alignment of this item's content |
| `className` | `string` | - | Additional CSS classes for the item row |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### TimelineStatus
| value | description |
| ----------- | --------------------- |
| `'default'` | Neutral marker |
| `'muted'` | Dimmed neutral marker |
| `'current'` | Accent-toned marker |
| `'success'` | Success-toned marker |
| `'warning'` | Warning-toned marker |
| `'danger'` | Danger-toned marker |
### Timeline.Leading
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Left column content (e.g. timestamps) |
| `className` | `string` | - | Additional CSS classes for the leading container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Timeline.Rail
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom rail content; when omitted, renders `Marker` and `Connector` (except on item 0) |
| `className` | `string` | - | Additional CSS classes for the rail container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Timeline.Marker
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Optional icon content inside the marker |
| `className` | `string` | - | Additional CSS classes for the marker |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Timeline.Connector
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `force` | `boolean` | `false` | Render the connector even on the first item |
| `className` | `string` | - | Additional CSS classes for the connector |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Timeline.Content
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to render (title, description, etc.) |
| `className` | `string` | - | Additional CSS classes for the content container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Timeline.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Title text for the item |
| `className` | `string` | - | Additional CSS classes for the title text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Timeline.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Description text for the item |
| `className` | `string` | - | Additional CSS classes for the description text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useTimeline
Hook to access the timeline root context. Must be used within a `Timeline` component.
```tsx
import { useTimeline } from 'heroui-native-pro';
const { size, density, itemAlign } = useTimeline();
```
#### Returns
| property | type | description |
| -------------------- | ------------------------------------------------------------- | ------------------------------------------ |
| `size` | `TimelineSize` | Current size tier propagated from the root |
| `density` | `TimelineDensity` | Current vertical rhythm |
| `itemAlign` | `TimelineItemAlign` | Default content alignment for items |
| `measurements` | `Record` | Per-item layout measurements |
| `setItemMeasurement` | `(index: number, partial: Partial) => void` | Updates layout measurements for an item |
### useTimelineItem
Hook to access the per-item context. Must be used within a `Timeline.Item` component.
```tsx
import { useTimelineItem } from 'heroui-native-pro';
const { index, isLast, status, align } = useTimelineItem();
```
#### Returns
| property | type | description |
| -------- | ------------------- | ----------------------------------- |
| `index` | `number` | Zero-based index of the item |
| `isLast` | `boolean` | Whether this is the last item |
| `status` | `TimelineStatus` | Marker tone for the item |
| `align` | `TimelineItemAlign` | Resolved vertical content alignment |
# Widget
**Category**: native
**URL**: https://heroui.pro/docs/native/components/widget
> A dashboard container that pairs an optional header and footer with an elevated content card for charts, tables, and KPIs.
## Import
```tsx
import { Widget } from 'heroui-native-pro';
```
## Anatomy
```tsx
...............
```
* **Widget**: Root container. Renders the outer surface (`bg-surface-secondary`, `rounded-2xl`) with internal padding, and cascades `disable-all` to animated descendants. Sub-components are fully optional.
* **Widget.Header**: Horizontal row with `space-between` justification. Pairs `Widget.Title` (and optional `Widget.Description`) with an inline `Widget.Legend`.
* **Widget.Title**: Primary widget label rendered with `accessibilityRole="header"`.
* **Widget.Description**: Secondary muted text. Use under the title as a hint or inside `Widget.Footer` as a summary line.
* **Widget.Content**: Elevated inner card (`bg-surface`, `rounded-xl`, `shadow-surface`) hosting the widget's payload (chart, table, KPI block, etc.).
* **Widget.Footer**: Optional bottom row for muted summary text or action chips.
* **Widget.Legend**: Inline container for one or more `Widget.LegendItem`s, typically placed inside the header next to the title.
* **Widget.LegendItem**: Single colored-dot + label entry. Accepts `colorClassName` (preferred) or `color` (inline color string) to drive the dot.
## Usage
### Basic usage
Compose the widget with a header (title + legend) and an elevated content card.
```tsx
Tokens Over TimeInputOutput...
```
### Title and description
Stack a `Widget.Description` beneath the title for a hint line.
```tsx
RequestsLast 30 days...
```
### With footer
Add a `Widget.Footer` for muted summary text or actions below the content card.
```tsx
Tokens Over Time...Updated 2 minutes ago
```
### Legend with theme colors
Use `colorClassName` to pull theme tokens through the standard className pipeline.
```tsx
OrganicPaid Ads
```
### Legend with custom colors
Use `color` for one-off color strings (hex, `rgb(...)`, resolved theme value).
```tsx
SuccessErrors
```
## Example
```tsx
import { LineChart, Widget } from 'heroui-native-pro';
import { View } from 'react-native';
const TOKENS_DATA = [
{ date: '09-01', input: 35000, output: 22000 },
{ date: '09-02', input: 80000, output: 35000 },
{ date: '09-03', input: 130000, output: 48000 },
];
const formatCompactNumber = (value: number) =>
value >= 1000 ? `${(value / 1000).toFixed(0)}k` : `${value}`;
export default function WidgetExample() {
return (
Tokens Over Time
Input
Output
{({ points }) => (
<>
>
)}
);
}
```
## API Reference
### Widget
| prop | type | default | description |
| -------------- | --------------------- | ------- | ------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Compound parts to render inside the widget shell |
| `className` | `string` | - | Additional CSS classes for the outer shell |
| `animation` | `WidgetRootAnimation` | - | Animation configuration for the widget root (cascades to animated descendants) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### WidgetRootAnimation
Animation configuration for the Widget root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down through `AnimationSettingsProvider`)
* `undefined`: Use default animations
### Widget.Header
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Header content (title, description, legend, etc.) |
| `className` | `string` | - | Additional CSS classes for the header row |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Widget.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Title text content |
| `className` | `string` | - | Additional CSS classes for the title text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Widget.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Description text content |
| `className` | `string` | - | Additional CSS classes for the description text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Widget.Content
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content rendered inside the elevated card |
| `className` | `string` | - | Additional CSS classes for the content card |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Widget.Footer
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Footer content |
| `className` | `string` | - | Additional CSS classes for the footer row |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Widget.Legend
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Legend entries |
| `className` | `string` | - | Additional CSS classes for the legend wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Widget.LegendItem
| prop | type | default | description |
| ---------------- | ------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Label text. String/number children are wrapped in a `Text` automatically |
| `color` | `string` | - | Color applied to the dot via inline `backgroundColor`. Wins over `colorClassName` when both are set |
| `colorClassName` | `string` | - | Tailwind background class for the dot (e.g. `"bg-chart-3"`). Preferred over `color` for theme tokens |
| `className` | `string` | - | Additional CSS classes for the wrapper slot |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `WidgetLegendItemStyles` | - | Inline style overrides for individual slots |
| `textProps` | `TextProps` | - | Additional props forwarded to the inner label `Text` element |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| --------- | ---------------------------------------- |
| `wrapper` | Outer flex row holding the dot and label |
| `dot` | Color indicator dot |
| `label` | Legend entry label text |
#### styles
| slot | type | description |
| --------- | ----------- | ------------------------------------- |
| `wrapper` | `ViewStyle` | Style for the outer flex row wrapper |
| `dot` | `ViewStyle` | Style for the color indicator dot |
| `label` | `TextStyle` | Style for the legend entry label text |
# Agenda
**Category**: native
**URL**: https://heroui.pro/docs/native/components/agenda
> A full calendar surface for mobile: a collapsible month calendar on top of a horizontally paged day / week / month body with draggable, resizable events.
> `Agenda` uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) for all date values (`CalendarDate` for days, `CalendarDateTime` for event start/end). Event drag-and-drop requires the optional peer dependency [`react-native-reanimated-dnd`](https://reanimated-dnd-docs.vercel.app); without it, events render but cannot be moved or resized.
## Import
```tsx
import { Agenda, useAgenda } from 'heroui-native-pro';
```
## Anatomy
State is built by the `useAgenda(options)` hook and spread into the root:
```tsx
const agenda = useAgenda({
events,
defaultView: 'week',
});
{(date) => }
{({ year, isSelected }) => (
)}
;
```
Every level has a default: a childless `` renders the full composition above; a childless `Agenda.Body` renders the whole default page template; a childless `Agenda.TimeGrid` renders the day columns and the current time indicator; a childless `Agenda.DayColumns` renders default `Agenda.Event` cards; a childless `Agenda.Calendar` renders the prewired calendar.
* **Agenda**: Root container. Receives the `useAgenda(options)` state as spread props and wraps everything in a `SplitView` whose snap points derive from the measured header content (collapsed week row / fully expanded calendar).
* **Agenda.Header**: Top SplitView pane. Measures its natural content height, so anything placed under the calendar automatically expands the snap points.
* **Agenda.Calendar**: Month calendar (with year picker) bound to the agenda date. Collapses to the week row containing the selected date at the minimum snap point. Children compose the calendar anatomy from raw `Calendar.*` parts plus the two Agenda-measured wrappers below.
* **Agenda.CalendarHeader**: Measured wrapper around `Calendar.Header`; children compose the header content (year picker trigger, nav buttons, Today button).
* **Agenda.CalendarGrid**: Measured, collapse-animated month grid wrapping `Calendar.Grid`. Optional render-function children customize day cells via `Calendar.GridBody`'s own API; the default cell shows an event-coverage indicator.
* **Agenda.Heading**: Localized month + year title for the active date.
* **Agenda.TodayButton**: Compact outline button that jumps to today and collapses the header.
* **Agenda.NavButton**: Previous / next agenda navigation (`slot="previous" | "next"`); steps a day, week, or month depending on the current view and collapses the header.
* **Agenda.DragArea** / **Agenda.DragHandle**: SplitView drag region and pill between the header and the body.
* **Agenda.Body**: Horizontally paged day / week / month area inside the bottom SplitView pane, kept in sync with the calendar. Children act as the page template, rendered once per pager page under the page context; all collection parts self-gate by the page's view, so one template covers every view.
* **Agenda.WeekHeader**: Weekday letters row above the time grid on week pages (`showDates` switches to full names + tappable date pills).
* **Agenda.AllDaySection**: Packed all-day bars above the time grid. Children act as the per-event template (event available via `useAgendaEvent()`).
* **Agenda.TimeGrid**: Vertically scrollable hour grid with the time gutter, hour lines, and drag drop-guides. Scroll offset is synchronized across pages. Children compose the grid content.
* **Agenda.DayColumns**: One column per page day. Children act as the per-event template; the default is `Agenda.Event`.
* **Agenda.Event**: The positioned, draggable, resizable event card. Reads the event from the template context (or an explicit `event` prop). Children customize the card content; the default renders the color tint, accent bar, title, and time.
* **Agenda.EventTitle** / **Agenda.EventTime**: Text parts bound to the template context event.
* **Agenda.CurrentTimeIndicator**: Live time badge + line + notch, rendered only on day/week pages containing today.
* **Agenda.MonthGrid**: Six week rows with multi-day spanning bars and per-cell event chips (`maxEventsPerCell`, `moreLabel`). Children act as the per-event chip content template.
* **Agenda.ViewSelector**: Floating day / week / month selector built on `Segment`, absolutely positioned at the bottom-center by default. Exposes `Group` / `Indicator` / `Item` / `Label` / `Separator` for custom compositions.
## Usage
### Basic Usage
The only required option is `events`. The Agenda never mutates the array — apply move/resize intents back into your state. Event start/end values are `CalendarDateTime` objects from `@internationalized/date`.
```tsx
import type { CalendarDateTime } from '@internationalized/date';
import { Agenda, useAgenda, type AgendaEvent } from 'heroui-native-pro';
const [events, setEvents] = useState(initialEvents);
const applyChange = (id: string, start: CalendarDateTime, end: CalendarDateTime) => {
setEvents((prev) =>
prev.map((event) => (event.id === id ? { ...event, start, end } : event))
);
};
const agenda = useAgenda({
events,
onEventMove: applyChange,
onEventResize: applyChange,
});
;
```
The hook return is also your window into the agenda from outside the component tree: read `agenda.heading`, `agenda.selectedEventId`, or `agenda.visibleDays`, and drive it with `agenda.setView`, `agenda.setDate`, or `agenda.goToToday` from any surrounding UI.
### Event Model
Events are plain objects built with `@internationalized/date` helpers. `color` tints the event chip, all-day events render as bars (day/week) or spanning month rows, read-only events cannot be moved or resized, and `"unconfirmed"` renders a dashed border. Dates follow the half-open `[start, end)` convention: an event ending at midnight does not cover the following day, so a one-day all-day event spans from midnight to the next day's midnight.
```tsx
import {
getLocalTimeZone,
Time,
toCalendarDateTime,
today,
} from '@internationalized/date';
import type { AgendaEvent } from 'heroui-native-pro';
const event: AgendaEvent = {
id: 'standup',
title: 'Daily standup',
start: toCalendarDateTime(today(getLocalTimeZone()), new Time(10, 30)),
end: toCalendarDateTime(today(getLocalTimeZone()), new Time(11, 0)),
color: '#3b82f6',
isAllDay: false,
isReadOnly: false,
status: 'confirmed',
};
```
### Controlled View and Date
Control the view mode and active date externally with `view` / `onViewChange` and `date` / `onDateChange`. The active date is a `CalendarDate` from `@internationalized/date`.
```tsx
import { getLocalTimeZone, today } from '@internationalized/date';
import { useAgenda, type AgendaView } from 'heroui-native-pro';
const [view, setView] = useState('week');
const [date, setDate] = useState(today(getLocalTimeZone()));
const agenda = useAgenda({
events,
view,
onViewChange: setView,
date,
onDateChange: setDate,
});
```
### Event Press
By default, pressing an event toggles the internal selection (`selectedEventId` / `onEventSelect`). Provide `onEventPress` to replace the toggle with an app-level action such as opening a details screen.
```tsx
const agenda = useAgenda({
events,
onEventPress: (event) => router.push(`/events/${event.id}`),
});
```
### Custom Event Templates
Collection parts render their own data; their children act as a per-item template rendered with the event available through context. For fully bespoke items, read the event inside your own component with `useAgendaEvent()`.
```tsx
```
### Custom Calendar Composition
`Agenda.Calendar` composes like `DatePicker.Calendar`: children are raw `Calendar.*` parts, with `Agenda.CalendarHeader` and `Agenda.CalendarGrid` carrying the measurement / collapse machinery.
```tsx
{(date) => }
{({ year, isSelected }) => (
)}
```
### Header Content Below the Calendar
`Agenda.Header` measures its content, so extra content expands the snap points dynamically.
```tsx
```
### Time Grid Configuration
Configure the rendered hour range, slot geometry, week start, and locale through the hook options. `startHour` is inclusive, `endHour` is exclusive.
```tsx
const agenda = useAgenda({
events,
startHour: 6,
endHour: 22,
slotHeight: 80,
slotDuration: 60,
firstDayOfWeek: 'mon',
locale: 'en-GB',
});
```
### Fade Gradients
Two gradient overlays soften scrolling edges: the time grid's top fade (content scrolling under the grid's top edge) and the body's bottom fade (above the floating view selector). Each owner exposes three props — visibility, color, and height. Hide them with `showBottomFade={false}` (body) / `showTopFade={false}` (time grid). The colors default to the theme surface color, matching the body background.
```tsx
```
## Example
```tsx
import {
getLocalTimeZone,
Time,
toCalendarDateTime,
today,
type CalendarDate,
type CalendarDateTime,
} from '@internationalized/date';
import { Agenda, useAgenda, type AgendaEvent } from 'heroui-native-pro';
import { useCallback, useState } from 'react';
import { View } from 'react-native';
const TIME_ZONE = getLocalTimeZone();
const at = (day: CalendarDate, hour: number, minute = 0): CalendarDateTime =>
toCalendarDateTime(day, new Time(hour, minute));
const buildEvents = (): AgendaEvent[] => {
const base = today(TIME_ZONE);
return [
{
id: 'standup',
title: 'Daily standup',
start: at(base, 10, 30),
end: at(base, 11, 30),
color: '#3b82f6',
},
{
id: 'interview',
title: 'Interview: RN engineer',
start: at(base, 10, 0),
end: at(base, 12, 0),
color: '#f59e0b',
},
{
id: 'locked',
title: 'All-hands (read-only)',
start: at(base, 8, 0),
end: at(base, 9, 0),
isReadOnly: true,
status: 'unconfirmed',
color: '#f43f5e',
},
{
id: 'conference',
title: 'AppJS Conference',
start: at(base, 0, 0),
end: at(base.add({ days: 2 }), 0, 0),
isAllDay: true,
color: '#8b5cf6',
},
];
};
export default function AgendaExample() {
const [events, setEvents] = useState(buildEvents);
const applyEventChange = useCallback(
(id: string, start: CalendarDateTime, end: CalendarDateTime) => {
setEvents((prev) =>
prev.map((event) => (event.id === id ? { ...event, start, end } : event))
);
},
[]
);
const agenda = useAgenda({
events,
onEventMove: applyEventChange,
onEventResize: applyEventChange,
});
return (
);
}
```
## API Reference
### Agenda
The root also accepts every property of `UseAgendaReturn` — spread the `useAgenda(options)` result into it: ``.
| prop | type | default | description |
| ---------------------- | -------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Compound children; when omitted, renders the default composition (Header + Calendar, DragArea, Body, ViewSelector) |
| `snapPoints` | `readonly number[]` | measured | SplitView snap points; derived from the measured header content when omitted (collapsed week row / expanded) |
| `minHeight` | `number` | measured | Minimum top section height, forwarded to `SplitView` |
| `maxHeight` | `number` | measured | Maximum top section height, forwarded to `SplitView` |
| `snapIndex` | `number` | - | Controlled snap index |
| `defaultSnapIndex` | `number` | `0` | Default snap index for uncontrolled usage; `0` shows the collapsed week row, `1` the full calendar |
| `skipInitialAnimation` | `boolean` | `true` | Applies the first snap instantly instead of animating the divider into place on mount |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onSnapIndexChange` | `(index: number) => void` | - | Called when the snap index changes |
| `onSnap` | `(snapIndex: number, topHeightPx: number) => void` | - | Called after a snap completes with the resolved index and top height in px |
| `animation` | `AgendaRootAnimation` | - | Root animation configuration, forwarded to the underlying `SplitView` |
| `...UseAgendaReturn` | `UseAgendaReturn` | - | Agenda state built by `useAgenda(options)` (see Hooks) |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AgendaRootAnimation
Alias of `SplitViewRootAnimation`. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `snapSpringConfig` | `WithSpringConfig` | `{ damping: 25, stiffness: 300, mass: 0.8, overshootClamping: false, restDisplacementThreshold: 0.01, restSpeedThreshold: 0.01 }` | Spring used when snapping the top section after drag release or `snapTo` |
### Agenda.Header
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Header content (Agenda.Calendar, custom content) |
| `className` | `string` | - | Additional CSS classes for the header wrapper. `height` is driven by the SplitView animation and cannot be set |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Agenda.Calendar
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Calendar anatomy; defaults to `CalendarHeader` + `CalendarGrid` + year picker overlay |
| `className` | `string` | - | Additional CSS classes for the calendar wrapper. The grid body's `transform` (translateY) is animated and cannot be set |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Agenda.CalendarHeader
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Header content; defaults to a year-picker trigger with month navigation and a Today button |
| `className` | `string` | - | Additional CSS classes for the measured header wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Agenda.CalendarGrid
Day cells are `Calendar.Cell` parts, so they expose the Calendar cell data attributes (`data-today`, `data-selected`, `data-outside-month`, ...) — see the Calendar documentation.
| prop | type | default | description |
| -------------- | -------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
| `children` | `(date: CalendarDate) => ReactElement` | - | Day cell renderer matching `Calendar.GridBody`'s API; defaults to `Calendar.Cell` with an event-coverage indicator |
| `className` | `string` | - | Additional CSS classes for the grid container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Agenda.Heading
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom heading content; defaults to the localized month + year of the active date |
| `className` | `string` | - | Additional CSS classes for the heading text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Agenda.TodayButton
| prop | type | default | description |
| ----------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom button content; defaults to a "Today" label |
| `className` | `string` | - | Additional CSS classes for the button |
### Agenda.NavButton
| prop | type | default | description |
| ------------------- | ---------------------- | ------- | ------------------------------------------------------- |
| `slot` | `'previous' \| 'next'` | - | Navigation direction (required) |
| `children` | `React.ReactNode` | - | Custom icon/content; defaults to a chevron |
| `className` | `string` | - | Additional CSS classes for the pressable |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### Agenda.DragArea
Passthrough of `SplitView.DragArea`.
| prop | type | default | description |
| --------------------------- | ------------------------ | ------- | -------------------------------------------- |
| `...SplitViewDragAreaProps` | `SplitViewDragAreaProps` | - | All `SplitView.DragArea` props are supported |
### Agenda.DragHandle
Passthrough of `SplitView.DragHandle`.
| prop | type | default | description |
| ----------------------------- | -------------------------- | ------- | ---------------------------------------------- |
| `...SplitViewDragHandleProps` | `SplitViewDragHandleProps` | - | All `SplitView.DragHandle` props are supported |
### Agenda.Body
| prop | type | default | description |
| ------------------ | ----------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Page template rendered per pager page; defaults to `WeekHeader` + `AllDaySection` + `TimeGrid` + `MonthGrid` |
| `showBottomFade` | `boolean` | `true` | Whether the bottom fade gradient (above the floating view selector) is rendered |
| `bottomFadeColor` | `string` | theme surface color | Color the bottom fade dissolves from |
| `bottomFadeHeight` | `number` | `64` | Height in px of the bottom fade |
| `className` | `string` | - | Additional CSS classes for the body container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Agenda.WeekHeader
Renders only on multi-day (week) pages.
| prop | type | default | description |
| -------------- | ------------------------------------- | ------- | ------------------------------------------------------------------------------- |
| `showDates` | `boolean` | `false` | Renders full weekday names with tappable date pills instead of slim day letters |
| `className` | `string` | - | Additional CSS classes for the week header container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `object` | - | Additional native styles for individual slots |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ----------- | ------------------------------------- |
| `container` | Week header row container |
| `cell` | One weekday cell |
| `day` | Weekday letter / name text |
| `date` | Tappable date pill (with `showDates`) |
#### styles
| slot | type | description |
| ----------- | ----------- | ----------------------------------- |
| `container` | `ViewStyle` | Style for the week header container |
| `cell` | `ViewStyle` | Style for one weekday cell |
| `day` | `TextStyle` | Style for the weekday text |
| `date` | `TextStyle` | Style for the date pill text |
#### Data Attributes
Set on the `day` and `date` slots; target them with `data-[...]` Tailwind variants via `classNames` (e.g. `classNames={{ date: 'data-[today=true]:bg-danger' }}`).
| attribute | values | description |
| --------------- | --------- | --------------------------------------------------------------------------------- |
| `data-today` | `boolean` | Whether the column's date is today (`day` and `date` slots) |
| `data-selected` | `boolean` | Whether the column's date is the selected agenda date and not today (`date` slot) |
### Agenda.AllDaySection
Renders only on day/week pages with at least one all-day event.
| prop | type | default | description |
| -------------- | ---------------------------------------- | ------- | ----------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Per-event content template (event via `useAgendaEvent()`); defaults to tint + title |
| `className` | `string` | - | Additional CSS classes for the section container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `object` | - | Additional native styles for individual slots |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ------------ | ------------------------------------------- |
| `container` | All-day section container |
| `event` | One packed all-day event bar |
| `eventTitle` | Title text inside the default bar content |
| `tint` | Absolute-fill color tint behind the content |
#### styles
| slot | type | description |
| ------------ | ----------- | -------------------------------- |
| `container` | `ViewStyle` | Style for the section container |
| `event` | `ViewStyle` | Style for one all-day event bar |
| `eventTitle` | `TextStyle` | Style for the default title text |
### Agenda.TimeGrid
Renders only on day/week pages. The top fade is customized via the `showTopFade` / `topFadeColor` / `topFadeHeight` props instead of a slot.
| prop | type | default | description |
| --------------- | ------------------------------------------------------- | ------------------- | ------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Grid content; defaults to the current-time indicator and the day columns |
| `showTopFade` | `boolean` | `true` | Whether the top fade gradient is rendered |
| `topFadeColor` | `string` | theme surface color | Color the top fade dissolves from |
| `topFadeHeight` | `number` | `36` | Height in px of the top fade |
| `className` | `string` | - | Additional CSS classes for the grid viewport wrapper |
| `classNames` | `ElementSlots>` | - | Additional CSS classes for individual slots |
| `styles` | `object` | - | Additional native styles for individual slots |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `container` | Grid viewport wrapper |
| `scroll` | Vertical scroll view |
| `content` | Scrollable content container |
| `gutterLabel` | Hour label in the left time gutter |
| `hourLine` | Horizontal hour line |
| `dragGuide` | Drop guide row shown while dragging/resizing. `opacity` and `transform` (translateY) are animated and cannot be set |
| `dragGuideLine` | Drop guide leading line container |
| `dragGuideLineDash` | Dashed border of the drop guide line |
| `dragGuideLabel` | Snapped time label on the rail |
#### styles
| slot | type | description |
| ------------------- | ----------- | --------------------------------------- |
| `container` | `ViewStyle` | Style for the grid viewport wrapper |
| `scroll` | `ViewStyle` | Style for the vertical scroll view |
| `content` | `ViewStyle` | Style for the scrollable content |
| `gutterLabel` | `TextStyle` | Style for the hour gutter labels |
| `hourLine` | `ViewStyle` | Style for the hour lines |
| `dragGuide` | `ViewStyle` | Style for the drop guide row |
| `dragGuideLine` | `ViewStyle` | Style for the drop guide line container |
| `dragGuideLineDash` | `ViewStyle` | Style for the dashed guide border |
| `dragGuideLabel` | `TextStyle` | Style for the rail time label |
### Agenda.DayColumns
| prop | type | default | description |
| -------------- | --------------------------------------------------- | ------- | ---------------------------------------------------- |
| `children` | `React.ReactNode` | - | Per-event template; defaults to `Agenda.Event` |
| `className` | `string` | - | Additional CSS classes for the columns row container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Additional native styles for individual slots |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ------------- | --------------------------------- |
| `container` | Columns row container |
| `column` | One day column |
| `eventsLayer` | Absolute-fill fading events layer |
### Agenda.Event
| prop | type | default | description |
| ----------------------- | -------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
| `event` | `AgendaEvent` | - | The event to render; defaults to the template context event (`useAgendaEvent`) |
| `children` | `React.ReactNode` | - | Card content; defaults to color tint + accent bar + title + time |
| `isAnimatedStyleActive` | `boolean` | `true` | When `false`, the decorative scale style is not applied (functional drag/resize styles stay active) |
| `className` | `string` | - | Additional CSS classes for the card container. `transform` (scale) and `height` are animated and cannot be set |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `object` | - | Additional native styles for individual slots |
| `animation` | `AgendaEventAnimation` | - | Press / drag scale animation for the card |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| --------------- | ----------------------------------------------------------------------------------------------------------------- |
| `container` | Positioned card container |
| `content` | Inner content wrapper |
| `tint` | Color tint overlay derived from `event.color` |
| `accentBar` | Left accent bar |
| `title` | Title text of the default content |
| `time` | Time text of the default content |
| `resizeHandle` | Invisible resize hit area at the bottom of the card |
| `resizeGrabber` | Visible resize grabber pill. `transform` (scale) is animated while the resize gesture is active and cannot be set |
#### styles
| slot | type | description |
| --------------- | ----------- | -------------------------------- |
| `container` | `ViewStyle` | Style for the card container |
| `content` | `ViewStyle` | Style for the content wrapper |
| `tint` | `ViewStyle` | Style for the color tint overlay |
| `accentBar` | `ViewStyle` | Style for the accent bar |
| `title` | `TextStyle` | Style for the title text |
| `time` | `TextStyle` | Style for the time text |
| `resizeHandle` | `ViewStyle` | Style for the resize hit area |
| `resizeGrabber` | `ViewStyle` | Style for the resize grabber |
#### Data Attributes
Set on the `container` and `resizeGrabber` slots. The default styles use them for the selected accent border (`data-[selected=true]:border-accent`), the unconfirmed dashed border (`data-[unconfirmed=true]:border-dashed`), and the selected grabber fill (`data-[selected=true]:bg-accent`).
| attribute | values | description |
| ------------------ | --------- | --------------------------------------------------------------------- |
| `data-selected` | `boolean` | Whether the event is selected (`container` and `resizeGrabber` slots) |
| `data-unconfirmed` | `boolean` | Whether the event's `status` is `'unconfirmed'` (`container` slot) |
#### AgendaEventAnimation
Animation configuration for the press / drag scale feedback. Can be:
* `false` or `"disabled"`: Disable the scale animation
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------- | --------------------------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------------- |
| `scale` | `{ value?: [number, number]; timingConfig?: WithTimingConfig }` | `{ value: [1, 0.97], timingConfig: { duration: 120 } }` | Scale values `[rest, active]` applied while pressed or dragged |
### Agenda.EventTitle
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom title content; defaults to the event's `title` |
| `className` | `string` | - | Additional CSS classes for the title text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Agenda.EventTime
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom time content; defaults to the localized `start – end` range (or "All day") |
| `className` | `string` | - | Additional CSS classes for the time text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Agenda.CurrentTimeIndicator
Renders only on day/week pages containing today.
| prop | type | default | description |
| -------------- | ----------------------------------------------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `object` | - | Additional native styles for individual slots |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ----------- | --------------------------------- |
| `container` | Indicator row container |
| `gutter` | Time gutter area of the indicator |
| `badge` | Live time badge |
| `line` | Horizontal line |
| `dot` | Notch at the line start |
| `label` | Time text inside the badge |
#### styles
| slot | type | description |
| ----------- | ----------- | --------------------------- |
| `container` | `ViewStyle` | Style for the row container |
| `gutter` | `ViewStyle` | Style for the gutter area |
| `badge` | `ViewStyle` | Style for the time badge |
| `line` | `ViewStyle` | Style for the line |
| `dot` | `ViewStyle` | Style for the notch |
| `label` | `TextStyle` | Style for the badge text |
### Agenda.MonthGrid
Renders only on month pages.
| prop | type | default | description |
| ------------------ | ------------------------------------ | ---------------------------- | ---------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Per-event chip content template (event via `useAgendaEvent()`); defaults to tint + title |
| `maxEventsPerCell` | `number` | `2` | Maximum event chips per cell before the overflow label |
| `moreLabel` | `(count: number) => string` | `` (count) => `+${count}` `` | Custom overflow label; pressing it opens the day view for that date |
| `className` | `string` | - | Additional CSS classes for the month grid container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `object` | - | Additional native styles for individual slots |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| -------------------- | ------------------------------------ |
| `container` | Month grid container |
| `weekdayRow` | Weekday labels row |
| `weekdayLabel` | One weekday label |
| `row` | One week row |
| `spanningLayer` | Absolute layer hosting spanning bars |
| `spanningEvent` | One multi-day spanning bar |
| `spanningEventTitle` | Title text inside a spanning bar |
| `cell` | One day cell |
| `cellDate` | Date pill of a cell |
| `cellEvents` | Event chip stack of a cell |
| `cellEvent` | One event chip |
| `cellEventTitle` | Title text inside an event chip |
| `cellMore` | Overflow ("+N") label |
#### styles
| slot | type | description |
| -------------------- | ----------- | ---------------------------------- |
| `container` | `ViewStyle` | Style for the month grid container |
| `weekdayRow` | `ViewStyle` | Style for the weekday row |
| `weekdayLabel` | `TextStyle` | Style for one weekday label |
| `row` | `ViewStyle` | Style for one week row |
| `spanningEvent` | `ViewStyle` | Style for a spanning bar |
| `spanningEventTitle` | `TextStyle` | Style for a spanning bar title |
| `cell` | `ViewStyle` | Style for one day cell |
| `cellDate` | `TextStyle` | Style for the date pill |
| `cellEvents` | `ViewStyle` | Style for the chip stack |
| `cellEvent` | `ViewStyle` | Style for one event chip |
| `cellEventTitle` | `TextStyle` | Style for a chip title |
| `cellMore` | `TextStyle` | Style for the overflow label |
#### Data Attributes
Set on the `cellDate` slot. The default styles use them for the today pill (`data-[today=true]:bg-accent`), the selected pill (`data-[selected=true]:bg-accent-soft`), and the muted outside-month dates (`data-[outside-month=true]:text-muted`).
| attribute | values | description |
| -------------------- | --------- | --------------------------------------------------- |
| `data-today` | `boolean` | Whether the cell's date is today |
| `data-selected` | `boolean` | Whether the cell's date is the selected agenda date |
| `data-outside-month` | `boolean` | Whether the cell's date is outside the page's month |
### Agenda.ViewSelector
Extends the `Segment` API except the selection value, which is bound to the agenda view. Exposes `Agenda.ViewSelector.Group` / `.Indicator` / `.Item` / `.Label` / `.Separator` for custom compositions (item values must be `AgendaView` strings); `Segment.ScrollView` is intentionally not exposed.
| prop | type | default | description |
| --------------------- | ------------------------------------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom `Segment` composition; defaults to a `Group` with an `Indicator` and one `Item` per option |
| `options` | `AgendaView[]` | `['day', 'week', 'month']` | Which views can be selected, in render order |
| `labels` | `Partial>` | `{ day: 'Day', week: 'Week', month: 'Month' }` | Custom labels per view |
| `size` | `SegmentRootProps['size']` | `'sm'` | Visual size of the underlying `Segment` |
| `className` | `string` | - | Additional CSS classes for the selector root; the default places it absolutely at the bottom-center |
| `...SegmentRootProps` | `SegmentRootProps` | - | All `Segment` root props are supported except `value`, `defaultValue`, and `onValueChange` |
## Hooks
### useAgenda
The state builder. Returns `UseAgendaReturn`; spread it into the root: ``.
```tsx
import { useAgenda } from 'heroui-native-pro';
const agenda = useAgenda({ events, defaultView: 'week' });
```
#### UseAgendaOptions
| option | type | default | description |
| ------------------------ | ---------------------------------------------------------------------- | ----------------------- | --------------------------------------------------------------------------- |
| `events` | `AgendaEvent[]` | - | Events to display; the Agenda never mutates this array |
| `view` | `AgendaView` | - | Controlled view mode |
| `defaultView` | `AgendaView` | `'day'` | Uncontrolled initial view mode |
| `date` | `CalendarDate` | - | Controlled active date |
| `defaultDate` | `CalendarDate` | today (local time zone) | Uncontrolled initial active date |
| `selectedEventId` | `string \| null` | - | Controlled selected event id |
| `defaultSelectedEventId` | `string \| null` | `null` | Uncontrolled initial selected event id |
| `startHour` | `number` | `0` | First rendered hour of the time grid (inclusive) |
| `endHour` | `number` | `24` | Last rendered hour of the time grid (exclusive) |
| `slotDuration` | `number` | `60` | Minutes represented by one grid slot |
| `slotHeight` | `number` | `60` | Rendered height in px of one grid slot |
| `firstDayOfWeek` | `AgendaFirstDayOfWeek` | - | First day of week for the calendar and week view |
| `locale` | `string` | environment locale | BCP 47 locale |
| `onViewChange` | `(view: AgendaView) => void` | - | Called when the view mode changes |
| `onDateChange` | `(date: CalendarDate) => void` | - | Called when the active date changes (calendar tap, page swipe, `goToToday`) |
| `onEventSelect` | `(id: string \| null) => void` | - | Called when an event is selected (or deselected with `null`) |
| `onEventPress` | `(event: AgendaEvent) => void` | - | Replaces the selection toggle when an event chip is pressed |
| `onEventMove` | `(id: string, start: CalendarDateTime, end: CalendarDateTime) => void` | - | Called with the new start/end after a drag; omit to disable dragging |
| `onEventResize` | `(id: string, start: CalendarDateTime, end: CalendarDateTime) => void` | - | Called with the new start/end after a resize; omit to disable resizing |
| `onEventDelete` | `(id: string) => void` | - | Deletion intent callback; invoke from custom UI |
#### AgendaEvent
| property | type | default | description |
| ------------ | ------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------- |
| `id` | `string` | - | Unique, stable identifier |
| `title` | `string` | - | Event title shown inside event chips |
| `start` | `CalendarDateTime` | - | Start date-time (wall clock, no time zone) |
| `end` | `CalendarDateTime` | - | End date-time (wall clock, no time zone); exclusive, so an event ending at midnight does not cover the following day |
| `color` | `string` | - | Optional accent color used to tint the event chip |
| `isAllDay` | `boolean` | `false` | All-day events render in the all-day section and as spanning month rows |
| `isReadOnly` | `boolean` | `false` | Read-only events cannot be moved or resized |
| `status` | `AgendaEventStatus` | `'confirmed'` | `'confirmed' \| 'unconfirmed'`; unconfirmed renders a dashed border |
#### Returns
`UseAgendaReturn` — the resolved state, layout helpers, actions, and interaction callbacks.
| property | type | description |
| ------------------------ | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `view` | `AgendaView` | Current view mode |
| `date` | `CalendarDate` | Active date |
| `events` | `AgendaEvent[]` | Events passed to the root |
| `selectedEventId` | `string \| null` | Selected event id or `null` |
| `startHour` | `number` | First rendered hour (inclusive) |
| `endHour` | `number` | Last rendered hour (exclusive) |
| `slotDuration` | `number` | Minutes per grid slot |
| `slotHeight` | `number` | Height in px of one grid slot |
| `locale` | `string` | Resolved BCP 47 locale |
| `timeZone` | `string` | Local time zone identifier |
| `firstDayOfWeek` | `AgendaFirstDayOfWeek \| undefined` | First day of week, when explicitly provided |
| `heading` | `string` | Month + year heading for the active date |
| `visibleDays` | `CalendarDate[]` | Days visible in the current view (1 for day, 7 for week, empty for month) |
| `visibleWeeks` | `CalendarDate[][]` | Six week rows for the active month (month view only, empty otherwise) |
| `setView` | `(view: AgendaView) => void` | Sets the view mode |
| `setDate` | `(date: CalendarDate) => void` | Sets the active date (syncs calendar and pager) |
| `selectEvent` | `(id: string \| null) => void` | Selects an event (or deselects with `null`) |
| `goToNext` | `() => void` | Moves to the next day/week/month depending on the view |
| `goToPrevious` | `() => void` | Moves to the previous day/week/month depending on the view |
| `goToToday` | `() => void` | Jumps to today |
| `getEventsForDay` | `(day: CalendarDate) => AgendaEvent[]` | Timed events whose start falls on the given day |
| `getEventLayout` | `(eventId: string) => AgendaEventLayout` | Overlap column layout for a timed event |
| `getAllEventsForDay` | `(day: CalendarDate) => AgendaEvent[]` | All events (timed + all-day) covering the given day |
| `getAllDayLayoutForDays` | `(days: CalendarDate[]) => AgendaAllDayLayoutItem[]` | Packed all-day rows for an arbitrary visible day range |
| `getMonthRowLayout` | `(week: CalendarDate[]) => AgendaMonthRowLayout` | Spanning-event layout for a month week row |
| `getPerCellEvents` | `(day: CalendarDate, week: CalendarDate[]) => AgendaEvent[]` | Non-spanning events for a month cell |
| `onEventDelete` | `((id: string) => void) \| undefined` | Deletion intent callback, when provided |
| `onEventMove` | `((id: string, start: CalendarDateTime, end: CalendarDateTime) => void) \| undefined` | Move intent callback, when provided |
| `onEventResize` | `((id: string, start: CalendarDateTime, end: CalendarDateTime) => void) \| undefined` | Resize intent callback, when provided |
| `onEventPress` | `((event: AgendaEvent) => void) \| undefined` | Press callback, when provided; replaces the selection toggle |
#### AgendaEventLayout
| property | type | description |
| -------------- | -------- | --------------------------------------------------------- |
| `columnIndex` | `number` | Zero-based column of the event within its overlap cluster |
| `totalColumns` | `number` | Total columns of the overlap cluster the event belongs to |
#### AgendaAllDayLayoutItem
| property | type | description |
| ---------- | ------------- | ------------------------------------------- |
| `event` | `AgendaEvent` | The all-day event |
| `colStart` | `number` | Zero-based first visible day column covered |
| `colSpan` | `number` | Number of visible day columns covered |
| `row` | `number` | Zero-based packed row index |
#### AgendaMonthRowLayout
| property | type | description |
| ---------------- | ---------------------------- | --------------------------------------------- |
| `items` | `AgendaMonthRowLayoutItem[]` | Packed spanning events for the week |
| `rowCount` | `number` | Total packed spanning rows in the week |
| `rowCountPerCol` | `number[]` | Per-column count of spanning rows covering it |
### useAgendaContext
Reads the agenda state anywhere inside the `Agenda` subtree (used by all compound parts internally).
```tsx
import { useAgendaContext } from 'heroui-native-pro';
const { view, date, heading, setView, goToToday } = useAgendaContext();
```
#### Returns
`AgendaContextValue` — the same state as `UseAgendaReturn` except the `onEventMove` / `onEventResize` / `onEventPress` callbacks.
### useAgendaPage
Reads the current pager page inside `Agenda.Body` template children.
```tsx
import { useAgendaPage } from 'heroui-native-pro';
const { date, days, weeks, isMonthPage } = useAgendaPage();
```
#### Returns
| property | type | description |
| ------------- | ------------------ | --------------------------------------------------------------------------- |
| `date` | `CalendarDate` | Page anchor date: the day itself, the week start, or the first of the month |
| `days` | `CalendarDate[]` | Days rendered by this page (1 for day, 7 for week, empty for month) |
| `weeks` | `CalendarDate[][]` | Six week rows for month pages (empty for day/week) |
| `isMonthPage` | `boolean` | Whether this page renders a month grid |
### useAgendaEvent
Reads the event being rendered inside item templates (`Agenda.DayColumns`, `Agenda.AllDaySection`, `Agenda.MonthGrid` children).
```tsx
import { useAgendaEvent } from 'heroui-native-pro';
const event = useAgendaEvent();
```
#### Returns
The current template `AgendaEvent`.
# Calendar
**Category**: native
**URL**: https://heroui.pro/docs/native/components/calendar
> A single-date calendar for selecting dates with month navigation, locale support, and customizable day cells.
> `Calendar` uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) for all date manipulations (`CalendarDate`, calendar systems, time zones, locale-aware formatting). For full context on the date types and helpers exposed through this component's props and callbacks, read the [`@internationalized/date` docs](https://react-aria.adobe.com/internationalized/date/) alongside this page.
## Import
```tsx
import { Calendar } from 'heroui-native-pro';
```
## Anatomy
### With Heading
```tsx
{(day) => }
{(date) => }
```
### With Year Picker
```tsx
{(day) => }
{(date) => }
{(renderProps) => (
)}
```
* **Calendar**: Root container that manages single-date selection state, locale, and animation settings. Supports controlled and uncontrolled modes with min/max constraints and date unavailability filtering.
* **Calendar.Header**: Toolbar row for navigation controls and the month/year title.
* **Calendar.Heading**: Month/year label text. The primitive computes the heading string automatically when children are omitted.
* **Calendar.NavButton**: Previous or next month navigation button. Renders a default chevron icon using the theme accent color; override via `iconProps` or pass custom `children`.
* **Calendar.Grid**: Month grid container that provides internal grid context to the header and body.
* **Calendar.GridHeader**: Weekday labels row. Requires a render function `(day: string) => ReactElement` as children.
* **Calendar.GridBody**: Day cells matrix. Requires a render function `(date: CalendarDate) => ReactElement` as children.
* **Calendar.HeaderCell**: Single weekday header cell. Stringifiable children are wrapped in `HeaderCellLabel`; pass `day` when omitting children.
* **Calendar.HeaderCellLabel**: Text slot for a weekday header cell label.
* **Calendar.Cell**: Selectable day cell. By default renders `CellBody` with `CellLabel` inside. Pass a render function as children to customize the cell content.
* **Calendar.CellBody**: Inner rounded region of a day cell with press scale animation. Pass `cellRenderProps` for data attribute selectors.
* **Calendar.CellLabel**: Day number label. Pass `cellRenderProps` for data attribute selectors.
* **Calendar.CellIndicator**: Dot marker under a day cell (e.g. for events). Pass `cellRenderProps` for `data-selected` styling.
* **Calendar.YearPickerTrigger**: Pressable trigger that replaces `Heading` to toggle the year picker overlay.
* **Calendar.YearPickerTriggerHeading**: Month/year label text inside the year picker trigger.
* **Calendar.YearPickerTriggerIndicator**: Animated chevron icon indicating the year picker open state.
* **Calendar.YearPickerGrid**: Overlay container positioned over the month grid when the year picker is open.
* **Calendar.YearPickerGridBody**: Scrollable list of year cells inside the year picker grid.
* **Calendar.YearPickerCell**: Pressable year cell that selects a year and closes the picker.
## Usage
### Basic Usage
The Calendar component uses compound parts to build a date picker. `GridHeader` and `GridBody` require render function children.
```tsx
{(day) => }
{(date) => }
```
### Default Value
Set an initial selected date with `defaultValue` using `@internationalized/date`.
```tsx
{(day) => }
{(date) => }
```
### Controlled Value
Use `value` and `onChange` to control the selected date externally.
```tsx
const [date, setDate] = useState(today(getLocalTimeZone()));
{(day) => }
{(date) => }
;
```
### Min and Max Dates
Restrict navigation and selection to a date range using `minValue` and `maxValue`.
```tsx
const now = today(getLocalTimeZone());
{(day) => }
{(date) => }
;
```
### Unavailable Dates
Mark specific dates as unavailable using the `isDateUnavailable` callback.
```tsx
const isDateUnavailable = (date: DateValue) => isWeekend(date, 'en-US');
{(day) => }
{(date) => }
;
```
### With Cell Indicators
Use a render function on `Calendar.Cell` to add dot indicators under specific dates.
```tsx
{(date) => (
{(renderProps) => (
{renderProps.formattedDate}
{datesWithEvents.includes(date.day) && (
)}
)}
)}
```
### International Calendar
Pass a BCP 47 locale string to render the calendar in a different language and calendar system.
```tsx
{(day) => }
{(date) => }
```
### Disabled State
Disable the entire calendar and all navigation controls.
```tsx
{(day) => }
{(date) => }
```
### Year Picker
Add a year picker overlay by replacing `Heading` with `YearPickerTrigger` and adding a `YearPickerGrid` inside the root.
```tsx
{(day) => }
{(date) => }
{(renderProps) => (
)}
```
## Example
```tsx
import {
getLocalTimeZone,
isToday,
parseDate,
today,
type DateValue,
} from '@internationalized/date';
import { Calendar } from 'heroui-native-pro';
import { useState } from 'react';
import { View } from 'react-native';
const datesWithEvents = [3, 7, 12, 15, 21, 28];
export default function CalendarExample() {
const [date, setDate] = useState(parseDate('2026-06-19'));
return (
{(day) => {day}}
{(date) => (
{(renderProps) => (
{renderProps.formattedDate}
{(isToday(date, getLocalTimeZone()) ||
datesWithEvents.includes(date.day)) && (
)}
)}
)}
);
}
```
## API Reference
### Calendar
| prop | type | default | description |
| ------------------------ | --------------------------------------------------------------------------- | ------- | -------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: { state: CalendarState }) => React.ReactNode)` | - | Calendar content or render function receiving calendar state |
| `value` | `DateValue \| null` | - | Controlled selected date |
| `defaultValue` | `DateValue \| null` | - | Default selected date for uncontrolled usage |
| `minValue` | `DateValue \| null` | - | Minimum selectable date; disables earlier dates and navigation |
| `maxValue` | `DateValue \| null` | - | Maximum selectable date; disables later dates and navigation |
| `isDateUnavailable` | `(date: DateValue) => boolean` | - | Callback to mark specific dates as unavailable |
| `isDisabled` | `boolean` | `false` | Whether the entire calendar is disabled |
| `isReadOnly` | `boolean` | `false` | Whether the calendar value is immutable |
| `isInvalid` | `boolean` | - | Whether the current selection is invalid |
| `firstDayOfWeek` | `'sun' \| 'mon' \| 'tue' \| 'wed' \| 'thu' \| 'fri' \| 'sat'` | - | Override the first day of the week |
| `locale` | `string` | - | BCP 47 locale; defaults to the environment locale |
| `isYearPickerOpen` | `boolean` | - | Controlled open state for the year picker overlay |
| `defaultYearPickerOpen` | `boolean` | `false` | Initial open state for the year picker in uncontrolled mode |
| `className` | `string` | - | Additional CSS classes for the root container |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the calendar subtree |
| `onChange` | `(value: MappedDateValue) => void` | - | Handler called when the selected date changes |
| `onYearPickerOpenChange` | `(isOpen: boolean) => void` | - | Handler called when the year picker open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AnimationRootDisableAll
Animation configuration for the Calendar root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### Calendar.Header
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------- |
| `children` | `React.ReactNode` | - | Header content (Heading, NavButtons, etc.) |
| `className` | `string` | - | Additional CSS classes for the header row container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Calendar.Heading
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom heading text; auto-computed month/year when omitted |
| `className` | `string` | - | Additional CSS classes for the heading text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Calendar.NavButton
| prop | type | default | description |
| ------------------- | ---------------------------- | ------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom icon content; replaces the default chevron when provided |
| `slot` | `'previous' \| 'next'` | - | Navigation direction; determines which chevron icon is rendered |
| `isDisabled` | `boolean` | - | Merged with calendar `isDisabled` and range boundary state |
| `className` | `string` | - | Additional CSS classes for the pressable |
| `iconProps` | `CalendarNavButtonIconProps` | - | Overrides for the built-in chevron; ignored with custom children |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### CalendarNavButtonIconProps
| prop | type | default | description |
| ------- | -------- | -------------- | --------------------------- |
| `size` | `number` | `18` | Icon size in logical pixels |
| `color` | `string` | Theme `accent` | Icon stroke/fill color |
### Calendar.Grid
| prop | type | default | description |
| -------------- | ------------------------------- | ------- | --------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Grid content (GridHeader, GridBody) |
| `offset` | `DateDuration` | - | Offset from the visible range start for multi-month grids |
| `weekdayStyle` | `'narrow' \| 'short' \| 'long'` | - | Weekday label format |
| `className` | `string` | - | Additional CSS classes for the grid container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Calendar.GridHeader
| prop | type | default | description |
| -------------- | ------------------------------------- | ------- | -------------------------------------------------------- |
| `children` | `(day: string) => React.ReactElement` | - | Render function called for each weekday label (required) |
| `className` | `string` | - | Additional CSS classes for the weekday row wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Calendar.GridBody
| prop | type | default | description |
| -------------- | -------------------------------------------- | ------- | ----------------------------------------------------------- |
| `children` | `(date: CalendarDate) => React.ReactElement` | - | Render function called for each day in the month (required) |
| `className` | `string` | - | Additional CSS classes for the grid body |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Calendar.HeaderCell
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom cell content; stringifiable children are wrapped in `HeaderCellLabel` |
| `day` | `string` | - | Weekday label string from `GridHeader`'s render callback; used when `children` is omitted |
| `className` | `string` | - | Additional CSS classes for the header cell container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Calendar.HeaderCellLabel
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Weekday label text |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Calendar.Cell
| prop | type | default | description |
| ------------------- | -------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------- |
| `date` | `CalendarDate` | - | The calendar date this cell represents (required) |
| `children` | `React.ReactNode \| ((renderProps: CalendarCellRenderProps) => React.ReactNode)` | - | Custom cell content; defaults to `CellBody` with `CellLabel` inside |
| `isDisabled` | `boolean` | - | Merged with calendar `isDisabled` and cell-specific disabled state |
| `className` | `string` | - | Additional CSS classes for the day cell pressable |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### CalendarCellRenderProps
Render props received by the `Calendar.Cell` children render function.
| property | type | description |
| ----------------------- | -------------- | ------------------------------------------------------------------ |
| `date` | `CalendarDate` | The calendar date for this cell |
| `formattedDate` | `string` | Locale-formatted day number string |
| `isSelected` | `boolean` | Whether this date is currently selected |
| `isToday` | `boolean` | Whether this date is today |
| `isDisabled` | `boolean` | Whether this date is disabled |
| `isUnavailable` | `boolean` | Whether this date is unavailable via `isDateUnavailable` |
| `isOutsideMonth` | `boolean` | Whether this date is outside the currently visible month |
| `isFocused` | `boolean` | Whether this date is currently focused |
| `isInvalid` | `boolean` | Whether this date is invalid per `minValue`/`maxValue` constraints |
| `isPressed` | `boolean` | Whether the day cell pressable is in a pressed state |
| `isRangeStart` | `boolean` | First day of the highlighted range (range calendar only) |
| `isRangeEnd` | `boolean` | Last day of the highlighted range (range calendar only) |
| `isRangeFilled` | `boolean` | Whether the range spans more than one day (range calendar only) |
| `isRangeMiddle` | `boolean` | Strictly inside the range, not start or end (range calendar only) |
| `isRangeMiddleRowStart` | `boolean` | Range middle cell at the start of a row (range calendar only) |
| `isRangeMiddleRowEnd` | `boolean` | Range middle cell at the end of a row (range calendar only) |
### Calendar.CellBody
| prop | type | default | description |
| ----------------------- | --------------------------- | ------- | -------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Body content (typically `CellLabel` and optional `CellIndicator`) |
| `cellRenderProps` | `CalendarCellRenderProps` | - | Render props from `Calendar.Cell`'s children callback; drives `data-*` selectors |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated scale styles are applied; set `false` for custom logic |
| `className` | `string` | - | Additional CSS classes for the cell body container |
| `animation` | `CalendarCellBodyAnimation` | - | Press scale animation configuration |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Data Attributes
| attribute | values | description |
| --------------------------------- | --------- | ----------------------------------------------- |
| `data-today` | `boolean` | Whether the date is today |
| `data-outside-month` | `boolean` | Whether the date is outside the visible month |
| `data-unavailable` | `boolean` | Whether the date is unavailable |
| `data-disabled` | `boolean` | Whether the date is disabled |
| `data-focused` | `boolean` | Whether the date is focused |
| `data-invalid` | `boolean` | Whether the date is invalid |
| `data-selected` | `boolean` | Whether the date is selected |
| `data-pressed` | `boolean` | Whether the cell is pressed |
| `data-range-start` | `boolean` | First day of a range (range calendar only) |
| `data-range-end` | `boolean` | Last day of a range (range calendar only) |
| `data-range-filled` | `boolean` | Range spans multiple days (range calendar only) |
| `data-range-middle` | `boolean` | Inside the range, not start/end (range only) |
| `data-range-middle-row-start` | `boolean` | Range middle at row start (range calendar only) |
| `data-range-middle-row-end` | `boolean` | Range middle at row end (range calendar only) |
| `data-disabled-not-outside-month` | `boolean` | Disabled but within the visible month |
#### CalendarCellBodyAnimation
Animation configuration for `Calendar.CellBody` press feedback. Can be:
* `false` or `"disabled"`: Disable press animation
* `undefined`: Use default animation
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------------- | ------------------ | ------------------- | ---------------------------------- |
| `scale.value` | `[number, number]` | `[1, 0.9]` | Scale values \[unpressed, pressed] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 120 }` | Animation timing configuration |
### Calendar.CellLabel
| prop | type | default | description |
| ----------------- | ------------------------- | ------- | -------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Label text (usually the day number) |
| `cellRenderProps` | `CalendarCellRenderProps` | - | Render props from `Calendar.Cell`'s children callback; drives `data-*` selectors |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
#### Data Attributes
Same data attributes as `Calendar.CellBody`. See above.
### Calendar.CellIndicator
| prop | type | default | description |
| ----------------- | ------------------------- | ------- | -------------------------------------------------------------------------------- |
| `cellRenderProps` | `CalendarCellRenderProps` | - | Render props from `Calendar.Cell`'s children callback; drives `data-*` selectors |
| `className` | `string` | - | Additional CSS classes for the indicator dot container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Data Attributes
Same data attributes as `Calendar.CellBody`. See above.
### Calendar.YearPickerTrigger
| prop | type | default | description |
| ------------------- | -------------------------------------------------------------------------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode \| ((values: YearPickerTriggerRenderProps) => React.ReactNode)` | - | Trigger content or render function |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### YearPickerTriggerRenderProps
| property | type | description |
| ----------- | ------------ | ----------------------------------- |
| `isOpen` | `boolean` | Whether the year picker is open |
| `monthYear` | `string` | Formatted month/year heading string |
| `toggle` | `() => void` | Toggle the year picker open state |
### Calendar.YearPickerTriggerHeading
| prop | type | default | description |
| -------------- | -------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------- |
| `children` | `React.ReactNode \| ((values: YearPickerTriggerRenderProps) => React.ReactNode)` | - | Heading text or render function; auto-computed when omitted |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Calendar.YearPickerTriggerIndicator
| prop | type | default | description |
| ----------------------- | -------------------------------------------------------------------------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode \| ((values: YearPickerTriggerRenderProps) => React.ReactNode)` | - | Custom indicator content or render function |
| `iconProps` | `{ size?: number; color?: string }` | - | Overrides for the default chevron icon |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated rotation styles are applied |
| `animation` | `YearPickerIndicatorAnimation` | - | Rotation animation configuration |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### YearPickerIndicatorAnimation
Animation configuration for the year picker trigger chevron rotation. Can be:
* `false` or `"disabled"`: Disable rotation animation
* `undefined`: Use default animation
* `object`: Custom animation configuration
| prop | type | default | description |
| ----------------------- | ------------------ | -------------- | ------------------------------------- |
| `rotation.value` | `[number, number]` | `[0, 90]` | Rotation degrees \[closed, open] |
| `rotation.springConfig` | `WithSpringConfig` | Default spring | Spring configuration for the rotation |
### Calendar.YearPickerGrid
| prop | type | default | description |
| ----------------------- | ------------------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Grid content (YearPickerGridBody) |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated opacity styles are applied |
| `animation` | `YearPickerGridAnimation` | - | Opacity animation configuration |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### YearPickerGridAnimation
Animation configuration for the year picker grid overlay. Can be:
* `false` or `"disabled"`: Disable opacity animation
* `undefined`: Use default animation
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------------- | ------------------ | ------------------- | ------------------------------------ |
| `opacity.value` | `[number, number]` | `[0, 1]` | Opacity values \[closed, open] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | Timing configuration for the opacity |
### Calendar.YearPickerGridBody
| prop | type | default | description |
| ------------------ | -------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
| `children` | `(values: YearPickerCellRenderProps) => React.ReactNode` | - | Render function called for each year |
| `...FlatListProps` | `FlatListProps` | - | FlatList props except `data`, `renderItem`, `keyExtractor`, `numColumns`, and `columnWrapperStyle` |
#### YearPickerCellRenderProps
| property | type | description |
| --------------- | ------------ | --------------------------------------- |
| `year` | `number` | The year number |
| `formattedYear` | `string` | Locale-formatted year string |
| `isSelected` | `boolean` | Whether this year matches the selection |
| `isCurrentYear` | `boolean` | Whether this year is the current year |
| `isOpen` | `boolean` | Whether the year picker is open |
| `selectYear` | `() => void` | Select this year and close the picker |
### Calendar.YearPickerCell
| prop | type | default | description |
| ------------------- | ----------------------------------------------------------------------------- | ------- | ------------------------------------------------------- |
| `year` | `number` | - | The year this cell represents (required) |
| `isSelected` | `boolean` | - | Whether this year is selected (required) |
| `children` | `React.ReactNode \| ((values: YearPickerCellRenderProps) => React.ReactNode)` | - | Custom cell content or render function |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
## Hooks
### useCalendar
Hook to access the calendar state context. Must be used within a `Calendar` component.
```tsx
import { useCalendar } from 'heroui-native-pro';
const state = useCalendar();
```
#### Returns: CalendarState
| property | type | description |
| ------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------ |
| `value` | `CalendarDate \| null` | Currently selected date |
| `setValue` | `(value: CalendarDate \| null) => void` | Set the selected date |
| `visibleRange` | `RangeValue` | The date range currently visible in the calendar |
| `focusedDate` | `CalendarDate` | Currently focused date |
| `setFocusedDate` | `(value: CalendarDate) => void` | Set the focused date |
| `isDisabled` | `boolean` | Whether the calendar is disabled |
| `isReadOnly` | `boolean` | Whether the calendar is read-only |
| `isValueInvalid` | `boolean` | Whether the current value is invalid |
| `timeZone` | `string` | Time zone of displayed dates |
| `minValue` | `DateValue \| null \| undefined` | Minimum allowed date |
| `maxValue` | `DateValue \| null \| undefined` | Maximum allowed date |
| `focusNextPage` | `() => void` | Navigate to the next month |
| `focusPreviousPage` | `() => void` | Navigate to the previous month |
| `selectFocusedDate` | `() => void` | Select the currently focused date |
| `selectDate` | `(date: CalendarDate) => void` | Select a specific date |
| `isSelected` | `(date: CalendarDate) => boolean` | Check if a date is selected |
| `isInvalid` | `(date: CalendarDate) => boolean` | Check if a date is invalid |
| `isCellDisabled` | `(date: CalendarDate) => boolean` | Check if a date cell is disabled |
| `isCellUnavailable` | `(date: CalendarDate) => boolean` | Check if a date cell is unavailable |
| `isCellFocused` | `(date: CalendarDate) => boolean` | Check if a date cell is focused |
| `getDatesInWeek` | `(weekIndex: number, startDate?: CalendarDate) => Array` | Get dates for a week row |
# DateField
**Category**: native
**URL**: https://heroui.pro/docs/native/components/date-field
> A date input field with `dd/mm/yyyy` masking and an optional calendar popup for selecting dates.
> `DateField` uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) for all date manipulations (`CalendarDate`, calendar systems, time zones, locale-aware formatting). For full context on the date types and helpers exposed through this component's props and callbacks, read the [`@internationalized/date` docs](https://react-aria.adobe.com/internationalized/date/) alongside this page.
## Import
```tsx
import { DateField } from 'heroui-native-pro';
```
## Anatomy
```tsx
.........
```
* **DateField**: Root container that manages date selection state, open state, input masking mode, and form field context (for Label, Description, FieldError). Supports controlled and uncontrolled modes.
* **DateField.InputGroup**: Input group wrapper that contains the text input, prefix, and suffix slots.
* **DateField.Input**: Text input with `dd/mm/yyyy` masked entry. In `"masked"` mode, digits auto-insert `/` separators and blur commits the parsed date. In `"loose"` mode, no masking or parse is applied. Selecting a date in the calendar updates the input text.
* **DateField.Prefix**: Optional leading slot inside the input group.
* **DateField.Suffix**: Trailing slot inside the input group. Typically contains the calendar trigger.
* **DateField.Select**: Pre-wired Select root connected to the DateField context. State props are managed by the root.
* **DateField.Trigger**: Pressable trigger that opens the calendar overlay. Automatically dismisses the keyboard on press.
* **DateField.TriggerIndicator**: Indicator icon inside the trigger. Defaults to a calendar icon with a muted background.
* **DateField.Portal**: Portal wrapper that re-provides DateField context across the portal boundary.
* **DateField.Overlay**: Backdrop overlay behind the calendar content.
* **DateField.Content**: Content container for the calendar popup. Supports `"popover"`, `"dialog"`, and `"bottom-sheet"` presentations.
* **DateField.Calendar**: Pre-wired Calendar root that commits the selected date, updates the input text, and closes the overlay on selection. Uses `Calendar` compound parts as children.
## Usage
### Basic Usage
The DateField uses masked input by default. Digits auto-insert `/` separators toward `dd/mm/yyyy`. Blur commits the parsed date.
```tsx
{(day) => }
{(date) => }
The field formats as dd/mm/yyyy.
```
### Controlled
Control the selected date externally with `value` and `onValueChange`. The option stores an ISO date string in `value` and a display label shown in the input. You can also control the calendar overlay with `isOpen` and `onOpenChange`.
```tsx
import type { DateFieldOption } from 'heroui-native-pro';
import { useState } from 'react';
const [selected, setSelected] = useState({
value: '2026-06-15',
label: '15/06/2026',
});
const [isOpen, setIsOpen] = useState(false);
{(day) => }
{(date) => }
;
```
### Loose Input Mode
Set `inputMode="loose"` for plain text without masking or blur parsing. The calendar still updates the input when a date is selected.
```tsx
...
```
### Popover Presentation
Display the calendar in a popover anchored to the trigger.
```tsx
...
```
### Dialog Presentation
Display the calendar in a centered modal dialog.
```tsx
...
```
### Field States
Use root props for required, invalid, and disabled states.
```tsx
...Required for logistics.
```
### Invalid State with FieldError
Combine `isInvalid` with FieldError to display validation messages.
```tsx
...Must be a business day.Please enter a valid return date.
```
## Example
```tsx
import { Description, FieldError, Label } from 'heroui-native';
import { Calendar, DateField } from 'heroui-native-pro';
import { View } from 'react-native';
export default function DateFieldExample() {
return (
{(day) => }
{(date) => }
The field formats as dd/mm/yyyy.
{(day) => }
{(date) => }
Must be a business day.Please enter a valid return date.
);
}
```
## API Reference
### DateField
| prop | type | default | description |
| --------------- | ----------------------------------------------- | ---------- | ------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Children elements (Label, DateField.InputGroup, Description, FieldError) |
| `value` | `DateFieldOption` | - | Controlled selected option |
| `defaultValue` | `DateFieldOption` | - | Default selected option for uncontrolled usage |
| `inputMode` | `DateFieldInputMode` | `'masked'` | Input keyboard behavior |
| `isDisabled` | `boolean` | `false` | Whether the entire field is disabled |
| `isInvalid` | `boolean` | `false` | Whether the field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the field is required |
| `isOpen` | `boolean` | - | Controlled open state of the calendar overlay |
| `isDefaultOpen` | `boolean` | - | Initial open state for uncontrolled usage |
| `locale` | `string` | - | BCP 47 locale forwarded to `DateField.Calendar` |
| `className` | `string` | - | Additional CSS classes for the root container |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the date field subtree |
| `onValueChange` | `(value: DateFieldOption \| undefined) => void` | - | Handler called when the selected option changes |
| `onOpenChange` | `(open: boolean) => void` | - | Handler called when the open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### DateFieldOption
| property | type | description |
| -------- | -------- | ------------------------------------- |
| `value` | `string` | ISO date string (e.g. `"2026-06-15"`) |
| `label` | `string` | Display string (e.g. `"15/06/2026"`) |
#### DateFieldInputMode
Input keyboard behavior for `DateField.Input`:
* `'masked'` — Digits only, auto-inserts `/` toward `dd/mm/yyyy`. Blur commits the parsed date. `maxLength` is set to `10`. (default)
* `'loose'` — Plain text, no masking or blur parsing. The calendar still updates the field on selection.
#### AnimationRootDisableAll
Animation configuration for the DateField root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### DateField.InputGroup
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Input group content (Input, Prefix, Suffix) |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### DateField.Input
| prop | type | default | description |
| --------------- | ------------------------ | -------------- | -------------------------------------------------------------------------- |
| `placeholder` | `string` | `'dd/mm/yyyy'` | Placeholder text shown when the input is empty |
| `inputMode` | `string` | `'numeric'` | Keyboard type for the text input |
| `isDisabled` | `boolean` | - | Whether the input is disabled; inherits from root when omitted |
| `maxLength` | `number` | `10` | Maximum character length; auto-set to `10` in masked mode |
| `onChangeText` | `(text: string) => void` | - | Side-effect handler called after the internal masked change handler |
| `onBlur` | `(e: BlurEvent) => void` | - | Side-effect handler called after the internal blur commit |
| `...InputProps` | `InputGroupInputProps` | - | All InputGroup.Input props are supported except `value` and `onChangeText` |
### DateField.Prefix
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Prefix content |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### DateField.Suffix
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Suffix content (typically the calendar trigger) |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### DateField.Select
| prop | type | default | description |
| -------------- | ----------------------------------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Select content (Trigger, Portal) |
| `isDisabled` | `boolean` | - | Overrides the root `isDisabled` when set |
| `presentation` | `'popover' \| 'dialog' \| 'bottom-sheet'` | `'popover'` | Presentation mode for the select content |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### DateField.Trigger
| prop | type | default | description |
| ------------------- | ------------------------------------ | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger content (TriggerIndicator) |
| `isDisabled` | `boolean` | - | Whether the trigger is disabled |
| `className` | `string` | - | Additional CSS classes for the trigger |
| `onPress` | `(e: GestureResponderEvent) => void` | - | Press handler; keyboard is dismissed before this fires |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### DateField.TriggerIndicator
| prop | type | default | description |
| ----------------------- | --------------------------------- | ------- | ------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom indicator content; defaults to a calendar icon when omitted |
| `iconProps` | `SelectTriggerIndicatorIconProps` | - | Overrides for the default icon |
| `isAnimatedStyleActive` | `boolean` | `false` | Whether animated rotation styles are applied |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `animation` | `SelectTriggerIndicatorAnimation` | `false` | Rotation animation configuration; disabled by default |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SelectTriggerIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | --------------------------- |
| `size` | `number` | `16` | Icon size in logical pixels |
| `color` | `string` | `muted` | Icon fill color |
### DateField.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------------- | ------- | ------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Portal content (Overlay, Content) |
| `hostName` | `string` | - | Optional name of the host element for the portal |
| `disableFullWindowOverlay` | `boolean` | `false` | Use a regular View instead of FullWindowOverlay on iOS |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay as a modal container (iOS) |
| `className` | `string` | - | Additional CSS classes for the portal container |
### DateField.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | ------------------------------------------------------- |
| `closeOnPress` | `boolean` | `true` | Whether to close the picker when the overlay is pressed |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated opacity styles are applied |
| `className` | `string` | - | Additional CSS classes for the overlay backdrop |
| `animation` | `SelectOverlayAnimation` | - | Opacity animation configuration |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### DateField.Content
The content component is a union type based on the `presentation` prop.
#### Popover presentation
| prop | type | default | description |
| -------------- | ------------------------------------------------ | --------------- | ----------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (DateField.Calendar) |
| `presentation` | `'popover'` | - | Popover presentation mode |
| `width` | `'content-fit' \| 'trigger' \| 'full' \| number` | `'content-fit'` | Content width sizing strategy |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentPopoverAnimation` | - | Keyframe animation configuration for entering/exiting |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Dialog presentation
| prop | type | default | description |
| -------------- | ------------------------ | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (DateField.Calendar) |
| `presentation` | `'dialog'` | - | Dialog presentation mode |
| `isSwipeable` | `boolean` | `true` | Whether the dialog can be swiped to dismiss |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentAnimation` | - | Keyframe animation configuration for scale/opacity |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Bottom sheet presentation
| prop | type | default | description |
| --------------------- | ------------------ | ------- | -------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (DateField.Calendar) |
| `presentation` | `'bottom-sheet'` | - | Bottom sheet presentation mode |
| `...BottomSheetProps` | `BottomSheetProps` | - | All @gorhom/bottom-sheet props are supported |
### DateField.Calendar
| prop | type | default | description |
| -------------------- | --------------------------- | --------------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Calendar compound parts (Calendar.Header, Calendar.Grid, etc.) |
| `value` | `DateValue \| null` | - | Overrides the calendar value derived from the root selection |
| `locale` | `string` | - | Overrides the root locale for the calendar grid |
| `accessibilityLabel` | `string` | `'Pick a date'` | Screen reader label for the calendar container |
| `onChange` | `(date: DateValue) => void` | - | Side-effect handler called before the default commit behavior |
| `...CalendarProps` | `CalendarProps` | - | All Calendar root props are supported (minValue, maxValue, etc.) |
## Hooks
### useDateField
Hook to access the DateField input context. Must be used within a `DateField` component.
```tsx
import { useDateField } from 'heroui-native-pro';
const { inputMode, inputText, onInputChangeText, onInputBlur } = useDateField();
```
#### Returns: DateFieldInputContextValue
| property | type | description |
| ------------------- | ------------------------ | ---------------------------------------------------------------------------------- |
| `inputMode` | `DateFieldInputMode` | Current input mode (`"masked"` or `"loose"`) |
| `inputText` | `string` | Current draft text in the input |
| `onInputChangeText` | `(text: string) => void` | Update the draft text; in masked mode, applies `dd/mm/yyyy` formatting |
| `onInputBlur` | `() => void` | Commit the draft text on blur; in masked mode, parses and updates the picker value |
# DatePicker
**Category**: native
**URL**: https://heroui.pro/docs/native/components/date-picker
> A single-date picker that combines a trigger field with a calendar popup for selecting dates.
> `DatePicker` uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) for all date manipulations (`CalendarDate`, calendar systems, time zones, locale-aware formatting). For full context on the date types and helpers exposed through this component's props and callbacks, read the [`@internationalized/date` docs](https://react-aria.adobe.com/internationalized/date/) alongside this page.
## Import
```tsx
import { DatePicker } from 'heroui-native-pro';
```
## Anatomy
```tsx
.........
```
* **DatePicker**: Root container that manages date selection state, open state, display formatting, and form field context (for Label, Description, FieldError). Supports controlled and uncontrolled modes.
* **DatePicker.Select**: Pre-wired Select root connected to the DatePicker context. State props (`value`, `isOpen`, `onValueChange`, `onOpenChange`) are managed by the root.
* **DatePicker.Trigger**: Pressable trigger button that opens the calendar overlay. Supports `variant` and inherits invalid border styling from the root.
* **DatePicker.Value**: Text display for the selected date label. Shows a placeholder when no date is selected.
* **DatePicker.TriggerIndicator**: Indicator icon inside the trigger. Defaults to a calendar icon instead of a chevron.
* **DatePicker.Portal**: Portal wrapper that re-provides DatePicker context across the portal boundary.
* **DatePicker.Overlay**: Backdrop overlay behind the calendar content.
* **DatePicker.Content**: Content container for the calendar popup. Supports `"popover"`, `"dialog"`, and `"bottom-sheet"` presentations.
* **DatePicker.Calendar**: Pre-wired Calendar root that commits the selected date, updates the trigger label, and closes the overlay on selection. Uses `Calendar` compound parts as children.
## Usage
### Basic Usage
The DatePicker uses a popover presentation by default. Pass `Calendar` compound parts as children of `DatePicker.Calendar`.
```tsx
{(day) => }
{(date) => }
```
### Controlled
Control the selected date externally with `value` and `onValueChange`. The option stores an ISO date string in `value` and a display label shown in the trigger. You can also control the calendar overlay with `isOpen` and `onOpenChange`.
```tsx
import type { DatePickerOption } from 'heroui-native-pro';
import { useState } from 'react';
const [selected, setSelected] = useState({
value: '2026-06-15',
label: 'Jun 15, 2026',
});
const [isOpen, setIsOpen] = useState(false);
{(day) => }
{(date) => }
;
```
### Dialog Presentation
Display the calendar in a centered modal dialog.
```tsx
...
```
### Bottom Sheet Presentation
Display the calendar in a bottom sheet.
```tsx
...
```
### Display Format
Configure how the selected date is displayed in the trigger using `dateDisplayFormat`.
```tsx
...
```
### Custom Format Function
Override the display label entirely with `formatDate`.
```tsx
function formatSpanishDate(date: CalendarDate): string {
return new Intl.DateTimeFormat('es-ES', {
weekday: 'long',
day: 'numeric',
month: 'long',
year: 'numeric',
}).format(date.toDate(getLocalTimeZone()));
}
...;
```
### Field States
Use root props for required, invalid, and disabled states.
```tsx
...Required for the project timeline.
```
### Invalid State with FieldError
Combine `isInvalid` with FieldError to display validation messages. The trigger shows a danger border.
```tsx
...Must be a business day.Please select a valid ship date.
```
### With Year Picker
Use `Calendar.YearPickerTrigger` inside `DatePicker.Calendar` to add a year picker overlay.
```tsx
{(day) => }
{(date) => }
{({ year, isSelected }) => (
)}
```
## Example
```tsx
import type { CalendarDate } from '@internationalized/date';
import { getLocalTimeZone } from '@internationalized/date';
import { Description, FieldError, Label } from 'heroui-native';
import { Calendar, DatePicker } from 'heroui-native-pro';
import { View } from 'react-native';
function formatSpanishDate(date: CalendarDate): string {
return new Intl.DateTimeFormat('es-ES', {
weekday: 'long',
day: 'numeric',
month: 'long',
year: 'numeric',
}).format(date.toDate(getLocalTimeZone()));
}
export default function DatePickerExample() {
return (
{(day) => }
{(date) => }
{({ year, isSelected }) => (
)}
{(day) => }
{(date) => }
Must be a business day.Please select a valid ship date.
);
}
```
## API Reference
### DatePicker
| prop | type | default | description |
| ------------------- | ------------------------------------------------ | ---------- | --------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements (Label, DatePicker.Select, Description, FieldError) |
| `value` | `DatePickerOption` | - | Controlled selected option |
| `defaultValue` | `DatePickerOption` | - | Default selected option for uncontrolled usage |
| `isDisabled` | `boolean` | `false` | Whether the entire field is disabled |
| `isInvalid` | `boolean` | `false` | Whether the field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the field is required |
| `isOpen` | `boolean` | - | Controlled open state of the calendar overlay |
| `isDefaultOpen` | `boolean` | - | Initial open state for uncontrolled usage |
| `dateDisplayFormat` | `DatePickerDateDisplayFormat` | `'medium'` | Preset date label format; ignored when `formatDate` is set |
| `locale` | `string` | - | BCP 47 locale for label formatting and calendar grid |
| `formatDate` | `(date: CalendarDate) => string` | - | Custom formatter that overrides `dateDisplayFormat` and `locale` for labels |
| `className` | `string` | - | Additional CSS classes for the root container |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the date picker subtree |
| `onValueChange` | `(value: DatePickerOption \| undefined) => void` | - | Handler called when the selected option changes |
| `onOpenChange` | `(open: boolean) => void` | - | Handler called when the open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### DatePickerOption
| property | type | description |
| -------- | -------- | ----------------------------------------------------------- |
| `value` | `string` | ISO date string (e.g. `"2026-06-15"`) |
| `label` | `string` | Display string shown in the trigger (e.g. `"Jun 15, 2026"`) |
#### DatePickerDateDisplayFormat
Built-in date label presets (maps to `Intl.DateTimeFormat` `dateStyle`):
* `'short'` — e.g. `"6/15/26"`
* `'medium'` — e.g. `"Jun 15, 2026"` (default)
* `'long'` — e.g. `"June 15, 2026"`
* `'full'` — e.g. `"Monday, June 15, 2026"`
#### AnimationRootDisableAll
Animation configuration for the DatePicker root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### DatePicker.Select
| prop | type | default | description |
| -------------- | ----------------------------------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Select content (Trigger, Portal) |
| `isDisabled` | `boolean` | - | Overrides the root `isDisabled` when set |
| `presentation` | `'popover' \| 'dialog' \| 'bottom-sheet'` | `'popover'` | Presentation mode for the select content |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### DatePicker.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | --------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger content (Value, TriggerIndicator) |
| `isDisabled` | `boolean` | - | Whether the trigger is disabled |
| `isInvalid` | `boolean` | - | When `true`, applies a danger border; inherits from root when omitted |
| `className` | `string` | - | Additional CSS classes for the trigger |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### DatePicker.Value
| prop | type | default | description |
| -------------- | ----------- | ----------------- | -------------------------------------------------- |
| `placeholder` | `string` | `'Choose a date'` | Text shown when no date is selected |
| `className` | `string` | - | Additional CSS classes for the value text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### DatePicker.TriggerIndicator
| prop | type | default | description |
| ----------------------- | --------------------------------- | ------- | ----------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom indicator content; defaults to a calendar icon when omitted |
| `iconProps` | `SelectTriggerIndicatorIconProps` | - | Overrides for the default icon |
| `isAnimatedStyleActive` | `boolean` | `false` | Whether animated rotation styles are applied |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `animation` | `SelectTriggerIndicatorAnimation` | `false` | Rotation animation configuration; disabled by default for calendar icon |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SelectTriggerIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | --------------------------- |
| `size` | `number` | `16` | Icon size in logical pixels |
| `color` | `string` | `muted` | Icon fill color |
### DatePicker.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------------- | ------- | ------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Portal content (Overlay, Content) |
| `hostName` | `string` | - | Optional name of the host element for the portal |
| `disableFullWindowOverlay` | `boolean` | `false` | Use a regular View instead of FullWindowOverlay on iOS |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay as a modal container (iOS) |
| `className` | `string` | - | Additional CSS classes for the portal container |
### DatePicker.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | ------------------------------------------------------- |
| `closeOnPress` | `boolean` | `true` | Whether to close the picker when the overlay is pressed |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated opacity styles are applied |
| `className` | `string` | - | Additional CSS classes for the overlay backdrop |
| `animation` | `SelectOverlayAnimation` | - | Opacity animation configuration |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### DatePicker.Content
The content component is a union type based on the `presentation` prop.
#### Popover presentation
| prop | type | default | description |
| -------------- | ------------------------------------------------ | --------------- | ----------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (DatePicker.Calendar) |
| `presentation` | `'popover'` | - | Popover presentation mode |
| `width` | `'content-fit' \| 'trigger' \| 'full' \| number` | `'content-fit'` | Content width sizing strategy |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentPopoverAnimation` | - | Keyframe animation configuration for entering/exiting |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Dialog presentation
| prop | type | default | description |
| -------------- | ------------------------ | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (DatePicker.Calendar) |
| `presentation` | `'dialog'` | - | Dialog presentation mode |
| `isSwipeable` | `boolean` | `true` | Whether the dialog can be swiped to dismiss |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentAnimation` | - | Keyframe animation configuration for scale/opacity |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Bottom sheet presentation
| prop | type | default | description |
| --------------------- | ------------------ | ------- | -------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (DatePicker.Calendar) |
| `presentation` | `'bottom-sheet'` | - | Bottom sheet presentation mode |
| `...BottomSheetProps` | `BottomSheetProps` | - | All @gorhom/bottom-sheet props are supported |
### DatePicker.Calendar
| prop | type | default | description |
| -------------------- | --------------------------- | --------------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Calendar compound parts (Calendar.Header, Calendar.Grid, etc.) |
| `value` | `DateValue \| null` | - | Overrides the calendar value derived from the root selection |
| `locale` | `string` | - | Overrides the root locale for the calendar grid |
| `accessibilityLabel` | `string` | `'Pick a date'` | Screen reader label for the calendar container |
| `onChange` | `(date: DateValue) => void` | - | Side-effect handler called before the default commit behavior |
| `...CalendarProps` | `CalendarProps` | - | All Calendar root props are supported (minValue, maxValue, etc.) |
## Hooks
### useDatePicker
Hook to access the DatePicker context. Must be used within a `DatePicker` component.
```tsx
import { useDatePicker } from 'heroui-native-pro';
const { value, commitDate, isOpen, formatLabel } = useDatePicker();
```
#### Returns: DatePickerContextValue
| property | type | description |
| ---------------- | ----------------------------------------------- | ------------------------------------------------------------------------ |
| `value` | `DatePickerOption \| undefined` | Current select option (ISO string + display label) |
| `onValueChange` | `(next: DatePickerOption \| undefined) => void` | Update the selected option |
| `isOpen` | `boolean` | Whether the calendar overlay is open |
| `onOpenChange` | `(open: boolean) => void` | Update the open state |
| `commitDate` | `(date: CalendarDate) => void` | Commit a date: updates the option, formats the label, closes the overlay |
| `formatLabel` | `(date: CalendarDate) => string` | Format a date using root `dateDisplayFormat` / `locale` / `formatDate` |
| `isDisabledRoot` | `boolean` | Whether the root is disabled |
| `locale` | `string \| undefined` | Root locale forwarded to `DatePicker.Calendar` |
# DateRangePicker
**Category**: native
**URL**: https://heroui.pro/docs/native/components/date-range-picker
> A date range picker that combines a trigger field with a range calendar popup for selecting start and end dates.
> `DateRangePicker` uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) for all date manipulations (`CalendarDate`, calendar systems, time zones, locale-aware formatting). For full context on the date types and helpers exposed through this component's props and callbacks, read the [`@internationalized/date` docs](https://react-aria.adobe.com/internationalized/date/) alongside this page.
## Import
```tsx
import { DateRangePicker } from 'heroui-native-pro';
```
## Anatomy
```tsx
.........
```
* **DateRangePicker**: Root container that manages date range selection state, open state, display formatting, and form field context (for Label, Description, FieldError). Supports controlled and uncontrolled modes.
* **DateRangePicker.Select**: Pre-wired Select root connected to the DateRangePicker context. State props (`value`, `isOpen`, `onValueChange`, `onOpenChange`) are managed by the root.
* **DateRangePicker.Trigger**: Pressable trigger button that opens the calendar overlay. Supports `variant` and inherits invalid border styling from the root.
* **DateRangePicker.Value**: Text display for the selected range label. Shows a placeholder when no range is selected.
* **DateRangePicker.TriggerIndicator**: Indicator icon inside the trigger. Defaults to a calendar icon instead of a chevron.
* **DateRangePicker.Portal**: Portal wrapper that re-provides DateRangePicker context across the portal boundary.
* **DateRangePicker.Overlay**: Backdrop overlay behind the calendar content.
* **DateRangePicker.Content**: Content container for the calendar popup. Supports `"popover"`, `"dialog"`, and `"bottom-sheet"` presentations.
* **DateRangePicker.Calendar**: Pre-wired RangeCalendar root that commits a completed range, updates the trigger label, and closes the overlay after selection. Two taps are required to complete a range (start, then end). Uses `RangeCalendar` compound parts as children.
## Usage
### Basic Usage
The DateRangePicker uses a popover presentation by default. Pass `RangeCalendar` compound parts as children of `DateRangePicker.Calendar`.
```tsx
{(day) => }
{(date) => }
```
### Controlled
Control the selected range externally with `value` and `onValueChange`. The option stores a JSON string with ISO start/end dates in `value` and a display label shown in the trigger. You can also control the calendar overlay with `isOpen` and `onOpenChange`.
```tsx
import type { DateRangePickerOption } from 'heroui-native-pro';
import { useState } from 'react';
const [selected, setSelected] = useState({
value: '{"start":"2026-04-01","end":"2026-04-07"}',
label: 'Apr 1, 2026 – Apr 7, 2026',
});
const [isOpen, setIsOpen] = useState(false);
{(day) => }
{(date) => }
;
```
### Dialog Presentation
Display the range calendar in a centered modal dialog.
```tsx
...
```
### Bottom Sheet Presentation
Display the range calendar in a bottom sheet.
```tsx
...
```
### Display Format
Configure how the selected range is displayed in the trigger using `dateDisplayFormat`.
```tsx
...
```
### Custom Format Function
Override the display label entirely with `formatDateRange`.
```tsx
function formatSpanishRange(start: CalendarDate, end: CalendarDate): string {
const fmt = new Intl.DateTimeFormat('es-ES', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
const a = fmt.format(start.toDate(getLocalTimeZone()));
const b = fmt.format(end.toDate(getLocalTimeZone()));
if (start.compare(end) === 0) return a;
return `${a} – ${b}`;
}
...Pick a start date, then an end date.;
```
### Custom Range Separator
Change the separator between formatted start and end dates in the trigger label.
```tsx
...
```
### Field States
Use root props for required, invalid, and disabled states.
```tsx
...Required for booking confirmation.
```
## Example
```tsx
import type { CalendarDate } from '@internationalized/date';
import { getLocalTimeZone } from '@internationalized/date';
import { Description, Label } from 'heroui-native';
import { DateRangePicker, RangeCalendar } from 'heroui-native-pro';
import { View } from 'react-native';
function formatSpanishDateRange(
start: CalendarDate,
end: CalendarDate
): string {
const fmt = new Intl.DateTimeFormat('es-ES', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
const a = fmt.format(start.toDate(getLocalTimeZone()));
const b = fmt.format(end.toDate(getLocalTimeZone()));
if (start.compare(end) === 0) return a;
return `${a} – ${b}`;
}
export default function DateRangePickerExample() {
return (
{(day) => }
{(date) => }
Pick a start date, then an end date.
);
}
```
## API Reference
### DateRangePicker
| prop | type | default | description |
| ------------------- | ----------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements (Label, DateRangePicker.Select, Description, FieldError) |
| `value` | `DateRangePickerOption` | - | Controlled selected option |
| `defaultValue` | `DateRangePickerOption` | - | Default selected option for uncontrolled usage |
| `isDisabled` | `boolean` | `false` | Whether the entire field is disabled |
| `isInvalid` | `boolean` | `false` | Whether the field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the field is required |
| `isOpen` | `boolean` | - | Controlled open state of the calendar overlay |
| `isDefaultOpen` | `boolean` | - | Initial open state for uncontrolled usage |
| `dateDisplayFormat` | `DateRangePickerDateDisplayFormat` | `'medium'` | Preset date label format; ignored when `formatDateRange` is set |
| `locale` | `string` | - | BCP 47 locale for label formatting and calendar grid |
| `formatDateRange` | `(start: CalendarDate, end: CalendarDate) => string` | - | Custom formatter that overrides `dateDisplayFormat` and `locale` for labels |
| `rangeSeparator` | `string` | `'–'` | Separator between start and end dates when using presets; same-day ranges collapse to a single date |
| `className` | `string` | - | Additional CSS classes for the root container |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the date range picker subtree |
| `onValueChange` | `(value: DateRangePickerOption \| undefined) => void` | - | Handler called when the selected option changes |
| `onOpenChange` | `(open: boolean) => void` | - | Handler called when the open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### DateRangePickerOption
| property | type | description |
| -------- | -------- | ------------------------------------------------------------------------------------------- |
| `value` | `string` | JSON string encoding start/end ISO dates (e.g. `{"start":"2026-04-01","end":"2026-04-07"}`) |
| `label` | `string` | Display string shown in the trigger (e.g. `"Apr 1, 2026 – Apr 7, 2026"`) |
#### DateRangePickerDateDisplayFormat
Built-in date label presets (maps to `Intl.DateTimeFormat` `dateStyle`):
* `'short'` — e.g. `"4/1/26 – 4/7/26"`
* `'medium'` — e.g. `"Apr 1, 2026 – Apr 7, 2026"` (default)
* `'long'` — e.g. `"April 1, 2026 – April 7, 2026"`
* `'full'` — e.g. `"Wednesday, April 1, 2026 – Tuesday, April 7, 2026"`
#### AnimationRootDisableAll
Animation configuration for the DateRangePicker root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### DateRangePicker.Select
| prop | type | default | description |
| -------------- | ----------------------------------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Select content (Trigger, Portal) |
| `isDisabled` | `boolean` | - | Overrides the root `isDisabled` when set |
| `presentation` | `'popover' \| 'dialog' \| 'bottom-sheet'` | `'popover'` | Presentation mode for the select content |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### DateRangePicker.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | --------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger content (Value, TriggerIndicator) |
| `isDisabled` | `boolean` | - | Whether the trigger is disabled |
| `isInvalid` | `boolean` | - | When `true`, applies a danger border; inherits from root when omitted |
| `className` | `string` | - | Additional CSS classes for the trigger |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### DateRangePicker.Value
| prop | type | default | description |
| -------------- | ----------- | ----------------------- | -------------------------------------------------- |
| `placeholder` | `string` | `'Choose a date range'` | Text shown when no range is selected |
| `className` | `string` | - | Additional CSS classes for the value text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### DateRangePicker.TriggerIndicator
| prop | type | default | description |
| ----------------------- | --------------------------------- | ------- | ----------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom indicator content; defaults to a calendar icon when omitted |
| `iconProps` | `SelectTriggerIndicatorIconProps` | - | Overrides for the default icon |
| `isAnimatedStyleActive` | `boolean` | `false` | Whether animated rotation styles are applied |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `animation` | `SelectTriggerIndicatorAnimation` | `false` | Rotation animation configuration; disabled by default for calendar icon |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SelectTriggerIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | --------------------------- |
| `size` | `number` | `16` | Icon size in logical pixels |
| `color` | `string` | `muted` | Icon fill color |
### DateRangePicker.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------------- | ------- | ------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Portal content (Overlay, Content) |
| `hostName` | `string` | - | Optional name of the host element for the portal |
| `disableFullWindowOverlay` | `boolean` | `false` | Use a regular View instead of FullWindowOverlay on iOS |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay as a modal container (iOS) |
| `className` | `string` | - | Additional CSS classes for the portal container |
### DateRangePicker.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | ------------------------------------------------------- |
| `closeOnPress` | `boolean` | `true` | Whether to close the picker when the overlay is pressed |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated opacity styles are applied |
| `className` | `string` | - | Additional CSS classes for the overlay backdrop |
| `animation` | `SelectOverlayAnimation` | - | Opacity animation configuration |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### DateRangePicker.Content
The content component is a union type based on the `presentation` prop.
#### Popover presentation
| prop | type | default | description |
| -------------- | ------------------------------------------------ | --------------- | ----------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (DateRangePicker.Calendar) |
| `presentation` | `'popover'` | - | Popover presentation mode |
| `width` | `'content-fit' \| 'trigger' \| 'full' \| number` | `'content-fit'` | Content width sizing strategy |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentPopoverAnimation` | - | Keyframe animation configuration for entering/exiting |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Dialog presentation
| prop | type | default | description |
| -------------- | ------------------------ | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (DateRangePicker.Calendar) |
| `presentation` | `'dialog'` | - | Dialog presentation mode |
| `isSwipeable` | `boolean` | `true` | Whether the dialog can be swiped to dismiss |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentAnimation` | - | Keyframe animation configuration for scale/opacity |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Bottom sheet presentation
| prop | type | default | description |
| --------------------- | ------------------ | ------- | -------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (DateRangePicker.Calendar) |
| `presentation` | `'bottom-sheet'` | - | Bottom sheet presentation mode |
| `...BottomSheetProps` | `BottomSheetProps` | - | All @gorhom/bottom-sheet props are supported |
### DateRangePicker.Calendar
| prop | type | default | description |
| ----------------------- | ------------------------------------------------ | --------------------- | ------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | RangeCalendar compound parts (RangeCalendar.Header, RangeCalendar.Grid, etc.) |
| `value` | `RangeValue \| null` | - | Overrides the calendar value derived from the root selection |
| `locale` | `string` | - | Overrides the root locale for the calendar grid |
| `accessibilityLabel` | `string` | `'Pick a date range'` | Screen reader label for the calendar container |
| `onChange` | `(value: RangeValue \| null) => void` | - | Side-effect handler called for range updates including `null` while restarting selection |
| `...RangeCalendarProps` | `RangeCalendarProps` | - | All RangeCalendar root props are supported (minValue, maxValue, allowsNonContiguousRanges, etc.) |
## Hooks
### useDateRangePicker
Hook to access the DateRangePicker context. Must be used within a `DateRangePicker` component.
```tsx
import { useDateRangePicker } from 'heroui-native-pro';
const { value, commitRange, isOpen, formatRangeLabel } = useDateRangePicker();
```
#### Returns: DateRangePickerContextValue
| property | type | description |
| ------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------------- |
| `value` | `DateRangePickerOption \| undefined` | Current select option (JSON range string + display label) |
| `onValueChange` | `(next: DateRangePickerOption \| undefined) => void` | Update the selected option |
| `isOpen` | `boolean` | Whether the calendar overlay is open |
| `onOpenChange` | `(open: boolean) => void` | Update the open state |
| `commitRange` | `(range: RangeValue) => void` | Commit a range: updates the option, formats the label, closes the overlay |
| `formatRangeLabel` | `(start: CalendarDate, end: CalendarDate) => string` | Format a range using root `dateDisplayFormat` / `locale` / `formatDateRange` |
| `isDisabledRoot` | `boolean` | Whether the root is disabled |
| `locale` | `string \| undefined` | Root locale forwarded to `DateRangePicker.Calendar` |
# DateTimePicker
**Category**: native
**URL**: https://heroui.pro/docs/native/components/date-time-picker
> A field shell that pairs a `Select` trigger with a `WheelDateTimePicker` presentation surface, exchanging an `@internationalized/date` `CalendarDateTime`.
## Import
```tsx
import { DateTimePicker } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **DateTimePicker**: Field shell owning selection, open state, and commit behavior. Forwards `minValue` / `maxValue` / `hourFormat` / `minuteInterval` / `locale` / `formatDate` to `DateTimePicker.Wheel`.
* **DateTimePicker.Select**: Wires `Select` (single mode) to the root state.
* **DateTimePicker.Portal**: Portals content and re-provides `DateTimePicker` context.
* **DateTimePicker.Overlay**: Backdrop behind portaled content.
* **DateTimePicker.Content**: Presentation surface (`popover` / `dialog` / `bottom-sheet`).
* **DateTimePicker.Trigger**: Trigger surface with invalid border styling.
* **DateTimePicker.Value**: Selected label / placeholder.
* **DateTimePicker.TriggerIndicator**: Trailing calendar icon (default).
* **DateTimePicker.Wheel**: Wheel date-time selector wired to commit on scroll; renders the default wheel parts (`WheelDate`, `WheelHour`, `WheelMinute`, `WheelPeriod` in 12-hour mode, `WheelIndicator`, `WheelMask`) when no children are passed.
## Usage
### Basic usage (managed state)
```tsx
```
### Controlled
Control the selected date-time externally with `value` and `onValueChange`. The option stores an ISO date-time string in `value` and a display label shown in the trigger. You can also control the wheel overlay with `isOpen` and `onOpenChange`.
```tsx
import type { DateTimePickerOption } from 'heroui-native-pro';
import { useState } from 'react';
const [selected, setSelected] = useState({
value: '2026-06-01T14:30:00',
label: 'Jun 1, 2026, 2:30 PM',
});
const [isOpen, setIsOpen] = useState(false);
;
```
### Bounded date range
Limit the selectable days with `minValue` / `maxValue`, forwarded to the wheel.
```tsx
import { today, getLocalTimeZone } from '@internationalized/date';
const start = today(getLocalTimeZone());
;
```
### 24-hour mode with interval
```tsx
```
### Custom label
Override the committed trigger label with `formatDateTime`.
```tsx
`${value.month}/${value.day} ${value.hour}:${String(value.minute).padStart(2, '0')}`
}
>
```
### Field states
`isRequired`, `isInvalid`, and `isDisabled` integrate with `Label`, `Description`, and `FieldError`.
```tsx
Please select a valid date and time.
```
## Example
```tsx
import type { CalendarDateTime } from '@internationalized/date';
import { Description, FieldError, Label } from 'heroui-native';
import { DateTimePicker } from 'heroui-native-pro';
import { View } from 'react-native';
function formatCompactDateTime(value: CalendarDateTime): string {
const hour12 = value.hour % 12 === 0 ? 12 : value.hour % 12;
const minute = String(value.minute).padStart(2, '0');
const marker = value.hour < 12 ? 'a.m.' : 'p.m.';
return `${value.month}/${value.day} · ${hour12}:${minute} ${marker}`;
}
export default function DateTimePickerExample() {
return (
Required to schedule the notification.Must be during business hours.Please select a valid cutoff date and time.
);
}
```
## API Reference
### DateTimePicker
| prop | type | default | description |
| ----------------------- | ---------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound children |
| `isDisabled` | `boolean` | `false` | Whether the entire field is disabled |
| `isInvalid` | `boolean` | `false` | Whether the field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the field is required |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration. Cascades `disable-all` to all children |
| `value` | `DateTimePickerOption` | - | Controlled selected option (single mode) |
| `defaultValue` | `DateTimePickerOption` | - | Uncontrolled initial selected option |
| `onValueChange` | `(value: DateTimePickerOption \| undefined) => void` | - | Called when the selected option changes |
| `isOpen` | `boolean` | - | Controlled open state of the select surface |
| `isDefaultOpen` | `boolean` | - | Uncontrolled initial open state |
| `onOpenChange` | `(open: boolean) => void` | - | Called when the open state changes |
| `minValue` | `CalendarDate` | `today` | Inclusive lower bound of the date column, forwarded to the wheel |
| `maxValue` | `CalendarDate` | `today + 1y` | Inclusive upper bound of the date column, forwarded to the wheel |
| `hourFormat` | `WheelDateTimePickerHourFormat` | `12` | Hour display mode, used for the wheel and label formatting |
| `minuteInterval` | `number` | `1` | Step between consecutive minute options, forwarded to the wheel |
| `dateTimeDisplayFormat` | `DateTimePickerDisplayFormat` | `short` | Preset used to build the trigger label. Ignored when `formatDateTime` is set |
| `locale` | `string` | `en-US` | BCP 47 locale for label formatting and the wheel's localized date / AM/PM labels |
| `formatDate` | `WheelDateTimePickerFormatDate` | - | Overrides the wheel's date column label formatting |
| `formatDateTime` | `(value: CalendarDateTime) => string` | - | Overrides `dateTimeDisplayFormat`, `hourFormat`, and `locale` for the committed label |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### DateTimePickerDisplayFormat
Built-in date-time label styles.
* `"short"`: month / day / year + hour and minute (e.g. `"Jun 1, 2026, 2:30 PM"`).
* `"medium"`: includes the weekday and seconds (e.g. `"Mon, Jun 1, 2026, 2:30:00 PM"`).
#### DateTimePickerOption
Single date-time option (same shape as single-mode `Select` value).
| prop | type | description |
| ------- | -------- | --------------------------------------------------- |
| `value` | `string` | ISO date-time string (e.g. `"2026-06-01T14:30:00"`) |
| `label` | `string` | Display text |
### DateTimePicker.Select
Wires `Select` (single mode) to the root state. Same props as `Select` minus the state props (`value` / `defaultValue` / `onValueChange` / `isOpen` / `isDefaultOpen` / `onOpenChange` / `selectionMode`).
### DateTimePicker.Portal
Portals content and re-provides `DateTimePicker` context. Same props as `Select.Portal`.
### DateTimePicker.Overlay
Backdrop behind portaled content. Same props as `Select.Overlay`.
### DateTimePicker.Content
Presentation surface. Same props as `Select.Content`, minus the dialog `isSwipeable` prop (`DateTimePicker` always disables dialog swipe-to-dismiss).
### DateTimePicker.Trigger
Trigger surface with invalid border styling. Extends `Select.Trigger` (minus `variant`).
| prop | type | default | description |
| ----------- | --------- | ------- | ---------------------------------------------------------------------------------- |
| `isInvalid` | `boolean` | - | When `true`, applies a 1.5px danger border. When omitted, uses `FormField` context |
### DateTimePicker.Value
Selected label / placeholder. Extends `Select.Value`.
| prop | type | default | description |
| ------------- | -------- | ------------------------ | ----------------------------------- |
| `placeholder` | `string` | `"Choose a date & time"` | Shown when no date-time is selected |
### DateTimePicker.TriggerIndicator
Trailing calendar icon (default). Same props as `Select.TriggerIndicator`.
### DateTimePicker.Wheel
Wheel date-time selector wired to commit on scroll. Same props as `WheelDateTimePicker` minus the value props (`value` / `defaultValue` / `onValueChange`), which are wired from `DateTimePicker` context. Each scroll updates the selected option live while the surface stays open.
### DateTimePicker.WheelDate / WheelHour / WheelMinute / WheelPeriod / WheelIndicator / WheelMask
Column and overlay parts aliasing the matching `WheelDateTimePicker` parts. Use them to customize column order, content, and styling inside `DateTimePicker.Wheel`.
# RangeCalendar
**Category**: native
**URL**: https://heroui.pro/docs/native/components/range-calendar
> A date range calendar for selecting start and end dates with month navigation, locale support, and customizable day cells.
> `RangeCalendar` uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) for all date manipulations (`CalendarDate`, calendar systems, time zones, locale-aware formatting). For full context on the date types and helpers exposed through this component's props and callbacks, read the [`@internationalized/date` docs](https://react-aria.adobe.com/internationalized/date/) alongside this page.
## Import
```tsx
import { RangeCalendar } from 'heroui-native-pro';
```
## Anatomy
### With Heading
```tsx
{(day) => }
{(date) => }
```
### With Year Picker
```tsx
{(day) => }
{(date) => }
{(renderProps) => (
)}
```
* **RangeCalendar**: Root container that manages date range selection state, locale, and animation settings. Supports controlled and uncontrolled modes with min/max constraints and date unavailability filtering.
* **RangeCalendar.Header**: Toolbar row for navigation controls and the month/year title.
* **RangeCalendar.Heading**: Month/year label text. The primitive computes the heading string automatically when children are omitted.
* **RangeCalendar.NavButton**: Previous or next month navigation button. Renders a default chevron icon using the theme accent color; override via `iconProps` or pass custom `children`.
* **RangeCalendar.Grid**: Month grid container that provides internal grid context to the header and body.
* **RangeCalendar.GridHeader**: Weekday labels row. Requires a render function `(day: string) => ReactElement` as children.
* **RangeCalendar.GridBody**: Day cells matrix. Requires a render function `(date: CalendarDate) => ReactElement` as children.
* **RangeCalendar.HeaderCell**: Single weekday header cell. Stringifiable children are wrapped in `HeaderCellLabel`; pass `day` when omitting children.
* **RangeCalendar.HeaderCellLabel**: Text slot for a weekday header cell label.
* **RangeCalendar.Cell**: Selectable day cell with range highlight strip styling. By default renders `CellBody` with `CellLabel` inside. Pass a render function as children to customize the cell content.
* **RangeCalendar.CellBody**: Inner rounded region of a day cell with press scale animation. Accent background is applied on range start/end cells. Pass `cellRenderProps` for data attribute selectors.
* **RangeCalendar.CellLabel**: Day number label. Uses `data-range-start` and `data-range-end` for accent foreground color. Pass `cellRenderProps` for data attribute selectors.
* **RangeCalendar.CellIndicator**: Dot marker under a day cell (e.g. for events). Pass `cellRenderProps` for `data-selected` styling.
* **RangeCalendar.YearPickerTrigger**: Pressable trigger that replaces `Heading` to toggle the year picker overlay.
* **RangeCalendar.YearPickerTriggerHeading**: Month/year label text inside the year picker trigger.
* **RangeCalendar.YearPickerTriggerIndicator**: Animated chevron icon indicating the year picker open state.
* **RangeCalendar.YearPickerGrid**: Overlay container positioned over the month grid when the year picker is open.
* **RangeCalendar.YearPickerGridBody**: Scrollable list of year cells inside the year picker grid.
* **RangeCalendar.YearPickerCell**: Pressable year cell that selects a year and closes the picker.
## Usage
### Basic Usage
The RangeCalendar uses the same compound structure as Calendar. Users select a start and end date by tapping two different days.
```tsx
{(day) => }
{(date) => }
```
### Controlled Value
Use `value` and `onChange` to control the selected range externally.
```tsx
const [range, setRange] = useState({
start: parseDate('2026-04-01'),
end: parseDate('2026-04-07'),
});
{(day) => }
{(date) => }
;
```
### Min and Max Dates
Restrict navigation and selection to a date range using `minValue` and `maxValue`.
```tsx
const now = today(getLocalTimeZone());
{(day) => }
{(date) => }
;
```
### Non-contiguous Ranges
Enable `allowsNonContiguousRanges` to allow ranges that span across unavailable dates.
```tsx
const isDateUnavailable = (date: DateValue) => {
return blockedRanges.some(
([start, end]) => date.compare(start) >= 0 && date.compare(end) <= 0
);
};
{(day) => {day}}
{(date) => }
;
```
### Year Picker
Add a year picker overlay by replacing `Heading` with `YearPickerTrigger` and adding a `YearPickerGrid` inside the root.
```tsx
{(day) => }
{(date) => }
{({ year, isSelected }) => (
)}
```
### Disabled State
Disable the entire calendar and all navigation controls.
```tsx
{(day) => }
{(date) => }
```
## Example
```tsx
import {
parseDate,
today,
getLocalTimeZone,
type DateValue,
} from '@internationalized/date';
import { RangeCalendar } from 'heroui-native-pro';
import { useState } from 'react';
import { View } from 'react-native';
export default function RangeCalendarExample() {
const now = today(getLocalTimeZone());
const blockedRanges = [
[now.add({ days: 2 }), now.add({ days: 5 })],
[now.add({ days: 12 }), now.add({ days: 13 })],
] as const;
const isDateUnavailable = (date: DateValue) => {
return blockedRanges.some(
([start, end]) => date.compare(start) >= 0 && date.compare(end) <= 0
);
};
return (
{(day) => (
{day}
)}
{(date) => }
);
}
```
## API Reference
### RangeCalendar
| prop | type | default | description |
| --------------------------- | -------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------ |
| `children` | `React.ReactNode \| ((props: { state: RangeCalendarState }) => React.ReactNode)` | - | Calendar content or render function receiving range calendar state |
| `value` | `RangeValue \| null` | - | Controlled selected date range |
| `defaultValue` | `RangeValue \| null` | - | Default selected date range for uncontrolled usage |
| `minValue` | `DateValue \| null` | - | Minimum selectable date; disables earlier dates and navigation |
| `maxValue` | `DateValue \| null` | - | Maximum selectable date; disables later dates and navigation |
| `isDateUnavailable` | `(date: DateValue) => boolean` | - | Callback to mark specific dates as unavailable |
| `allowsNonContiguousRanges` | `boolean` | - | Allow ranges that span across unavailable dates |
| `isDisabled` | `boolean` | `false` | Whether the entire calendar is disabled |
| `isReadOnly` | `boolean` | `false` | Whether the calendar value is immutable |
| `isInvalid` | `boolean` | - | Whether the current selection is invalid |
| `firstDayOfWeek` | `'sun' \| 'mon' \| 'tue' \| 'wed' \| 'thu' \| 'fri' \| 'sat'` | - | Override the first day of the week |
| `locale` | `string` | - | BCP 47 locale; defaults to the environment locale |
| `isYearPickerOpen` | `boolean` | - | Controlled open state for the year picker overlay |
| `defaultYearPickerOpen` | `boolean` | `false` | Initial open state for the year picker in uncontrolled mode |
| `className` | `string` | - | Additional CSS classes for the root container |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the calendar subtree |
| `onChange` | `(value: RangeValue>) => void` | - | Handler called when the selected range changes |
| `onYearPickerOpenChange` | `(isOpen: boolean) => void` | - | Handler called when the year picker open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### RangeValue
| property | type | description |
| -------- | ----------- | --------------------------- |
| `start` | `DateValue` | The start date of the range |
| `end` | `DateValue` | The end date of the range |
#### AnimationRootDisableAll
Animation configuration for the RangeCalendar root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### RangeCalendar.Header
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------- |
| `children` | `React.ReactNode` | - | Header content (Heading, NavButtons, etc.) |
| `className` | `string` | - | Additional CSS classes for the header row container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### RangeCalendar.Heading
| prop | type | default | description |
| -------------- | ----------------- | ------- | ---------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom heading text; auto-computed month/year when omitted |
| `className` | `string` | - | Additional CSS classes for the heading text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### RangeCalendar.NavButton
| prop | type | default | description |
| ------------------- | --------------------------------- | ------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom icon content; replaces the default chevron when provided |
| `slot` | `'previous' \| 'next'` | - | Navigation direction; determines which chevron icon is rendered |
| `isDisabled` | `boolean` | - | Merged with calendar `isDisabled` and range boundary state |
| `className` | `string` | - | Additional CSS classes for the pressable |
| `iconProps` | `RangeCalendarNavButtonIconProps` | - | Overrides for the built-in chevron; ignored with custom children |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### RangeCalendarNavButtonIconProps
| prop | type | default | description |
| ------- | -------- | -------------- | --------------------------- |
| `size` | `number` | `18` | Icon size in logical pixels |
| `color` | `string` | Theme `accent` | Icon stroke/fill color |
### RangeCalendar.Grid
| prop | type | default | description |
| -------------- | ------------------------------- | ------- | --------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Grid content (GridHeader, GridBody) |
| `offset` | `DateDuration` | - | Offset from the visible range start for multi-month grids |
| `weekdayStyle` | `'narrow' \| 'short' \| 'long'` | - | Weekday label format |
| `className` | `string` | - | Additional CSS classes for the grid container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### RangeCalendar.GridHeader
| prop | type | default | description |
| -------------- | ------------------------------------- | ------- | -------------------------------------------------------- |
| `children` | `(day: string) => React.ReactElement` | - | Render function called for each weekday label (required) |
| `className` | `string` | - | Additional CSS classes for the weekday row wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### RangeCalendar.GridBody
| prop | type | default | description |
| -------------- | -------------------------------------------- | ------- | ----------------------------------------------------------- |
| `children` | `(date: CalendarDate) => React.ReactElement` | - | Render function called for each day in the month (required) |
| `className` | `string` | - | Additional CSS classes for the grid body |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### RangeCalendar.HeaderCell
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom cell content; stringifiable children are wrapped in `HeaderCellLabel` |
| `day` | `string` | - | Weekday label string from `GridHeader`'s render callback; used when `children` is omitted |
| `className` | `string` | - | Additional CSS classes for the header cell container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### RangeCalendar.HeaderCellLabel
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Weekday label text |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### RangeCalendar.Cell
| prop | type | default | description |
| ------------------- | -------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------- |
| `date` | `CalendarDate` | - | The calendar date this cell represents (required) |
| `children` | `React.ReactNode \| ((renderProps: CalendarCellRenderProps) => React.ReactNode)` | - | Custom cell content; defaults to `CellBody` with `CellLabel` inside |
| `isDisabled` | `boolean` | - | Merged with calendar `isDisabled` and cell-specific disabled state |
| `className` | `string` | - | Additional CSS classes for the day cell pressable |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### CalendarCellRenderProps
Render props received by the `RangeCalendar.Cell` children render function.
| property | type | description |
| ----------------------- | -------------- | ------------------------------------------------------------------ |
| `date` | `CalendarDate` | The calendar date for this cell |
| `formattedDate` | `string` | Locale-formatted day number string |
| `isSelected` | `boolean` | Whether this date is currently selected |
| `isToday` | `boolean` | Whether this date is today |
| `isDisabled` | `boolean` | Whether this date is disabled |
| `isUnavailable` | `boolean` | Whether this date is unavailable via `isDateUnavailable` |
| `isOutsideMonth` | `boolean` | Whether this date is outside the currently visible month |
| `isFocused` | `boolean` | Whether this date is currently focused |
| `isInvalid` | `boolean` | Whether this date is invalid per `minValue`/`maxValue` constraints |
| `isPressed` | `boolean` | Whether the day cell pressable is in a pressed state |
| `isRangeStart` | `boolean` | First day of the highlighted range |
| `isRangeEnd` | `boolean` | Last day of the highlighted range |
| `isRangeFilled` | `boolean` | Whether the range spans more than one day |
| `isRangeMiddle` | `boolean` | Strictly inside the range, not start or end |
| `isRangeMiddleRowStart` | `boolean` | Range middle cell at the start of a row |
| `isRangeMiddleRowEnd` | `boolean` | Range middle cell at the end of a row |
### RangeCalendar.CellBody
| prop | type | default | description |
| ----------------------- | --------------------------- | ------- | ------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Body content (typically `CellLabel` and optional `CellIndicator`) |
| `cellRenderProps` | `CalendarCellRenderProps` | - | Render props from `RangeCalendar.Cell`'s children callback; drives `data-*` selectors |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated scale styles are applied; set `false` for custom logic |
| `className` | `string` | - | Additional CSS classes for the cell body container |
| `animation` | `CalendarCellBodyAnimation` | - | Press scale animation configuration |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Data Attributes
| attribute | values | description |
| --------------------------------- | --------- | --------------------------------------------- |
| `data-today` | `boolean` | Whether the date is today |
| `data-today-not-in-range` | `boolean` | Today and not a visible range endpoint/middle |
| `data-outside-month` | `boolean` | Whether the date is outside the visible month |
| `data-unavailable` | `boolean` | Whether the date is unavailable |
| `data-disabled` | `boolean` | Whether the date is disabled |
| `data-focused` | `boolean` | Whether the date is focused |
| `data-invalid` | `boolean` | Whether the date is invalid |
| `data-selected` | `boolean` | Whether the date is selected |
| `data-pressed` | `boolean` | Whether the cell is pressed |
| `data-range-start` | `boolean` | First day of the selected range |
| `data-range-end` | `boolean` | Last day of the selected range |
| `data-range-filled` | `boolean` | Range spans multiple days |
| `data-range-middle` | `boolean` | Inside the range, not start or end |
| `data-range-middle-row-start` | `boolean` | Range middle at the start of a row |
| `data-range-middle-row-end` | `boolean` | Range middle at the end of a row |
| `data-disabled-not-outside-month` | `boolean` | Disabled but within the visible month |
#### CalendarCellBodyAnimation
Animation configuration for `RangeCalendar.CellBody` press feedback. Can be:
* `false` or `"disabled"`: Disable press animation
* `undefined`: Use default animation
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------------- | ------------------ | ------------------- | ---------------------------------- |
| `scale.value` | `[number, number]` | `[1, 0.9]` | Scale values \[unpressed, pressed] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 120 }` | Animation timing configuration |
### RangeCalendar.CellLabel
| prop | type | default | description |
| ----------------- | ------------------------- | ------- | ------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Label text (usually the day number) |
| `cellRenderProps` | `CalendarCellRenderProps` | - | Render props from `RangeCalendar.Cell`'s children callback; drives `data-*` selectors |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
#### Data Attributes
Same data attributes as `RangeCalendar.CellBody`. See above.
### RangeCalendar.CellIndicator
| prop | type | default | description |
| ----------------- | ------------------------- | ------- | ------------------------------------------------------------------------------------- |
| `cellRenderProps` | `CalendarCellRenderProps` | - | Render props from `RangeCalendar.Cell`'s children callback; drives `data-*` selectors |
| `className` | `string` | - | Additional CSS classes for the indicator dot container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Data Attributes
Same data attributes as `RangeCalendar.CellBody`. See above.
### RangeCalendar.YearPickerTrigger
| prop | type | default | description |
| ------------------- | -------------------------------------------------------------------------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode \| ((values: YearPickerTriggerRenderProps) => React.ReactNode)` | - | Trigger content or render function |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### YearPickerTriggerRenderProps
| property | type | description |
| ----------- | ------------ | ----------------------------------- |
| `isOpen` | `boolean` | Whether the year picker is open |
| `monthYear` | `string` | Formatted month/year heading string |
| `toggle` | `() => void` | Toggle the year picker open state |
### RangeCalendar.YearPickerTriggerHeading
| prop | type | default | description |
| -------------- | -------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------- |
| `children` | `React.ReactNode \| ((values: YearPickerTriggerRenderProps) => React.ReactNode)` | - | Heading text or render function; auto-computed when omitted |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### RangeCalendar.YearPickerTriggerIndicator
| prop | type | default | description |
| ----------------------- | -------------------------------------------------------------------------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode \| ((values: YearPickerTriggerRenderProps) => React.ReactNode)` | - | Custom indicator content or render function |
| `iconProps` | `{ size?: number; color?: string }` | - | Overrides for the default chevron icon |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated rotation styles are applied |
| `animation` | `YearPickerIndicatorAnimation` | - | Rotation animation configuration |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### YearPickerIndicatorAnimation
Animation configuration for the year picker trigger chevron rotation. Can be:
* `false` or `"disabled"`: Disable rotation animation
* `undefined`: Use default animation
* `object`: Custom animation configuration
| prop | type | default | description |
| ----------------------- | ------------------ | -------------- | ------------------------------------- |
| `rotation.value` | `[number, number]` | `[0, 90]` | Rotation degrees \[closed, open] |
| `rotation.springConfig` | `WithSpringConfig` | Default spring | Spring configuration for the rotation |
### RangeCalendar.YearPickerGrid
| prop | type | default | description |
| ----------------------- | ------------------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Grid content (YearPickerGridBody) |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated opacity styles are applied |
| `animation` | `YearPickerGridAnimation` | - | Opacity animation configuration |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### YearPickerGridAnimation
Animation configuration for the year picker grid overlay. Can be:
* `false` or `"disabled"`: Disable opacity animation
* `undefined`: Use default animation
* `object`: Custom animation configuration
| prop | type | default | description |
| ---------------------- | ------------------ | ------------------- | ------------------------------------ |
| `opacity.value` | `[number, number]` | `[0, 1]` | Opacity values \[closed, open] |
| `opacity.timingConfig` | `WithTimingConfig` | `{ duration: 200 }` | Timing configuration for the opacity |
### RangeCalendar.YearPickerGridBody
| prop | type | default | description |
| ------------------ | -------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
| `children` | `(values: YearPickerCellRenderProps) => React.ReactNode` | - | Render function called for each year |
| `...FlatListProps` | `FlatListProps` | - | FlatList props except `data`, `renderItem`, `keyExtractor`, `numColumns`, and `columnWrapperStyle` |
#### YearPickerCellRenderProps
| property | type | description |
| --------------- | ------------ | --------------------------------------- |
| `year` | `number` | The year number |
| `formattedYear` | `string` | Locale-formatted year string |
| `isSelected` | `boolean` | Whether this year matches the selection |
| `isCurrentYear` | `boolean` | Whether this year is the current year |
| `isOpen` | `boolean` | Whether the year picker is open |
| `selectYear` | `() => void` | Select this year and close the picker |
### RangeCalendar.YearPickerCell
| prop | type | default | description |
| ------------------- | ----------------------------------------------------------------------------- | ------- | ------------------------------------------------------- |
| `year` | `number` | - | The year this cell represents (required) |
| `isSelected` | `boolean` | - | Whether this year is selected (required) |
| `children` | `React.ReactNode \| ((values: YearPickerCellRenderProps) => React.ReactNode)` | - | Custom cell content or render function |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
## Hooks
### useRangeCalendar
Hook to access the range calendar state context. Must be used within a `RangeCalendar` component.
```tsx
import { useRangeCalendar } from 'heroui-native-pro';
const state = useRangeCalendar();
```
#### Returns: RangeCalendarState
| property | type | description |
| ------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------- |
| `value` | `RangeValue \| null` | Currently selected date range |
| `setValue` | `(value: RangeValue \| null) => void` | Set the selected date range |
| `highlightedRange` | `RangeValue \| null` | The currently highlighted range (committed or preview) |
| `anchorDate` | `CalendarDate \| null` | Anchor date the user clicked to begin range selection |
| `setAnchorDate` | `(date: CalendarDate \| null) => void` | Set the anchor date |
| `isDragging` | `boolean` | Whether the user is currently dragging over the calendar |
| `setDragging` | `(isDragging: boolean) => void` | Set the dragging state |
| `highlightDate` | `(date: CalendarDate) => void` | Highlight a date during range selection |
| `clearSelection` | `() => void` | Clear the current selection |
| `visibleRange` | `RangeValue` | The date range currently visible in the calendar |
| `focusedDate` | `CalendarDate` | Currently focused date |
| `setFocusedDate` | `(value: CalendarDate) => void` | Set the focused date |
| `isDisabled` | `boolean` | Whether the calendar is disabled |
| `isReadOnly` | `boolean` | Whether the calendar is read-only |
| `isValueInvalid` | `boolean` | Whether the current value is invalid |
| `timeZone` | `string` | Time zone of displayed dates |
| `minValue` | `DateValue \| null \| undefined` | Minimum allowed date |
| `maxValue` | `DateValue \| null \| undefined` | Maximum allowed date |
| `focusNextPage` | `() => void` | Navigate to the next month |
| `focusPreviousPage` | `() => void` | Navigate to the previous month |
| `selectFocusedDate` | `() => void` | Select the currently focused date |
| `selectDate` | `(date: CalendarDate) => void` | Select a specific date |
| `isSelected` | `(date: CalendarDate) => boolean` | Check if a date is selected |
| `isInvalid` | `(date: CalendarDate) => boolean` | Check if a date is invalid |
| `isCellDisabled` | `(date: CalendarDate) => boolean` | Check if a date cell is disabled |
| `isCellUnavailable` | `(date: CalendarDate) => boolean` | Check if a date cell is unavailable |
| `isCellFocused` | `(date: CalendarDate) => boolean` | Check if a date cell is focused |
| `getDatesInWeek` | `(weekIndex: number, startDate?: CalendarDate) => Array` | Get dates for a week row |
# TimePicker
**Category**: native
**URL**: https://heroui.pro/docs/native/components/time-picker
> A time picker that combines a trigger field with a scrollable wheel popup for selecting an hour, minute, and optional AM/PM period.
> `TimePicker` uses [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) for time manipulation (`Time`, locale-aware formatting). The wheel exchanges a `Time` value internally, while the selected option stores an ISO time string. For full context on the time type exposed through `formatTime`, read the [`@internationalized/date` docs](https://react-aria.adobe.com/internationalized/date/) alongside this page.
## Import
```tsx
import { TimePicker } from 'heroui-native-pro';
```
## Anatomy
```tsx
...
```
* **TimePicker**: Root container that manages time selection state, open state, label formatting, and form field context (for Label, Description, FieldError). Supports controlled and uncontrolled modes.
* **TimePicker.Select**: Pre-wired Select root connected to the TimePicker context. State props (`value`, `isOpen`, `onValueChange`, `onOpenChange`) are managed by the root. Always single selection mode.
* **TimePicker.Trigger**: Pressable trigger button that opens the wheel overlay. Inherits invalid border styling from the root.
* **TimePicker.Value**: Text display for the selected time label. Shows a placeholder when no time is selected.
* **TimePicker.TriggerIndicator**: Indicator icon inside the trigger. Defaults to a clock icon instead of a chevron.
* **TimePicker.Portal**: Portal wrapper that re-provides TimePicker context across the portal boundary.
* **TimePicker.Overlay**: Backdrop overlay behind the wheel content.
* **TimePicker.Content**: Content container for the wheel popup. Supports `"popover"`, `"dialog"`, and `"bottom-sheet"` presentations. Dialog swipe-to-dismiss is always disabled, and the bottom sheet defaults to no pan-down-to-close.
* **TimePicker.Wheel**: Pre-wired wheel time selector that commits the selected time live on scroll and updates the trigger label while the surface stays open. Renders the default wheel parts when no children are passed.
* **TimePicker.WheelHour**: Hour column.
* **TimePicker.WheelMinute**: Minute column.
* **TimePicker.WheelPeriod**: AM/PM column. Rendered by default only in 12-hour mode.
* **TimePicker.WheelIndicator**: Shared selection band spanning every column.
* **TimePicker.WheelMask**: Top / bottom fade overlays. Defaults its gradient color to the overlay surface so it blends with the popup background.
## Usage
### Basic Usage
The TimePicker uses a popover presentation by default. Pass `TimePicker.Wheel` with no children to render the default hour, minute, period, indicator, and mask parts.
```tsx
```
### Dialog Presentation
Display the wheel in a centered modal dialog.
```tsx
```
### Bottom Sheet Presentation
Display the wheel in a bottom sheet.
```tsx
```
### Hour Format and Minute Interval
Use `hourFormat` to switch between 12-hour and 24-hour mode, and `minuteInterval` to control the step between minute options. In 24-hour mode the AM/PM column is omitted.
```tsx
```
### Custom Format Function
Override the trigger label entirely with `formatTime`.
```tsx
function formatCompactTime(time: Time): string {
const hour12 = time.hour % 12 === 0 ? 12 : time.hour % 12;
const minute = String(time.minute).padStart(2, '0');
const marker = time.hour < 12 ? 'a.m.' : 'p.m.';
return `${hour12}:${minute} ${marker}`;
}
;
```
### Field States
Use root props for required, invalid, and disabled states. Combine `isInvalid` with FieldError to display validation messages; the trigger shows a danger border.
```tsx
Must be during business hours.Please select a valid cutoff time.
```
## Example
```tsx
import type { Time } from '@internationalized/date';
import { Description, FieldError, Label } from 'heroui-native';
import { TimePicker } from 'heroui-native-pro';
import { View } from 'react-native';
function formatCompactTime(time: Time): string {
const hour12 = time.hour % 12 === 0 ? 12 : time.hour % 12;
const minute = String(time.minute).padStart(2, '0');
const marker = time.hour < 12 ? 'a.m.' : 'p.m.';
return `${hour12}:${minute} ${marker}`;
}
export default function TimePickerExample() {
return (
Required to schedule the notification.Must be during business hours.Please select a valid cutoff time.
);
}
```
## API Reference
### TimePicker
| prop | type | default | description |
| ------------------- | ------------------------------------------------ | --------- | --------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements (Label, TimePicker.Select, Description, FieldError) |
| `value` | `TimePickerOption` | - | Controlled selected option |
| `defaultValue` | `TimePickerOption` | - | Default selected option for uncontrolled usage |
| `isDisabled` | `boolean` | `false` | Whether the entire field is disabled |
| `isInvalid` | `boolean` | `false` | Whether the field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the field is required |
| `isOpen` | `boolean` | - | Controlled open state of the wheel overlay |
| `isDefaultOpen` | `boolean` | - | Initial open state for uncontrolled usage |
| `hourFormat` | `WheelTimePickerHourFormat` | `12` | Hour display mode; `12` adds an AM/PM marker, `24` omits it |
| `minuteInterval` | `number` | `1` | Step between consecutive minute options |
| `timeDisplayFormat` | `TimePickerTimeDisplayFormat` | `'short'` | Preset time label format; ignored when `formatTime` is set |
| `locale` | `string` | - | BCP 47 locale for label formatting and the wheel's AM/PM labels |
| `formatTime` | `(time: Time) => string` | - | Custom formatter that overrides `timeDisplayFormat`, `hourFormat`, `locale` |
| `className` | `string` | - | Additional CSS classes for the root container |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the time picker subtree |
| `onValueChange` | `(value: TimePickerOption \| undefined) => void` | - | Handler called when the selected option changes |
| `onOpenChange` | `(open: boolean) => void` | - | Handler called when the open state changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### TimePickerOption
| property | type | description |
| -------- | -------- | ------------------------------------------------------ |
| `value` | `string` | ISO time string (e.g. `"14:30:00"`) |
| `label` | `string` | Display string shown in the trigger (e.g. `"2:30 PM"`) |
#### TimePickerTimeDisplayFormat
Built-in time label presets:
* `'short'` — hour and minute only (e.g. `"2:30 PM"` / `"14:30"`) (default)
* `'medium'` — includes seconds (e.g. `"2:30:00 PM"`)
#### WheelTimePickerHourFormat
Hour display mode for the wheel and label formatting:
* `12` — twelve-hour clock with an AM/PM period column (default)
* `24` — twenty-four-hour clock without a period column
#### AnimationRootDisableAll
Animation configuration for the TimePicker root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### TimePicker.Select
| prop | type | default | description |
| -------------- | ----------------------------------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Select content (Trigger, Portal) |
| `isDisabled` | `boolean` | - | Overrides the root `isDisabled` when set |
| `presentation` | `'popover' \| 'dialog' \| 'bottom-sheet'` | `'popover'` | Presentation mode for the select content |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### TimePicker.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | --------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger content (Value, TriggerIndicator) |
| `isDisabled` | `boolean` | - | Whether the trigger is disabled |
| `isInvalid` | `boolean` | - | When `true`, applies a danger border; inherits from root when omitted |
| `className` | `string` | - | Additional CSS classes for the trigger |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### TimePicker.Value
| prop | type | default | description |
| -------------- | ----------- | ----------------- | -------------------------------------------------- |
| `placeholder` | `string` | `'Choose a time'` | Text shown when no time is selected |
| `className` | `string` | - | Additional CSS classes for the value text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### TimePicker.TriggerIndicator
| prop | type | default | description |
| ----------------------- | --------------------------------- | ------- | -------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom indicator content; defaults to a clock icon when omitted |
| `iconProps` | `SelectTriggerIndicatorIconProps` | - | Overrides for the default icon |
| `isAnimatedStyleActive` | `boolean` | `false` | Whether animated rotation styles are applied |
| `style` | `ViewStyle` | - | Inline style for the indicator container |
| `animation` | `SelectTriggerIndicatorAnimation` | `false` | Rotation animation configuration; disabled by default for clock icon |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SelectTriggerIndicatorIconProps
| prop | type | default | description |
| ------- | -------- | ------- | --------------------------- |
| `size` | `number` | `16` | Icon size in logical pixels |
| `color` | `string` | `muted` | Icon fill color |
### TimePicker.Portal
| prop | type | default | description |
| -------------------------------------------- | ----------------- | ------- | ------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Portal content (Overlay, Content) |
| `hostName` | `string` | - | Optional name of the host element for the portal |
| `disableFullWindowOverlay` | `boolean` | `false` | Use a regular View instead of FullWindowOverlay on iOS |
| `unstable_accessibilityContainerViewIsModal` | `boolean` | `false` | Controls whether VoiceOver treats the overlay as a modal container (iOS) |
| `className` | `string` | - | Additional CSS classes for the portal container |
### TimePicker.Overlay
| prop | type | default | description |
| ----------------------- | ------------------------ | ------- | ------------------------------------------------------- |
| `closeOnPress` | `boolean` | `true` | Whether to close the picker when the overlay is pressed |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated opacity styles are applied |
| `className` | `string` | - | Additional CSS classes for the overlay backdrop |
| `animation` | `SelectOverlayAnimation` | - | Opacity animation configuration |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### TimePicker.Content
The content component is a union type based on the `presentation` prop. The dialog `isSwipeable` prop is removed — TimePicker always disables dialog swipe-to-dismiss, and the bottom sheet defaults to no pan-down-to-close.
#### Popover presentation
| prop | type | default | description |
| -------------- | ------------------------------------------------ | --------------- | ----------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (TimePicker.Wheel) |
| `presentation` | `'popover'` | - | Popover presentation mode |
| `width` | `'content-fit' \| 'trigger' \| 'full' \| number` | `'content-fit'` | Content width sizing strategy |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentPopoverAnimation` | - | Keyframe animation configuration for entering/exiting |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Dialog presentation
| prop | type | default | description |
| -------------- | ------------------------ | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (TimePicker.Wheel) |
| `presentation` | `'dialog'` | - | Dialog presentation mode |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentAnimation` | - | Keyframe animation configuration for scale/opacity |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Bottom sheet presentation
| prop | type | default | description |
| --------------------- | ------------------ | ------- | -------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (TimePicker.Wheel) |
| `presentation` | `'bottom-sheet'` | - | Bottom sheet presentation mode |
| `...BottomSheetProps` | `BottomSheetProps` | - | All @gorhom/bottom-sheet props are supported |
### TimePicker.Wheel
Wired from TimePicker context; `value`, `defaultValue`, and `onValueChange` are managed by the root. Each scroll commits the selected option (formatted label + select value) live while the surface stays open. When `children` are omitted, the default wheel parts are rendered (period column only in 12-hour mode).
| prop | type | default | description |
| ---------------- | ------------------------------ | ---------- | ------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Wheel parts; defaults to Hour, Minute, Period (12h), Indicator, and Mask |
| `hourFormat` | `WheelTimePickerHourFormat` | root value | Overrides the root hour display mode for the wheel |
| `minuteInterval` | `number` | root value | Overrides the root step between minute options |
| `itemHeight` | `number` | `44` | Pixel height of a single row, shared by all columns |
| `visibleCount` | `number` | `5` | Number of visible rows, shared by all columns. Must be odd |
| `isDisabled` | `boolean` | `false` | Disables interaction for the whole wheel |
| `locale` | `string` | root value | Overrides the root locale for the wheel's AM/PM labels |
| `className` | `string` | - | Additional CSS classes for the wheel container |
| `animation` | `WheelTimePickerRootAnimation` | - | Animation configuration; cascades `disable-all` to the columns |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported |
### TimePicker.WheelHour
Hour column. The underlying `name` and `items` are owned by the wheel so the stored value stays correct.
| prop | type | default | description |
| -------------- | ------------------------------------ | ------- | ---------------------------------------------------------- |
| `isDisabled` | `boolean` | `false` | Disables interaction for this column |
| `className` | `string` | - | Additional CSS classes for the column container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual column slots |
| `styles` | `WheelPickerRootStyles` | - | Inline styles for individual column slots |
| `renderItem` | `WheelPickerRenderItem` | - | Custom row renderer; defaults to a `WheelPicker.ItemLabel` |
| `animation` | `WheelPickerRootAnimation` | - | Per-item opacity / scale interpolation configuration |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported |
### TimePicker.WheelMinute
Minute column. Same props as `TimePicker.WheelHour`; the underlying `name` and `items` are owned by the wheel.
### TimePicker.WheelPeriod
AM/PM column. Same props as `TimePicker.WheelHour`; the underlying `name` and `items` are owned by the wheel. Rendered by default only in 12-hour mode.
### TimePicker.WheelIndicator
Shared selection band spanning every column.
| prop | type | default | description |
| -------------- | ------------------------------------------------------------ | ------- | ----------------------------------------------------- |
| `children` | `React.ReactNode` | - | Optional content rendered inside the `highlight` slot |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual indicator slots |
| `styles` | `Partial>` | - | Inline styles for individual indicator slots |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ----------- | --------------------------------------------------------- |
| `wrapper` | Absolutely-positioned band centered on the wheel viewport |
| `highlight` | Filled rectangle rendered inside the wrapper |
### TimePicker.WheelMask
Top / bottom fade overlays. When `color` is omitted it defaults to the Select `overlay` surface color so the gradient blends with the popup background.
| prop | type | default | description |
| -------------- | ------------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `color` | `string` | `overlay` surface | Solid color the gradient fades from. Accepts any RN color string |
| `height` | `number \| string` | `"100%"` | Height of each mask half. `number` = raw pixels; percentage scales the default fade height (`((visibleCount - 1) / 4) * itemHeight`) |
| `className` | `string` | - | Additional CSS classes applied to both mask halves |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual mask slots |
| `styles` | `Partial>` | - | Inline styles for individual mask slots |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| -------- | ------------------- |
| `top` | Top fade overlay |
| `bottom` | Bottom fade overlay |
## Hooks
### useTimePicker
Hook to access the TimePicker context. Must be used within a `TimePicker` component.
```tsx
import { useTimePicker } from 'heroui-native-pro';
const { value, commitTime, isOpen, formatLabel } = useTimePicker();
```
#### Returns: TimePickerContextValue
| property | type | description |
| ---------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `value` | `TimePickerOption \| undefined` | Current select option (ISO time string + display label) |
| `onValueChange` | `(next: TimePickerOption \| undefined) => void` | Update the selected option |
| `isOpen` | `boolean` | Whether the wheel overlay is open |
| `onOpenChange` | `(open: boolean) => void` | Update the open state |
| `commitTime` | `(time: Time, options?: TimePickerCommitOptions) => void` | Commit a time: updates the option, formats the label, and closes unless `options.close` is `false` |
| `formatLabel` | `(time: Time) => string` | Format a time using root `timeDisplayFormat` / `hourFormat` / `locale` / `formatTime` |
| `hourFormat` | `WheelTimePickerHourFormat` | Root hour format forwarded to `TimePicker.Wheel` |
| `minuteInterval` | `number` | Root minute interval forwarded to `TimePicker.Wheel` |
| `locale` | `string \| undefined` | Root locale forwarded to `TimePicker.Wheel` |
| `isDisabledRoot` | `boolean` | Whether the root is disabled |
#### TimePickerCommitOptions
| property | type | default | description |
| -------- | --------- | ------- | ---------------------------------------------------- |
| `close` | `boolean` | `true` | Whether to close the select surface after committing |
# WheelDateTimePicker
**Category**: native
**URL**: https://heroui.pro/docs/native/components/wheel-date-time-picker
> A standalone wheel date-time selector built on `WheelPickerGroup` that exchanges an `@internationalized/date` `CalendarDateTime` value.
## Import
```tsx
import { WheelDateTimePicker } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **WheelDateTimePicker**: Root container. Owns the `CalendarDateTime` value, builds the column item data from `minValue` / `maxValue` / `hourFormat` / `minuteInterval` / `locale`, and drives the underlying `WheelPickerGroup`. When `children` are omitted it renders the full default set (`Date`, `Hour`, `Minute`, `Period` in 12-hour mode, `Indicator`, `Mask`).
* **WheelDateTimePicker.Date**: Combined day column spanning `[minValue, maxValue]`. The root owns its `name` and `items`; each option's value is an ISO calendar-date string (`"YYYY-MM-DD"`).
* **WheelDateTimePicker.Hour**: Hour column. Root-owned `name` / `items`.
* **WheelDateTimePicker.Minute**: Minute column stepped by the active `minuteInterval`. Root-owned `name` / `items`.
* **WheelDateTimePicker.Period**: AM/PM period column rendered by default in 12-hour mode. Root-owned `name` / `items`.
* **WheelDateTimePicker.Indicator**: Shared selection band spanning every column (`WheelPickerGroup.Indicator`).
* **WheelDateTimePicker.Mask**: Top / bottom fade overlays spanning the full group viewport (`WheelPickerGroup.Mask`).
## Usage
### Basic usage
Bind `value` / `onValueChange` to a `CalendarDateTime`. With no children the picker renders date, hour, minute, an AM/PM period column (12-hour default), indicator, and mask.
```tsx
import { CalendarDateTime } from '@internationalized/date';
const [value, setValue] = useState(new CalendarDateTime(2026, 6, 1, 9, 30));
;
```
### Uncontrolled
Pass `defaultValue` to seed the initial selection without managing external state.
```tsx
import { CalendarDateTime } from '@internationalized/date';
console.log(next)}
/>;
```
### Bounded date range
Limit the selectable days with `minValue` / `maxValue` (`CalendarDate`). When omitted, the range defaults to today through today + 1 year, and is always widened to include the active `value`.
```tsx
import { today, getLocalTimeZone } from '@internationalized/date';
const start = today(getLocalTimeZone());
;
```
### 24-hour mode
Set `hourFormat={24}` for a `0`–`23` hour column with no period column.
```tsx
```
### Minute interval
Step the minute column with `minuteInterval` for appointment-style selection.
```tsx
```
### Localized labels
Localize the date and AM/PM labels via `locale`. The stored period value stays the canonical `"AM"` / `"PM"`.
```tsx
```
### Custom date label
Override the date column label formatting with `formatDate`.
```tsx
isToday ? 'Today' : `${date.month}/${date.day}`
}
/>
```
### Commit on rest
`onValueCommit` fires exactly once after every column has come to rest.
```tsx
saveDateTime(next)}
/>
```
### Custom composition
Pass children to take full ownership of column order, content, and styling. Style each column through its `classNames` and the shared band through `WheelDateTimePicker.Indicator`.
```tsx
```
### Disabled
Block interaction and dim the picker with `isDisabled`.
```tsx
```
## API Reference
### WheelDateTimePicker
| prop | type | default | description |
| ---------------- | ----------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Compound children. When omitted, the root renders the full default set (`Date`, `Hour`, `Minute`, `Period` in 12-hour mode, `Indicator`, `Mask`) |
| `itemHeight` | `number` | `44` | Pixel height of a single row, shared by all columns |
| `visibleCount` | `number` | `5` | Number of visible rows, shared by all columns. Must be odd |
| `minValue` | `CalendarDate` | `today` | Inclusive lower bound of the selectable date column |
| `maxValue` | `CalendarDate` | `today + 1y` | Inclusive upper bound of the selectable date column |
| `hourFormat` | `WheelDateTimePickerHourFormat` | `12` | Hour display mode. Determines the hour column range and whether an AM/PM period column is rendered by default |
| `minuteInterval` | `number` | `1` | Step between consecutive minute options. Should be a positive integer that divides evenly into 60 |
| `locale` | `string` | `en-US` | BCP 47 locale used to localize the date and AM/PM labels. The stored period value remains the canonical `"AM"` / `"PM"` |
| `formatDate` | `WheelDateTimePickerFormatDate` | - | Overrides the default date column label formatting |
| `value` | `CalendarDateTime` | - | Controlled selected date-time |
| `defaultValue` | `CalendarDateTime` | - | Uncontrolled initial selected date-time |
| `isDisabled` | `boolean` | `false` | Disables interaction for every column |
| `className` | `string` | - | Additional CSS classes for the group container |
| `onValueChange` | `(value: CalendarDateTime) => void` | - | Fires whenever the selection changes — during scroll, on tap-to-focus, and on imperative column scrolls |
| `onValueCommit` | `(value: CalendarDateTime) => void` | - | Fires exactly once after every column has come to rest |
| `animation` | `WheelDateTimePickerRootAnimation` | - | Animation configuration. Cascades `disable-all` to the underlying group and its wheels |
| `ref` | `WheelDateTimePickerRootRef` | - | Forwarded to the underlying group root `View` |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported (minus the value-record props the root manages internally) |
#### WheelDateTimePickerHourFormat
Hour display mode (aliased from `WheelTimePickerHourFormat`).
* `12`: twelve-hour clock with an AM/PM period column.
* `24`: twenty-four-hour clock without a period column.
#### WheelDateTimePickerPeriod
Canonical day-period value stored on the period column. Always `"AM"` or `"PM"` regardless of the localized label shown to the user.
#### WheelDateTimePickerFormatDate
`(date: CalendarDate, context: { isToday: boolean }) => string`
Formats a `CalendarDate` into the label rendered on a row of the date column.
#### WheelDateTimePickerRootAnimation
Animation configuration for the root, aliased from `WheelPickerGroupRootAnimation`. The root owns no animated styles of its own — this prop only cascades the `disable-all` state to the underlying group and its wheels.
* `"disable-all"`: Disable all animations including the columns (rows snap without fading or scaling).
* `undefined`: Use default animations.
#### WheelDateTimePickerValues
Decomposed wheel selection used to bridge between a `CalendarDateTime` value and the group values record.
| prop | type | description |
| -------- | --------------------------- | -------------------------------------------------------------- |
| `date` | `string` | ISO calendar-date string (`"YYYY-MM-DD"`) for the selected day |
| `hour` | `number` | Hour value. `1`–`12` in 12-hour mode, `0`–`23` in 24-hour mode |
| `minute` | `number` | Minute value, snapped to the active `minuteInterval` |
| `period` | `WheelDateTimePickerPeriod` | Day period. Present only in 12-hour mode |
### WheelDateTimePicker.Date
Combined day column. The root owns `name` and `items`; the value and `onValueChange` are managed by the root via the group. Each option's value is an ISO calendar-date string. Extends `WheelPicker` props (minus `name` / `items`).
### WheelDateTimePicker.Hour
Hour column. The root owns `name` and `items`. Extends `WheelPicker` props (minus `name` / `items`). The `itemLabel` slot keeps numerals tabular by default.
### WheelDateTimePicker.Minute
Minute column. Same props as `WheelDateTimePicker.Hour`. The `itemLabel` slot keeps numerals tabular by default.
### WheelDateTimePicker.Period
AM/PM period column rendered by default in 12-hour mode. Same props as `WheelDateTimePicker.Hour`, with a `WheelDateTimePickerPeriod` value type. Root-owned `name` / `items`.
### WheelDateTimePicker.Indicator
Shared selection band spanning every column. Same props as `WheelPickerGroup.Indicator`.
### WheelDateTimePicker.Mask
Top / bottom fade overlays spanning the full group viewport. Same props as `WheelPickerGroup.Mask`.
# WheelTimePicker
**Category**: native
**URL**: https://heroui.pro/docs/native/components/wheel-time-picker
> A standalone wheel time selector built on `WheelPickerGroup` that exchanges an `@internationalized/date` `Time` value.
## Import
```tsx
import { WheelTimePicker } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **WheelTimePicker**: Root container. Owns the `Time` value, builds the column item data from `hourFormat` / `minuteInterval` / `locale`, and drives the underlying `WheelPickerGroup`. When `children` are omitted it renders the full default set (`Hour`, `Minute`, `Period` in 12-hour mode, `Indicator`, `Mask`).
* **WheelTimePicker.Hour**: Hour column. The root owns its `name` and `items` so the stored value stays correct.
* **WheelTimePicker.Minute**: Minute column stepped by the active `minuteInterval`. Root-owned `name` / `items`.
* **WheelTimePicker.Period**: AM/PM period column rendered by default in 12-hour mode. Root-owned `name` / `items`.
* **WheelTimePicker.Indicator**: Shared selection band spanning every column (`WheelPickerGroup.Indicator`).
* **WheelTimePicker.Mask**: Top / bottom fade overlays spanning the full group viewport (`WheelPickerGroup.Mask`).
## Usage
### Basic usage
Bind `value` / `onValueChange` to a `Time`. With no children the picker renders hour, minute, an AM/PM period column (12-hour default), indicator, and mask.
```tsx
import { Time } from '@internationalized/date';
const [time, setTime] = useState(new Time(9, 30));
;
```
### Uncontrolled
Pass `defaultValue` to seed the initial selection without managing external state.
```tsx
import { Time } from '@internationalized/date';
console.log(next)}
/>;
```
### 24-hour mode
Set `hourFormat={24}` for a `0`–`23` hour column with no period column.
```tsx
```
### Minute interval
Step the minute column with `minuteInterval` for appointment-style selection.
```tsx
```
### Localized period labels
Localize the AM/PM labels via `locale`. The stored value stays the canonical `"AM"` / `"PM"`.
```tsx
```
### Commit on rest
`onValueCommit` fires exactly once after every column has come to rest.
```tsx
saveTime(next)}
/>
```
### Custom composition
Pass children to take full ownership of column order, content, and styling. Style each column through its `classNames` and the shared band through `WheelTimePicker.Indicator`.
```tsx
```
### Custom item render
Pass `renderItem` to compose custom row content per column. Use `WheelPicker.Item` as the outer wrapper to preserve sizing and tap-to-focus.
```tsx
(
{item.label}
hour
)}
/>
```
### Disabled
Block interaction and dim the picker with `isDisabled`.
```tsx
```
## Example
```tsx
import { Time } from '@internationalized/date';
import { WheelTimePicker } from 'heroui-native-pro';
import { useState } from 'react';
import { Text, View } from 'react-native';
export default function MeetingTimePicker() {
const [time, setTime] = useState(new Time(9, 30));
return (
{String(time.hour).padStart(2, '0')}:
{String(time.minute).padStart(2, '0')}
);
}
```
## API Reference
### WheelTimePicker
| prop | type | default | description |
| ---------------- | ------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound children. When omitted, the root renders the full default set (`Hour`, `Minute`, `Period` in 12-hour mode, `Indicator`, `Mask`) |
| `itemHeight` | `number` | `44` | Pixel height of a single row, shared by all columns |
| `visibleCount` | `number` | `5` | Number of visible rows, shared by all columns. Must be odd |
| `hourFormat` | `WheelTimePickerHourFormat` | `12` | Hour display mode. Determines the hour column range and whether an AM/PM period column is rendered by default |
| `minuteInterval` | `number` | `1` | Step between consecutive minute options. Should be a positive integer that divides evenly into 60 |
| `locale` | `string` | `en-US` | BCP 47 locale used to localize the AM/PM period labels. The stored period value remains the canonical `"AM"` / `"PM"` |
| `value` | `Time` | - | Controlled selected time |
| `defaultValue` | `Time` | - | Uncontrolled initial selected time |
| `isDisabled` | `boolean` | `false` | Disables interaction for every column |
| `className` | `string` | - | Additional CSS classes for the group container |
| `onValueChange` | `(value: Time) => void` | - | Fires whenever the selection changes — during scroll, on tap-to-focus, and on imperative column scrolls |
| `onValueCommit` | `(value: Time) => void` | - | Fires exactly once after every column has come to rest |
| `animation` | `WheelTimePickerRootAnimation` | - | Animation configuration. Cascades `disable-all` to the underlying group and its wheels |
| `ref` | `WheelTimePickerRootRef` | - | Forwarded to the underlying group root `View` |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported (minus the value-record props the root manages internally) |
#### WheelTimePickerHourFormat
Hour display mode.
* `12`: twelve-hour clock with an AM/PM period column.
* `24`: twenty-four-hour clock without a period column.
#### WheelTimePickerPeriod
Canonical day-period value stored on the period column. Always `"AM"` or `"PM"` regardless of the localized label shown to the user.
#### WheelTimePickerRootAnimation
Animation configuration for the root, aliased from `WheelPickerGroupRootAnimation`. The root owns no animated styles of its own — this prop only cascades the `disable-all` state to the underlying group and its wheels.
* `"disable-all"`: Disable all animations including the columns (rows snap without fading or scaling).
* `undefined`: Use default animations.
#### WheelTimePickerValues
Decomposed wheel selection used to bridge between a `Time` value and the group values record.
| prop | type | description |
| -------- | ----------------------- | -------------------------------------------------------------- |
| `hour` | `number` | Hour value. `1`–`12` in 12-hour mode, `0`–`23` in 24-hour mode |
| `minute` | `number` | Minute value, snapped to the active `minuteInterval` |
| `period` | `WheelTimePickerPeriod` | Day period. Present only in 12-hour mode |
### WheelTimePicker.Hour
Hour column. The root owns `name` and `items`; the value and `onValueChange` are managed by the root via the group. Use the props below to customize row content and appearance. Extends `WheelPicker` props (minus `name` / `items`).
| prop | type | default | description |
| -------------- | ------------------------------------------------------------ | ----------------------- | ----------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound parts (e.g. a custom `WheelPicker.Mask`). The shared indicator is rendered by the root |
| `isDisabled` | `boolean` | `false` | Disables interaction for this column independently of the root |
| `className` | `string` | - | Additional CSS classes for the column container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual column slots. The `itemLabel` slot keeps numerals tabular by default |
| `styles` | `WheelPickerRootStyles` | - | Inline styles for individual column slots |
| `renderItem` | `WheelPickerRenderItem` | - | Custom row renderer. When omitted, the default renderer shows `item.label` inside a `WheelPicker.ItemLabel` |
| `keyExtractor` | `(item: WheelPickerOption, index: number) => string` | Primitive-aware default | Key extractor for the underlying `FlatList` |
| `animation` | `WheelPickerRootAnimation` | - | Animation configuration for the per-item opacity / scale interpolation |
| `ref` | `WheelTimePickerHourRef` | - | Imperative ref exposing `scrollToIndex` and `scrollToValue` in addition to the underlying view |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ------------------ | ------------------------------------------------------------------------- |
| `container` | Outer viewport wrapping the scroll list and overlays |
| `contentContainer` | Scroll content container carrier; receives the vertical centering padding |
| `item` | Per-row animated container |
| `itemLabel` | Default label text inside a row (tabular numerals by default) |
### WheelTimePicker.Minute
Minute column. Same props as `WheelTimePicker.Hour` (`WheelPicker` props minus `name` / `items`, root-owned). The `itemLabel` slot keeps numerals tabular by default.
### WheelTimePicker.Period
AM/PM period column rendered by default in 12-hour mode. Same props as `WheelTimePicker.Hour`, with a `WheelTimePickerPeriod` value type (`renderItem` receives `WheelPickerOption`). Root-owned `name` / `items`.
### WheelTimePicker.Indicator
Shared selection band spanning every column. Same props as `WheelPickerGroup.Indicator`.
| prop | type | default | description |
| -------------- | ------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Optional content rendered inside the indicator's `highlight` slot (patterns, gradients, icons). Pair with `overflow-hidden` on the highlight so the content is clipped to the rounded corners |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual indicator slots |
| `styles` | `Partial>` | - | Inline styles for individual indicator slots |
| `ref` | `WheelTimePickerIndicatorRef` | - | Forwarded to the underlying indicator `View` |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ----------- | --------------------------------------------------------- |
| `wrapper` | Absolutely-positioned band centered on the group viewport |
| `highlight` | Filled rectangle rendered inside the wrapper |
#### styles
| slot | type | description |
| ----------- | ----------- | ---------------------------------------- |
| `wrapper` | `ViewStyle` | Inline style for the indicator wrapper |
| `highlight` | `ViewStyle` | Inline style for the indicator highlight |
### WheelTimePicker.Mask
Top / bottom fade overlays spanning the full group viewport. Same props as `WheelPickerGroup.Mask`.
| prop | type | default | description |
| -------------- | ------------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `color` | `string` | `useThemeColor('background')` | Solid color the gradient fades from. Accepts any RN color string. Falls back to the theme `background` color when omitted |
| `height` | `number \| string` | `"100%"` | Height of each mask half. `number` = raw pixels; percentage scales the default fade height (`((visibleCount - 1) / 4) * itemHeight`) |
| `className` | `string` | - | Additional CSS classes applied to both mask halves |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual mask slots |
| `styles` | `Partial>` | - | Inline styles for individual mask slots |
| `ref` | `WheelTimePickerMaskRef` | - | Forwarded to the underlying mask `View` |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| -------- | ------------------- |
| `top` | Top fade overlay |
| `bottom` | Bottom fade overlay |
#### styles
| slot | type | description |
| -------- | ----------- | ---------------------------------------- |
| `top` | `ViewStyle` | Inline style for the top fade overlay |
| `bottom` | `ViewStyle` | Inline style for the bottom fade overlay |
# NumberValue
**Category**: native
**URL**: https://heroui.pro/docs/native/components/number-value
> A React Native number display with locale-aware currency, percentage, and compact formatting.
## Import
```tsx
import { NumberValue } from 'heroui-native-pro';
```
## Anatomy
```tsx
approx users
```
* **NumberValue**: Root `View` (laid out in a row with baseline alignment) that reads a numeric `value`, resolves the format options, and exposes the formatted string through context. When no children are provided, the root auto-renders `NumberValue.Value`. A render-function `children` is also supported for fully custom output (no wrapping container is rendered in that case).
* **NumberValue.Value**: Renders the formatted numeric string from the root context as a `Text`. Use this part to place the value explicitly when composing a custom layout around it.
* **NumberValue.Prefix**: Inline `Text` rendered before the value (e.g. a leading label or unit symbol).
* **NumberValue.Suffix**: Inline `Text` rendered after the value (e.g. a trailing label or unit symbol).
## Full `Intl.NumberFormat` compatibility
`NumberValue` relies on the runtime's built-in `Intl.NumberFormat`. On most modern React Native runtimes (Hermes with Intl enabled, or JSC on iOS) basic formatting — decimals, currency, percent, `signDisplay: "auto"` — works out of the box **without any polyfill**.
However, some advanced options are not available on every platform / JS engine version and require the [FormatJS](https://formatjs.github.io/) polyfills to behave identically to the web. If you plan to use any of the following, install the polyfills:
* `notation: "compact"` (e.g. `1.2K`, `3.4M`)
* `notation: "scientific"` / `"engineering"`
* `signDisplay: "always" | "exceptZero" | "never" | "negative"`
* `style: "unit"` (e.g. `kilometer-per-hour`)
* `currencyDisplay: "name"`, `currencySign: "accounting"`
* Non-default locales (anything other than the runtime's default, e.g. `de-DE`, `ja-JP`, `fr-FR`)
* Consistent behaviour across iOS, Android, and old Hermes builds
### Installation
```bash
yarn add @formatjs/intl-getcanonicallocales @formatjs/intl-locale @formatjs/intl-numberformat @formatjs/intl-pluralrules
```
### Import order
The polyfills must be imported **at the very top of your app's entry file** (e.g. `App.tsx`, `_layout.tsx`, `index.js`) and in **exactly this order** — each polyfill depends on the previous ones being loaded first.
```tsx
import '@formatjs/intl-getcanonicallocales/polyfill-force';
import '@formatjs/intl-locale/polyfill-force';
import '@formatjs/intl-numberformat/locale-data/en';
import '@formatjs/intl-numberformat/polyfill-force';
import '@formatjs/intl-pluralrules/locale-data/en';
import '@formatjs/intl-pluralrules/polyfill-force';
```
### Adding more locales
Each locale's data must be imported explicitly — the polyfills do not ship all locales by default to keep the bundle small. To support additional locales, add a `locale-data` import for every locale you want, for **both** `intl-numberformat` and `intl-pluralrules`:
```tsx
import '@formatjs/intl-getcanonicallocales/polyfill-force';
import '@formatjs/intl-locale/polyfill-force';
import '@formatjs/intl-numberformat/locale-data/en';
import '@formatjs/intl-numberformat/locale-data/de';
import '@formatjs/intl-numberformat/locale-data/fr';
import '@formatjs/intl-numberformat/locale-data/ja';
import '@formatjs/intl-numberformat/polyfill-force';
import '@formatjs/intl-pluralrules/locale-data/en';
import '@formatjs/intl-pluralrules/locale-data/de';
import '@formatjs/intl-pluralrules/locale-data/fr';
import '@formatjs/intl-pluralrules/locale-data/ja';
import '@formatjs/intl-pluralrules/polyfill-force';
```
> The `locale-data` imports must always come **before** the corresponding `polyfill-force` import of the same package. Only load the locales you actually use — every locale adds to the JS bundle size.
If you pass `locale="xx-XX"` as a prop but the matching locale data has not been imported, the polyfill silently falls back to the default locale, producing incorrect formatting — so double-check the imports match the locales referenced in your UI.
## Usage
### Basic Usage
Without children, the root auto-renders the formatted value using the `value` slot.
```tsx
```
### Currency
Use `numberStyle="currency"` together with a `currency` ISO code.
```tsx
```
### Percent
Values are interpreted as fractions — `0.033` displays as `3.3%`.
```tsx
```
### Compact Notation
Renders short scale abbreviations (`1.2K`, `3.4M`). Requires the FormatJS polyfills — see the compatibility section above.
```tsx
```
### Sign Display
Controls when a sign character is emitted. Useful for KPIs and deltas.
```tsx
```
### With Prefix and Suffix
Compose inline content around the value. When children are provided, include `NumberValue.Value` explicitly to position the formatted string.
```tsx
revenueapproxdownloads
```
### Raw `formatOptions` Pass-through
For advanced use cases (accounting sign, units, scientific notation), pass `formatOptions` directly. When provided, it overrides every individual convenience prop (`numberStyle`, `currency`, `unit`, `notation`, `signDisplay`, `minimumFractionDigits`, `maximumFractionDigits`).
```tsx
```
### Locale Override
Override the device locale for an individual instance. Requires the matching polyfill locale data (see compatibility section).
```tsx
```
### Styling Slots
The root exposes two slots — `container` (the outer `View`) and `value` (the inner `Text`). Use `classNames` to style both in one place, or target the value `Text` through the `classNames.value` slot.
```tsx
```
### Render-function Children
For fully custom rendering, pass a function that receives the formatted string. No wrapping container is rendered in this form.
```tsx
{(formatted) => (
${formatted}
)}
```
### Tabular Numbers
`NumberValue.Value` applies `fontVariant: tabular-nums` to its `Text` so digits occupy fixed-width cells — rows of numbers line up cleanly without manual alignment.
```tsx
```
### Disabling Animations for Descendants
`NumberValue` is a read-only text display and has no intrinsic animation. The `animation` prop is still exposed so that, when animated components are composed inside the value (e.g. a render-function layout), you can cascade a single `"disable-all"` setting to all of them via `AnimationSettingsProvider`.
```tsx
{(formatted) => (
{formatted}
)}
```
The root also respects the global animation settings and any parent `AnimationSettingsProvider`, using priority `global > parent > own` — so you rarely need to set this prop directly unless you want to locally opt out of animations for a subtree.
## Example
```tsx
import { NumberValue, Surface } from 'heroui-native-pro';
import { Text, View } from 'react-native';
export default function NumberValueExample() {
return (
Monthly revenue
/ month
Active users
users
Conversion
);
}
```
## API Reference
### NumberValue
| prop | type | default | description |
| ----------------------- | ----------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `value` | `number` | - | The numeric value to format. |
| `children` | `React.ReactNode \| (formatted: string) => React.ReactNode` | - | `ReactNode` to compose layout with `NumberValue.Value` / `Prefix` / `Suffix`, a render function receiving the formatted string, or `undefined` to auto-render the value. |
| `formatOptions` | `Intl.NumberFormatOptions` | - | Raw `Intl.NumberFormat` options. When provided, overrides every convenience prop below. |
| `locale` | `string` | device locale | BCP-47 locale identifier (e.g. `"en-US"`, `"de-DE"`). |
| `numberStyle` | `"currency" \| "decimal" \| "percent" \| "unit"` | `"decimal"` | Formatting style (mapped to `Intl.NumberFormat`'s `style`). |
| `currency` | `string` | - | ISO currency code. Required when `numberStyle="currency"`. |
| `unit` | `string` | - | Unit identifier (e.g. `"kilometer-per-hour"`). Required when `numberStyle="unit"`. |
| `notation` | `"compact" \| "engineering" \| "scientific" \| "standard"` | `"standard"` | Numeric notation. `"compact"` / `"scientific"` / `"engineering"` require the FormatJS polyfills. |
| `signDisplay` | `"always" \| "auto" \| "exceptZero" \| "never"` | `"auto"` | Controls when the sign character is emitted. Non-`"auto"` values require the FormatJS polyfills. |
| `minimumFractionDigits` | `number` | - | Minimum number of fraction digits. |
| `maximumFractionDigits` | `number` | - | Maximum number of fraction digits. |
| `className` | `string` | - | Additional CSS classes merged into the `container` slot. |
| `classNames` | `ElementSlots` | - | CSS classes per root slot (see below). |
| `style` | `StyleProp` | - | Inline style applied to the outer `View` (merged after `styles.container`). |
| `styles` | `NumberValueRootStyles` | - | Inline styles per root slot (see below). |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration. `NumberValue` has no intrinsic animation; this prop exists to cascade the `"disable-all"` setting to animated descendants (see below). |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported. |
#### `AnimationRootDisableAll`
Animation configuration for the `NumberValue` root. `NumberValue` itself is a read-only display and has no intrinsic animation — this prop only cascades the disabled state to any animated descendants rendered inside the value (via `AnimationSettingsProvider`). Can be:
* `"disable-all"`: Disable all animations for animated descendants composed inside `NumberValue` (cascades down).
* `undefined`: Inherit the animation settings from the parent / global context.
#### `ElementSlots`
| prop | type | description |
| ----------- | -------- | ------------------------------------------------ |
| `container` | `string` | Class names for the outer `View` container slot. |
| `value` | `string` | Class names for the inner value `Text` slot. |
#### `NumberValueRootStyles`
| prop | type | description |
| ----------- | ----------- | ---------------------------------------------- |
| `container` | `ViewStyle` | Inline style applied to the outer `View` slot. |
| `value` | `TextStyle` | Inline style applied to the inner `Text` slot. |
### NumberValue.Value
Renders the formatted numeric string from the nearest `NumberValue` context. The root's `value` slot class names and inline styles are forwarded automatically so a consumer-placed `Value` matches the auto-rendered default.
| prop | type | default | description |
| -------------- | ----------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | context `formatted` | Override the rendered text. Useful for rare cases where you want to display something other than the formatted string. |
| `className` | `string` | - | Additional CSS classes merged with the root's `value` slot classes. |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported. |
### NumberValue.Prefix
Inline `Text` placed before the value. Use for leading labels, unit symbols, or modifier words such as `approx`.
| prop | type | default | description |
| -------------- | ----------- | ------- | ---------------------------------------------------- |
| `children` | `ReactNode` | - | Prefix content (typically a string). |
| `className` | `string` | - | Additional CSS classes applied to the prefix `Text`. |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported. |
### NumberValue.Suffix
Inline `Text` placed after the value. Use for trailing labels (e.g. `/ month`, `users`) or unit symbols.
| prop | type | default | description |
| -------------- | ----------- | ------- | ---------------------------------------------------- |
| `children` | `ReactNode` | - | Suffix content (typically a string). |
| `className` | `string` | - | Additional CSS classes applied to the suffix `Text`. |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported. |
### useNumberValue
Hook to access the `NumberValue` root context. Must be used within a `NumberValue` component — primarily useful when building custom parts that need to read the formatted string or inherit the root's `value` slot styling.
```tsx
import { useNumberValue } from 'heroui-native-pro';
const { formatted, valueClassName, valueStyle } = useNumberValue();
```
#### Returns
| property | type | description |
| ---------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `formatted` | `string` | Locale-formatted representation of the root's `value`. |
| `valueClassName` | `Exclude` | Class names configured on the root's `value` slot (via `classNames.value`). Forward to keep styling consistent with the auto-rendered default. |
| `valueStyle` | `StyleProp` | Inline style configured on the root's `value` slot (via `styles.value`). Forward to keep styling consistent with the auto-rendered default. |
# ProgressBar
**Category**: native
**URL**: https://heroui.pro/docs/native/components/progress-bar
> A progress bar shows either determinate or indeterminate progress of an operation over time.
## Import
```tsx
import { ProgressBar } from 'heroui-native-pro';
```
## Anatomy
```tsx
...
```
* **ProgressBar**: Root container that manages progress state, formatting, and variant configuration. Computes percentage and formatted value text from `value`, `minValue`, `maxValue`, and `formatOptions`. When plain string children are provided, they auto-expand into Label, ValueLabel, Track, and Fill.
* **ProgressBar.Track**: Background container for the fill element. Applies rounded corners, overflow hidden, and size-based height.
* **ProgressBar.TrackBackground**: Optional theme-aware background container rendered behind the track surface. Mounted automatically when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **ProgressBar.Fill**: Animated element representing filled progress. Automatically switches between determinate (width animation) and indeterminate (translateX sweep) based on the root's `isIndeterminate` prop.
* **ProgressBar.Label**: Text describing the progress operation.
* **ProgressBar.ValueLabel**: Displays the formatted progress value with tabular figures for consistent digit alignment. Hidden when indeterminate.
## Usage
### Basic usage
Compose the label row and the track with fill manually for full control.
```tsx
Loading
```
### String children shortcut
Pass a string as children to auto-render the label row, track, and fill.
```tsx
Loading
```
### Sizes
Switch the track height with the `size` prop.
```tsx
.........
```
### Colors
Switch the fill color with the `color` prop.
```tsx
...............
```
### Indeterminate
Set `isIndeterminate` to render a looping sweep animation when progress is unknown. The value label is hidden in this mode.
```tsx
Loading...
```
### Without label
Omit `ProgressBar.Label` and `ProgressBar.ValueLabel` to render only the track. Provide `accessibilityLabel` for screen readers.
```tsx
```
### Disabled
Set `isDisabled` to lower the opacity and mark the component as disabled for accessibility.
```tsx
...
```
### Custom value range
Set `minValue` and `maxValue` to use a custom progress range.
```tsx
...
```
### Custom value format
Pass `formatOptions` to format the displayed value with `Intl.NumberFormat` options.
```tsx
...
```
### Custom gradient fill
Apply a gradient background to `ProgressBar.Fill` via the `style` prop.
```tsx
Sunset
```
### Render function children
Use a render function to access progress state for custom layouts.
```tsx
{({ percentage, valueText, isIndeterminate }) => (
<>
>
)}
```
## Example
```tsx
import { ProgressBar } from 'heroui-native-pro';
import { View } from 'react-native';
export default function ProgressBarExample() {
return (
Loading
);
}
```
## API Reference
### ProgressBar
| prop | type | default | description |
| ----------------- | ------------------------------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode \| ((props: ProgressBarRenderProps) => ReactNode)` | - | Children elements or render function with access to progress state. String children auto-expand sub-parts |
| `size` | `ProgressBarSize` | `"md"` | Size of the progress track |
| `color` | `ProgressBarColor` | `"accent"` | Color of the fill bar |
| `value` | `number` | `0` | The current progress value |
| `minValue` | `number` | `0` | The minimum value of the progress range |
| `maxValue` | `number` | `100` | The maximum value of the progress range |
| `isIndeterminate` | `boolean` | `false` | Whether progress is indeterminate (unknown duration) |
| `isDisabled` | `boolean` | `false` | Whether the component is disabled |
| `className` | `string` | - | Additional CSS classes for the root container |
| `formatOptions` | `Intl.NumberFormatOptions` | `{ style: 'percent' }` | Number format options for the value display |
| `animation` | `ProgressBarRootAnimation` | - | Animation configuration for the root component |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ProgressBarSize
| type | description |
| ---------------------- | ----------------------------------- |
| `'sm' \| 'md' \| 'lg'` | Size variants of the progress track |
#### ProgressBarColor
| type | description |
| ------------------------------------------------------------- | ------------------------------ |
| `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | Color variants of the fill bar |
#### ProgressBarRenderProps
| prop | type | description |
| ----------------- | --------- | --------------------------------- |
| `percentage` | `number` | Computed percentage (0–100) |
| `valueText` | `string` | Formatted value text |
| `isIndeterminate` | `boolean` | Whether progress is indeterminate |
#### ProgressBarRootAnimation
Animation configuration for the ProgressBar root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### ProgressBar.Track
| prop | type | default | description |
| -------------- | ----------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `ReactNode` | - | Content to display inside the track (typically `ProgressBar.Fill`) |
| `className` | `string` | - | Additional CSS classes for the track container |
| `background` | `ReactNode` | - | Background layer behind the track surface. `undefined` renders the theme-aware default; custom node replaces it; `null` removes it |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ProgressBar.TrackBackground
Absolute-fill container rendered behind the track surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `children` | `ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ProgressBar.Fill
> Note: `width` and `transform` (translateX) are occupied by animations and cannot be set via `className`. To fully control the fill, set `isAnimatedStyleActive={false}`.
| prop | type | default | description |
| ----------------------- | -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------- |
| `isAnimatedStyleActive` | `boolean` | `true` | When `false`, animated styles (width / translateX) are not applied, allowing full control via className/style |
| `className` | `string` | - | Additional CSS classes for the fill element |
| `animation` | `ProgressBarFillAnimation` | - | Animation configuration for the fill element |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ProgressBarFillAnimation
Animation configuration for `ProgressBar.Fill`. Can be:
* `false` or `"disabled"`: Disable fill animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------------------- | ---------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------- |
| `fillTimingConfig` | `AnimationValue` | `{ duration: 300 }` | Timing configuration for the determinate fill width transition |
| `indeterminateFillTimingConfig` | `AnimationValue` | `{ duration: 1500, easing: Easing.bezier(0.65,0,0.35,1) }` | Timing configuration for the indeterminate sweep animation |
### ProgressBar.Label
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `children` | `ReactNode` | - | Text content for the label |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### ProgressBar.ValueLabel
| prop | type | default | description |
| -------------- | ----------- | ------- | ------------------------------------------------------------------------------------ |
| `children` | `ReactNode` | - | Custom content to override the formatted value text. Defaults to the formatted value |
| `className` | `string` | - | Additional CSS classes for the value label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useProgressBar
Hook to access the ProgressBar context. Must be used within a `ProgressBar` component.
```tsx
import { useProgressBar } from 'heroui-native-pro';
const {
percentage,
valueText,
isIndeterminate,
isDisabled,
size,
color,
trackWidth,
onTrackLayout,
} = useProgressBar();
```
#### Returns: ProgressBarContextValue
| property | type | description |
| ----------------- | ------------------------- | ------------------------------------------------- |
| `percentage` | `number` | Computed percentage (0–100) of current progress |
| `valueText` | `string` | Formatted value text (e.g. `"60%"`) |
| `isIndeterminate` | `boolean` | Whether progress is indeterminate |
| `isDisabled` | `boolean` | Whether the component is disabled |
| `size` | `ProgressBarSize` | Current size variant |
| `color` | `ProgressBarColor` | Current color variant |
| `trackWidth` | `number` | Measured track width in pixels |
| `onTrackLayout` | `(width: number) => void` | Callback for `Track` to report its measured width |
# ProgressCircle
**Category**: native
**URL**: https://heroui.pro/docs/native/components/progress-circle
> A circular progress indicator that shows determinate or indeterminate progress of an operation over time.
## Import
```tsx
import { ProgressCircle } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **ProgressCircle**: Root container that manages progress state, formatting, and variant configuration. Computes percentage and formatted value text from `value`, `minValue`, `maxValue`, and `formatOptions`.
* **ProgressCircle.Indicator**: SVG rendering of the track and fill circles. Automatically switches between determinate (animated `strokeDashoffset`) and indeterminate (continuous rotation) modes based on the root's `isIndeterminate` prop. Accepts `strokeWidth`, `trackColor`, and `fillColor` overrides.
* **ProgressCircle.ValueLabel**: Text centered on the circle displaying the formatted progress value. Renders with tabular figures for consistent digit alignment. Hidden when indeterminate.
## Usage
### Basic usage
Render the indicator inside the root.
```tsx
```
### Sizes
Switch the circle size with the `size` prop using a preset name.
```tsx
.........
```
### Custom size
Pass a number to `size` for a custom pixel dimension.
```tsx
```
### Colors
Switch the fill arc color with the `color` prop.
```tsx
...............
```
### Indeterminate
Set `isIndeterminate` to render a looping spin animation when progress is unknown. The value label is hidden in this mode.
```tsx
```
### With value label
Add `ProgressCircle.ValueLabel` to display the formatted value centered on the circle.
```tsx
```
### Custom value label content
Pass children to `ProgressCircle.ValueLabel` to render custom content instead of the formatted value.
```tsx
1119Remaining
```
### Custom stroke width
Pass `strokeWidth` to `ProgressCircle.Indicator` to control the thickness of the track and fill arcs.
```tsx
```
### Custom colors
Override the resolved theme colors with the `trackColor` and `fillColor` props on the indicator.
```tsx
```
### Disabled
Set `isDisabled` to lower the opacity and mark the component as disabled for accessibility.
```tsx
```
### Custom value range
Set `minValue` and `maxValue` to use a custom progress range.
```tsx
```
### Custom value format
Pass `formatOptions` to format the displayed value with `Intl.NumberFormat` options.
```tsx
```
### Render function children
Use a render function to access progress state for custom layouts.
```tsx
{({ percentage, valueText, isIndeterminate }) => }
```
## Example
```tsx
import { ProgressCircle } from 'heroui-native-pro';
import { View } from 'react-native';
export default function ProgressCircleExample() {
return (
);
}
```
## API Reference
### ProgressCircle
| prop | type | default | description |
| ----------------- | ---------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------ |
| `children` | `ReactNode \| ((props: ProgressCircleRenderProps) => ReactNode)` | - | Children elements or render function with access to progress state |
| `size` | `ProgressCircleSize` | `"md"` | Size of the circle. Accepts a preset name or a custom pixel value |
| `color` | `ProgressCircleColor` | `"accent"` | Color of the progress arc |
| `value` | `number` | `0` | The current progress value |
| `minValue` | `number` | `0` | The minimum value of the progress range |
| `maxValue` | `number` | `100` | The maximum value of the progress range |
| `isIndeterminate` | `boolean` | `false` | Whether progress is indeterminate (unknown duration) |
| `isDisabled` | `boolean` | `false` | Whether the component is disabled |
| `className` | `string` | - | Additional CSS classes for the root container |
| `formatOptions` | `Intl.NumberFormatOptions` | `{ style: 'percent' }` | Number format options for the value display |
| `animation` | `ProgressCircleRootAnimation` | - | Animation configuration for the root component |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ProgressCircleSize
| type | description |
| -------------------------------- | -------------------------------------------------------------- |
| `'sm' \| 'md' \| 'lg' \| number` | Preset size name (`24`, `36`, `48` px) or a custom pixel value |
#### ProgressCircleColor
| type | description |
| ------------------------------------------------------------- | ------------------------------ |
| `'default' \| 'accent' \| 'success' \| 'warning' \| 'danger'` | Color variants of the fill arc |
#### ProgressCircleRenderProps
| prop | type | description |
| ----------------- | --------- | --------------------------------- |
| `percentage` | `number` | Computed percentage (0–100) |
| `valueText` | `string` | Formatted value text |
| `isIndeterminate` | `boolean` | Whether progress is indeterminate |
#### ProgressCircleRootAnimation
Animation configuration for the ProgressCircle root. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### ProgressCircle.Indicator
| prop | type | default | description |
| -------------- | ---------------------------------- | ------- | -------------------------------------------------------------------------------- |
| `strokeWidth` | `number` | `4` | Stroke width of the track and fill arcs |
| `trackColor` | `string` | - | Override color for the track circle stroke. Defaults to the theme's `default` |
| `fillColor` | `string` | - | Override color for the fill circle stroke. Defaults to the resolved `color` prop |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `animation` | `ProgressCircleIndicatorAnimation` | - | Animation configuration for the indicator |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ProgressCircleIndicatorAnimation
Animation configuration for `ProgressCircle.Indicator`. Can be:
* `false` or `"disabled"`: Disable indicator animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------ | ---------------------------------- | ------------------------------------------- | -------------------------------------------------------------------- |
| `fillTimingConfig` | `AnimationValue` | `{ duration: 300 }` | Timing configuration for the determinate strokeDashoffset transition |
| `spinTimingConfig` | `AnimationValue` | `{ duration: 1000, easing: Easing.linear }` | Timing configuration for the indeterminate spin rotation |
### ProgressCircle.ValueLabel
| prop | type | default | description |
| -------------- | ----------- | ------- | ------------------------------------------------------------------------------------ |
| `children` | `ReactNode` | - | Custom content to override the formatted value text. Defaults to the formatted value |
| `className` | `string` | - | Additional CSS classes for the value label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useProgressCircle
Hook to access the ProgressCircle context. Must be used within a `ProgressCircle` component.
```tsx
import { useProgressCircle } from 'heroui-native-pro';
const { percentage, valueText, isIndeterminate, isDisabled, size, color } =
useProgressCircle();
```
#### Returns: ProgressCircleContextValue
| property | type | description |
| ----------------- | --------------------- | ----------------------------------------------- |
| `percentage` | `number` | Computed percentage (0–100) of current progress |
| `valueText` | `string` | Formatted value text (e.g. `"60%"`) |
| `isIndeterminate` | `boolean` | Whether progress is indeterminate |
| `isDisabled` | `boolean` | Whether the component is disabled |
| `size` | `ProgressCircleSize` | Current size variant |
| `color` | `ProgressCircleColor` | Current color variant |
# Rating
**Category**: native
**URL**: https://heroui.pro/docs/native/components/rating
> A React Native star rating input with fractional read-only values, custom icons, sizes, and selection.
## Import
```tsx
import { Rating } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **Rating**: Root container built on `heroui-native`'s `RadioGroup`. Manages the numeric value, auto-renders items from `1` to `maxValue` when children are omitted, and propagates size/icon/read-only state via context.
* **Rating.Item**: Individual rating item. Wraps `RadioGroup.Item` and renders the default icon plus a clipped overlay for partial fills. Supports render-function children for fully custom indicators.
## Usage
### Basic usage
Auto-renders 5 items and calls `onValueChange` with the selected integer value.
```tsx
const [value, setValue] = useState(3);
;
```
### Uncontrolled
```tsx
```
### Sizes
Control item size with the `size` prop.
```tsx
```
### Read-only fractional
`isReadOnly` disables interaction and enables fractional fills (e.g. `3.5`, `4.2`). The partial fill is achieved by clipping a second copy of the icon, so any solid-fill icon can be used.
```tsx
```
### Max value
Ratings are not limited to 5 items — set `maxValue` to render any number of items.
```tsx
```
### Disabled
```tsx
```
### Custom icon
Pass an `icon` prop with any SVG component. The component is expected to accept `size`, `color`, and `colorClassName` props so `Rating` can drive its dimensions and active / inactive colors. Wrap the SVG with `withUniwind` to enable `colorClassName`:
```tsx
import type { FC } from 'react';
import Svg, { Path, type SvgProps } from 'react-native-svg';
import { withUniwind } from 'uniwind';
interface HeartIconProps extends SvgProps {
size?: number;
color?: string;
colorClassName?: string;
}
const HeartIconBase: FC = ({
size = 24,
color = 'currentColor',
...rest
}) => (
);
const HeartIcon = withUniwind(HeartIconBase, {
color: { fromClassName: 'colorClassName', styleProperty: 'accentColor' },
});
} defaultValue={3} />;
```
### Customizing icon size and colors
Use `iconProps` on the root to override size and active / inactive colors for every item. When both a raw color (`activeColor` / `inactiveColor`) and a className (`activeColorClassName` / `inactiveColorClassName`) are provided, the className wins for theme-aware styling.
```tsx
}
iconProps={{
size: 28,
activeColorClassName: 'accent-danger',
inactiveColorClassName: 'accent-muted/20',
}}
defaultValue={3}
/>
```
### Custom indicator
Pass a render-function child to `Rating.Item` for a fully custom indicator. The function receives `{ isActive, isPartial, partialPercent }`.
```tsx
{[1, 2, 3, 4, 5].map((itemValue) => (
{({ isActive, partialPercent }) => (
{partialPercent > 0 && partialPercent < 100
? `${(itemValue - 1 + partialPercent / 100).toFixed(1)}`
: itemValue}
)}
))}
```
## Example
```tsx
import { Rating } from 'heroui-native-pro';
import { Text, View } from 'react-native';
import { useState } from 'react';
export default function RatingExample() {
const [value, setValue] = useState(4);
return (
Rating: {value}
);
}
```
## API Reference
### Rating
| prop | type | default | description |
| --------------- | ------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
| `value` | `number` | - | Controlled rating value (integer; fractional only when `isReadOnly`) |
| `defaultValue` | `number` | - | Uncontrolled default rating value |
| `onValueChange` | `(value: number) => void` | - | Callback fired when the selected rating changes. Always receives an integer |
| `maxValue` | `number` | `5` | Maximum rating value. Controls the number of items auto-rendered when `children` is absent |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of the rating items |
| `isReadOnly` | `boolean` | `false` | Disables interaction and enables fractional `value` for partial fills |
| `isDisabled` | `boolean` | `false` | Disables the rating entirely (opacity + no interaction) |
| `icon` | `ReactNode` | - | Custom icon element used by every item (item-level `icon` wins). Must accept `size`, `color`, and `colorClassName` props |
| `iconProps` | `RatingIconProps` | - | Shared icon styling (size + active / inactive colors) applied to every item |
| `className` | `string` | - | Additional CSS classes applied to the root |
Extends [heroui-native `RadioGroup`](https://heroui.com/docs/native/components/radio-group#api-reference) except for `value`, `defaultValue`, `onValueChange`, and `children` which are overridden to accept numeric ratings.
#### RatingIconProps
Shared icon styling forwarded to the default star or any custom `icon` component. When both a raw color and a className are set, the className wins — the raw color is used as a fallback.
| prop | type | default | description |
| ------------------------ | -------- | --------------------------- | ------------------------------------------------------------ |
| `size` | `number` | Derived from root `size` | Icon size in pixels |
| `activeColor` | `string` | - | Raw fill color for active / partially active items |
| `inactiveColor` | `string` | - | Raw fill color for inactive items |
| `activeColorClassName` | `string` | `'accent-warning'` | Uniwind `colorClassName` for active / partially active items |
| `inactiveColorClassName` | `string` | `'accent-surface-tertiary'` | Uniwind `colorClassName` for inactive items |
### Rating.Item
The underlying pressable is an animated `RadioGroup.Item` that plays a subtle scale animation on press. Customize or disable it via the `animation` prop (or disable animated styles entirely with `isAnimatedStyleActive={false}`).
| prop | type | default | description |
| ----------------------- | --------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------- |
| `value` | `number` | - | 1-based numeric value of this item |
| `icon` | `ReactNode` | - | Custom icon for this specific item (overrides the root-level `icon`) |
| `children` | `ReactNode \| ((p: RatingItemRenderProps) => Node)` | - | Content or render function for a fully custom indicator |
| `animation` | `RatingItemAnimation` | - | Animation configuration for the press-scale feedback. Pass `false` or `'disabled'` to disable |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether the animated press-scale style is applied. Set to `false` to disable animated styles and apply your own |
| `className` | `string` | - | Additional CSS classes applied to the item pressable |
Extends [heroui-native `RadioGroup.Item`](https://heroui.com/docs/native/components/radio-group#radiogroupitem) except for `value` and `children` which are overridden.
#### RatingItemRenderProps
| prop | type | description |
| ---------------- | --------- | ------------------------------------------------------------------ |
| `isActive` | `boolean` | Whether the item is considered active (filled or partially filled) |
| `isPartial` | `boolean` | Whether the item is partially filled (read-only mode only) |
| `partialPercent` | `number` | Partial fill percentage in the 0-100 range |
#### RatingItemAnimation
Animation configuration for the item press-scale feedback. Can be:
* `false` or `'disabled'`: Disable the item press animation
* `undefined`: Use default animation
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------------- | ------------------ | ------------------- | ----------------------------------- |
| `scale.value` | `[number, number]` | `[1, 0.9]` | Scale values `[unpressed, pressed]` |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Animation timing configuration |
## Hooks
### useRating
Hook to access the Rating context. Must be used within a `Rating` component.
```tsx
import { useRating } from 'heroui-native-pro';
const { value, maxValue, size, isReadOnly, isDisabled, iconProps } =
useRating();
```
#### Returns
| property | type | description |
| ------------ | ----------------- | ---------------------------------------------------------- |
| `value` | `number` | Current rating value (may be fractional in read-only mode) |
| `maxValue` | `number` | Current maximum rating value |
| `size` | `RatingSize` | Current size |
| `isReadOnly` | `boolean` | Whether the rating is read-only |
| `isDisabled` | `boolean` | Whether the rating is disabled |
| `icon` | `ReactNode` | Shared default icon (may be `undefined`) |
| `iconProps` | `RatingIconProps` | Shared icon styling (may be `undefined`) |
# TrendChip
**Category**: native
**URL**: https://heroui.pro/docs/native/components/trend-chip
> A React Native trend chip for mobile metrics with direction, percentage, icon, and contextual suffix.
## Import
```tsx
import { TrendChip } from 'heroui-native-pro';
```
## Anatomy
```tsx
............
```
* **TrendChip**: Root container built on top of `heroui-native`'s `Chip`. Derives the chip color from the `trend` prop (`up` -> success, `neutral` -> warning, `down` -> danger).
* **TrendChip.Indicator**: Renders the default trend arrow. Pass a custom SVG child to replace it; the child inherits size and color from the chip context.
* **TrendChip.Value**: Numeric content rendered as a `Chip.Label` with tabular figures so digits align vertically across chips.
* **TrendChip.Prefix**: Optional inline label rendered before the value (e.g. `$`, `+`).
* **TrendChip.Suffix**: Optional inline label rendered after the value, muted by default (e.g. `%`, `vs last month`).
## Usage
### Basic usage
Pass a string or number as children and the default indicator + value layout is applied automatically.
```tsx
+12.4%
```
### Trend direction
Choose the trend direction with the `trend` prop. This sets both the arrow icon and the chip color.
```tsx
+12.4%0.0%-3.2%
```
### Variants
Switch visual styles with the `variant` prop.
```tsx
+12.4%+12.4%+12.4%+12.4%
```
### Sizes
Control the chip size with the `size` prop.
```tsx
+4.1%+4.1%+4.1%
```
### Custom indicator
Pass a custom SVG child to `TrendChip.Indicator` to replace the default arrow. Size and color are inherited from the chip context.
```tsx
+42.0%
```
### Prefix and suffix
Compose the chip content with `TrendChip.Prefix`, `TrendChip.Value`, and `TrendChip.Suffix`. Use `TrendChip.Indicator` without children to keep the default arrow.
```tsx
+$1,248-5.9vs last month
```
## Example
```tsx
import { TrendChip } from 'heroui-native-pro';
import { Text, View } from 'react-native';
const ROWS = [
{ label: 'Revenue', value: '+112.4%', trend: 'up' },
{ label: 'Signups', value: '+8.00%', trend: 'up' },
{ label: 'Churn', value: '-3.20%', trend: 'down' },
{ label: 'Traffic', value: '+0.10%', trend: 'neutral' },
] as const;
export default function TrendChipExample() {
return (
{ROWS.map((row) => (
{row.label}{row.value}
))}
);
}
```
## API Reference
### TrendChip
| prop | type | default | description |
| --------- | -------------------------------------------------- | -------- | ----------------------------------------------------------------------- |
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Size of the chip |
| `trend` | `TrendDirection` | `'up'` | Trend direction. Drives the default arrow and the underlying chip color |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'soft'` | `'soft'` | Visual variant of the chip |
Extends [heroui-native `Chip`](https://heroui.com/docs/native/components/chip#api-reference) except for `size`, `color`, and `variant`.
#### TrendDirection
| type | description |
| ----------------------------- | ---------------------------------------------------------------------------------------------- |
| `'up' \| 'down' \| 'neutral'` | Trend direction. Maps to `success`, `danger`, `warning` on the underlying `Chip`, respectively |
### TrendChip.Indicator
| prop | type | default | description |
| ---------------- | ----------------------------------------- | ------- | --------------------------------------------------------------------------------- |
| `children` | `React.ReactElement` | - | Custom icon element. Cloned with `size` / `color` / `colorClassName` from context |
| `size` | `number` | - | Icon size in pixels. Defaults to a size matched to the chip's `size` |
| `color` | `string` | - | Icon stroke color. Overrides the color resolved from the chip context |
| `colorClassName` | `string` | - | Uniwind `colorClassName` used to derive the icon color from the theme |
| `className` | `string` | - | Additional CSS classes for the indicator wrapper |
| `...SvgProps` | `SvgProps` | - | All `react-native-svg` `Svg` props are supported |
#### TrendArrowIconProps
| prop | type | default | description |
| ---------------- | ---------- | ---------------- | ------------------------------------------------------------------ |
| `size` | `number` | `12` | Icon size in pixels |
| `color` | `string` | `"currentColor"` | Icon stroke color |
| `colorClassName` | `string` | - | Uniwind `colorClassName` mapped to `accentColor` via `withUniwind` |
| `...SvgProps` | `SvgProps` | - | All `react-native-svg` `Svg` props are supported |
### TrendChip.Value
Extends [heroui-native `Chip.Label`](https://heroui.com/docs/native/components/chip#chiplabel). Renders with tabular figures so digits align vertically across chips.
### TrendChip.Prefix
Extends [heroui-native `Chip.Label`](https://heroui.com/docs/native/components/chip#chiplabel). Rendered inline before the value.
### TrendChip.Suffix
Extends [heroui-native `Chip.Label`](https://heroui.com/docs/native/components/chip#chiplabel). Rendered inline after the value with a muted color.
## Hooks
### useTrendChip
Hook to access the TrendChip context. Must be used within a `TrendChip` component.
```tsx
import { useTrendChip } from 'heroui-native-pro';
const { size, trend, variant } = useTrendChip();
```
#### Returns
| property | type | description |
| --------- | -------------------------------------------------- | ------------------------ |
| `size` | `'sm' \| 'md' \| 'lg'` | Current size of the chip |
| `trend` | `'up' \| 'down' \| 'neutral'` | Current trend direction |
| `variant` | `'primary' \| 'secondary' \| 'tertiary' \| 'soft'` | Current visual variant |
# Autocomplete
**Category**: native
**URL**: https://heroui.pro/docs/native/components/autocomplete
> An autocomplete combines a select with filtering, allowing users to search and select from a list of options.
> `Autocomplete` extends the heroui-native `Select` (selection, open state, and presentation) and `SearchField` (in-content filtering). Selection lives on the trigger while the search input is rendered inside the portaled content — it is a searchable select, not a free-text combo box.
## Import
```tsx
import { Autocomplete } from 'heroui-native-pro';
```
## Anatomy
```tsx
...
```
* **Autocomplete**: Root that wraps `Select` and manages the search text, item filtering, clear behavior, and form field context (for Label, Description, FieldError). Supports single and multiple selection, controlled and uncontrolled modes.
* **Autocomplete.Trigger**: Pressable field surface that toggles the overlay. Inherits invalid border styling from the root.
* **Autocomplete.Value**: Selected label(s) or placeholder (defaults to `"Select an item"`).
* **Autocomplete.ClearButton**: Optional close button inside the trigger row that clears the selection and calls `onClear`. Hidden while nothing is selected.
* **Autocomplete.TriggerIndicator**: Chevron indicator that rotates with the open state.
* **Autocomplete.Portal**: Portal wrapper that re-provides the Autocomplete context across the portal boundary.
* **Autocomplete.Overlay**: Backdrop overlay behind the content. Tinted for the dialog and bottom-sheet presentations; transparent for popovers, where it only provides tap-outside dismissal.
* **Autocomplete.Content**: Content container. Supports `"popover"`, `"dialog"`, and `"bottom-sheet"` presentations.
* **Autocomplete.SearchField**: Search input wired to the autocomplete search text. Renders a default `SearchField` composition when no children are given.
* **Autocomplete.List**: Scrollable list container (`keyboardShouldPersistTaps="handled"`) with a capped height.
* **Autocomplete.Item**: Selectable option filtered by the search text. `textValue` overrides `label` for matching.
* **Autocomplete.ItemLabel / ItemDescription / ItemIndicator**: Item content parts (same as `Select`).
* **Autocomplete.ListLabel**: Section label for grouped items.
* **Autocomplete.Empty**: Fallback shown when no item matches the search text (also covers an empty collection).
* **Autocomplete.Close**: Close button for the overlay (useful for dialog and bottom-sheet presentations).
## Usage
### Basic Usage
The autocomplete uses a popover presentation by default. Items are filtered as the user types with a case- and diacritic-insensitive "contains" match.
```tsx
```
### With Clear Button
Add `Autocomplete.ClearButton` to the trigger row. It stays hidden while nothing is selected; pressing it clears the selection and calls `onClear` from the root.
```tsx
...
```
### Multiple Selection
Set `selectionMode="multiple"`. The trigger shows a formatted list of the selected labels, and items stay open on press.
```tsx
import type { AutocompleteOption } from 'heroui-native-pro';
import { useState } from 'react';
const [selected, setSelected] = useState([]);
...;
```
### Controlled
Control the selected option with `value` / `onValueChange`, the overlay with `isOpen` / `onOpenChange`, and the search text with `inputValue` / `onInputChange`.
```tsx
import type { AutocompleteOption } from 'heroui-native-pro';
import { useState } from 'react';
const [selected, setSelected] = useState({
value: 'ca',
label: 'California',
});
const [isOpen, setIsOpen] = useState(false);
const [search, setSearch] = useState('');
......;
```
### Custom Filter
Pass a `filter` predicate to replace the default "contains" match — e.g. a "starts with" match, or an always-true filter when the item collection is filtered externally (async search).
```tsx
textValue.toLowerCase().startsWith(inputValue.trim().toLowerCase())
}
>
...
```
### Filter Text Override
Use `textValue` on items to match against different text than the visible label (e.g. keywords).
```tsx
```
### Sections
Group items with `Autocomplete.ListLabel`.
```tsx
West CoastEast Coast
```
### Empty State
`Autocomplete.Empty` renders only when no registered item matches the current search text. String (or omitted) children render styled muted text; custom nodes render as-is.
```tsx
...No matching states.
```
### Dialog and Bottom Sheet Presentations
Match `presentation` between the root and `Autocomplete.Content`, exactly like `Select`.
The default `Autocomplete.SearchField` input autofocuses when the overlay opens, for every presentation. Because of that, the bottom sheet defaults to a fixed `snapPoints={['90%']}` with `enableDynamicSizing={false}` so its content sits above the keyboard from the start, together with `keyboardBehavior="extend"`, `keyboardBlurBehavior="restore"`, `android_keyboardInputMode="adjustResize"`, and bottom-sheet-aware focus/blur handlers on the input. Selecting an item that closes the overlay also dismisses the keyboard. All of these props can be overridden (e.g. `autoFocus={false}` on `Autocomplete.SearchField`).
```tsx
...
```
For the dialog presentation, position the content below the top safe-area inset (instead of the centered default) so the keyboard cannot cover it:
```tsx
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const insets = useSafeAreaInsets();
...
;
```
### Field States
Use root props for required, invalid, and disabled states. Combine `isInvalid` with `FieldError` to display validation messages; the trigger shows a danger border.
```tsx
...Where you currently live.Please select a state.
```
## Example
```tsx
import { Label } from 'heroui-native';
import { Autocomplete } from 'heroui-native-pro';
import { View } from 'react-native';
const STATES = [
{ value: 'ca', label: 'California' },
{ value: 'tx', label: 'Texas' },
{ value: 'fl', label: 'Florida' },
{ value: 'ny', label: 'New York' },
{ value: 'wa', label: 'Washington' },
];
export default function AutocompleteExample() {
return (
{STATES.map((state) => (
))}
);
}
```
## API Reference
### Autocomplete
Extends all `Select` root props (generic on `selectionMode`).
| prop | type | default | description |
| ------------------- | -------------------------------------------- | ----------- | -------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements (Label, Trigger, Portal, Description, FieldError) |
| `selectionMode` | `'single' \| 'multiple'` | `'single'` | Whether single or multiple selection is enabled |
| `value` | `AutocompleteOption \| AutocompleteOption[]` | - | Controlled selected option(s), typed by `selectionMode` |
| `defaultValue` | `AutocompleteOption \| AutocompleteOption[]` | - | Default selected option(s) for uncontrolled usage |
| `isOpen` | `boolean` | - | Controlled open state of the overlay |
| `isDefaultOpen` | `boolean` | - | Initial open state for uncontrolled usage |
| `presentation` | `'popover' \| 'bottom-sheet' \| 'dialog'` | `'popover'` | Presentation mode (must match `Autocomplete.Content`) |
| `isDisabled` | `boolean` | `false` | Whether the entire field is disabled |
| `isInvalid` | `boolean` | `false` | Whether the field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the field is required |
| `inputValue` | `string` | - | Controlled search text |
| `defaultInputValue` | `string` | `''` | Initial search text for uncontrolled usage |
| `filter` | `AutocompleteFilter` | contains | Predicate deciding whether an item matches the search text |
| `clearInputOnClose` | `boolean` | `true` | Whether the search text resets when the overlay closes |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onValueChange` | `(value) => void` | - | Handler called when the selection changes |
| `onOpenChange` | `(open: boolean) => void` | - | Handler called when the open state changes |
| `onInputChange` | `(value: string) => void` | - | Handler called when the search text changes |
| `onClear` | `() => void` | - | Handler called after `Autocomplete.ClearButton` clears the selection |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the autocomplete subtree |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AutocompleteOption
| property | type | description |
| -------- | -------- | ------------------------------------------------ |
| `value` | `string` | Unique option value |
| `label` | `string` | Display string shown in the trigger and the item |
#### AutocompleteFilter
```ts
type AutocompleteFilter = (textValue: string, inputValue: string) => boolean;
```
The default filter is a case- and diacritic-insensitive "contains" match (exported as `defaultAutocompleteFilter`).
### Autocomplete.Trigger
| prop | type | default | description |
| ------------------- | ----------------- | ------- | --------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger content (Value, ClearButton, TriggerIndicator) |
| `isDisabled` | `boolean` | - | Whether the trigger is disabled |
| `isInvalid` | `boolean` | - | When `true`, applies a danger border; inherits from root when omitted |
| `className` | `string` | - | Additional CSS classes for the trigger |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### Autocomplete.Value
| prop | type | default | description |
| -------------- | ----------- | ------------------ | -------------------------------------------------- |
| `placeholder` | `string` | `'Select an item'` | Text shown when nothing is selected |
| `className` | `string` | - | Additional CSS classes for the value text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### Autocomplete.TriggerIndicator
Same as `Select.TriggerIndicator` — an animated chevron that rotates with the open state (`animation`, `iconProps`, `isAnimatedStyleActive`).
### Autocomplete.ClearButton
Extends `CloseButton` (which extends `Button`). Hidden while nothing is selected. Renders as a compact 24px button (icon size 14) with `hitSlop` preserving the touch target.
| prop | type | default | description |
| -------------------- | ---------------------- | ------------------- | ------------------------------------------ |
| `iconProps` | `CloseButtonIconProps` | `{ size: 14 }` | Overrides for the default close icon |
| `hitSlop` | `number` | `10` | Extra touch area around the compact button |
| `isDisabled` | `boolean` | root `isDisabled` | Whether the button is disabled |
| `accessibilityLabel` | `string` | `'Clear selection'` | Screen reader label |
| `className` | `string` | - | Additional CSS classes |
| `onPress` | `(event) => void` | - | Called after the selection is cleared |
| `...ButtonProps` | `ButtonRootProps` | - | All `Button` props are supported |
### Autocomplete.Portal / Autocomplete.Overlay / Autocomplete.Content / Autocomplete.ContentBackground
Same props as the corresponding `Select` parts. `Autocomplete.Content` is a union type discriminated by `presentation` (`'popover'` with `width`, `'dialog'` with `isSwipeable`, `'bottom-sheet'` with `BottomSheetProps`).
For the bottom-sheet presentation, the sheet defaults to a fixed `snapPoints={['90%']}` with `enableDynamicSizing={false}` — the search input autofocuses on open, so the sheet must be tall enough from the start to keep the content above the keyboard. Keyboard props default to `keyboardBehavior="extend"`, `keyboardBlurBehavior="restore"`, and `android_keyboardInputMode="adjustResize"`. Pass any of these props explicitly to override.
### Autocomplete.SearchField
Extends `SearchField` root props (minus `value` / `onChange`, which come from the root). The default input uses `variant="secondary"` and wires bottom-sheet-aware focus/blur handlers (no-ops outside a bottom sheet).
| prop | type | default | description |
| ---------------- | ----------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom `SearchField` composition; replaces the default when provided |
| `placeholder` | `string` | `'Search...'` | Placeholder for the default input (ignored with `children`) |
| `autoFocus` | `boolean` | `true` | Whether the default input focuses when the overlay opens (ignored with `children`) |
| `autoFocusDelay` | `number` | `150` / `300` | Delay in ms before the automatic focus: 150 for popover and dialog, 300 for bottom-sheet (ignored with `children`) |
| `inputProps` | `SearchFieldInputProps` | - | Extra props for the default input, e.g. `variant` to override `'secondary'` (ignored with `children`) |
| `isDisabled` | `boolean` | root value | Whether the search field is disabled |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Autocomplete.List
| prop | type | default | description |
| --------------------------- | ----------------- | ----------- | ----------------------------------------------------- |
| `children` | `React.ReactNode` | - | Items and list labels |
| `keyboardShouldPersistTaps` | `string` | `'handled'` | Keeps item presses working while the keyboard is open |
| `className` | `string` | - | Additional CSS classes (max height set in CSS) |
| `...ScrollViewProps` | `ScrollViewProps` | - | All standard ScrollView props are supported |
### Autocomplete.Item
Extends `Select.Item`.
| prop | type | default | description |
| ------------------- | ---------------- | ------- | ---------------------------------------------------------------- |
| `value` | `string` | - | Unique option value |
| `label` | `string` | - | Display label (also the default filter text) |
| `textValue` | `string` | - | Filter text override (e.g. keywords for a custom-rendered label) |
| `disabled` | `boolean` | `false` | Whether the item is disabled |
| `closeOnPress` | `boolean` | mode | Close on press (`true` in single mode, `false` in multiple mode) |
| `children` | node / render fn | - | Custom item content; defaults to `ItemLabel` + `ItemIndicator` |
| `className` | `string` | - | Additional CSS classes |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### Autocomplete.ItemLabel / ItemDescription / ItemIndicator / ListLabel / Close
Same props as the corresponding `Select` parts.
### Autocomplete.Empty
| prop | type | default | description |
| -------------- | --------------------------------------------- | --------------------- | ------------------------------------------------------ |
| `children` | `React.ReactNode` | `'No results found.'` | String children render styled text; nodes render as-is |
| `className` | `string` | - | Additional CSS classes for the container |
| `classNames` | `{ container?: string; text?: string }` | - | CSS classes per slot |
| `styles` | `{ container?: ViewStyle; text?: TextStyle }` | - | Styles per slot |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
## Hooks
### useAutocomplete
Hook to access the Autocomplete context. Must be used within an `Autocomplete` component.
```tsx
import { useAutocomplete } from 'heroui-native-pro';
const { inputValue, onInputChange, visibleItemCount } = useAutocomplete();
```
#### Returns: AutocompleteContextValue
| property | type | description |
| ------------------ | -------------------------------------------- | ----------------------------------------------------------- |
| `inputValue` | `string` | Current search text |
| `onInputChange` | `(value: string) => void` | Update the search text |
| `filter` | `AutocompleteFilter` | Active filter predicate |
| `registerItem` | `(value: string, textValue: string) => void` | Registers an item's filter text (used internally by `Item`) |
| `unregisterItem` | `(value: string) => void` | Removes an item from the registry |
| `visibleItemCount` | `number` | Registered items matching the current search text |
| `onClear` | `(() => void) \| undefined` | Root `onClear` callback |
| `isDisabledRoot` | `boolean` | Whether the root is disabled |
> Selection and open state live in the wrapped `Select` — read them with `useSelect` from `heroui-native` inside the autocomplete subtree.
# ComboBox
**Category**: native
**URL**: https://heroui.pro/docs/native/components/combo-box
> A text input combined with a listbox popover, letting users filter a collection of options to items matching a query.
> `ComboBox` composes the heroui-native `Select` (popover presentation only) and `InputGroup` components, mirroring the HeroUI web ComboBox anatomy. For a searchable select where filtering happens inside the popover instead of an inline input, see `Autocomplete`.
## Import
```tsx
import { ComboBox } from 'heroui-native-pro';
```
## Anatomy
```tsx
......
```
* **ComboBox**: Root that wraps `Select` and manages the input text, item filtering, popover opening, and form field context (for Label, Description, FieldError). Supports controlled and uncontrolled selection, open state, and input text.
* **ComboBox.Trigger**: Unstyled pressable that wraps the input group so the popover anchors to (and can match) the full input width. Taps on the text input focus it; taps elsewhere on the group toggle the popover.
* **ComboBox.InputGroup**: Input group wrapper containing the text input and optional prefix/suffix slots. Inherits `isDisabled` from the root.
* **ComboBox.Input**: Text input wired to the combo box. Typing filters the items; focus/typing opens the popover per `menuTrigger`. In single mode it displays the selected label.
* **ComboBox.Prefix** / **ComboBox.Suffix**: Optional leading/trailing slots inside the input group. The suffix typically holds the trigger indicator.
* **ComboBox.TriggerIndicator**: Chevron indicator that rotates with the open state.
* **ComboBox.ClearButton**: Close button inside the suffix that clears the selection and input text, then calls `onClear`. Hidden while there is no selection and the input is empty. Being its own pressable, it does not toggle the popover.
* **ComboBox.Value**: Selected labels or placeholder. Primarily used in multiple mode (the input shows the selection in single mode).
* **ComboBox.Portal**: Portal wrapper that re-provides the combo box context across the portal boundary.
* **ComboBox.Overlay**: Backdrop behind the popover. Transparent by default so the input stays visible; pressing it dismisses the popover.
* **ComboBox.Content**: Popover content container. `presentation` is fixed to `"popover"`; `width` defaults to `"trigger"`.
* **ComboBox.ContentBackground**: Theme-aware background layer behind the content.
* **ComboBox.List**: Scrollable list container that keeps item taps working while the keyboard is open.
* **ComboBox.ListLabel**: Section label for grouped items.
* **ComboBox.Item**: Selectable option filtered by the input text (`textValue` overrides `label` for matching).
* **ComboBox.ItemLabel** / **ComboBox.ItemDescription** / **ComboBox.ItemIndicator**: Item content parts (label text, muted description, selected check).
* **ComboBox.Empty**: Fallback shown when no item matches the input text.
## Usage
### Basic Usage
Single selection with the default case- and diacritic-insensitive "contains" filter. Selecting an item writes its label into the input and closes the popover; clearing the input clears the selection; closing the popover reverts the input text to the selected label — custom values are not kept. When the input shows the committed selection label, reopening shows the full collection (the query is treated as empty).
The popover has no keyboard avoidance, so place the field in the upper half of the screen (or pass `placement="top"` on `ComboBox.Content` when the field sits low) to keep the popover above the keyboard.
```tsx
```
### Multiple Selection
Set `selectionMode="multiple"`. The input acts as a search field and is cleared when the popover closes; the popover stays open while items toggle; display the selection with `ComboBox.Value`.
```tsx
import type { ComboBoxOption } from 'heroui-native-pro';
import { useState } from 'react';
const [selected, setSelected] = useState([]);
;
```
### Controlled
Control the selection, open state, and input text externally.
```tsx
import type { ComboBoxOption } from 'heroui-native-pro';
import { useState } from 'react';
const [selected, setSelected] = useState({
value: 'cat',
label: 'Cat',
});
const [isOpen, setIsOpen] = useState(false);
const [query, setQuery] = useState('');
......;
```
### Menu Trigger
`menuTrigger` decides when the popover opens: `"focus"` (default) opens it when the input gains focus and while typing, `"input"` opens it only while typing, and `"manual"` opens it only from `ComboBox.Trigger` presses (e.g. the chevron area).
```tsx
.........
```
### Custom Filtering
Provide a `filter` predicate, or use `textValue` on items so keywords match that are not part of the visible label. For async or externally filtered collections, control `inputValue` via `onInputChange`, render the fetched options as `ComboBox.Item` children, and keep the built-in filter permissive (or return `true`) when the server already filtered.
```tsx
textValue.toLowerCase().startsWith(inputValue.trim().toLowerCase())
}
>
...
```
```tsx
```
### Sections
```tsx
North AmericaEurope
```
### Field States
Use root props for required, invalid, and disabled states; the input picks up the form field styling automatically.
```tsx
.........Please select an option....
```
## Example
```tsx
import { Description, Label } from 'heroui-native';
import { ComboBox } from 'heroui-native-pro';
import { View } from 'react-native';
export default function ComboBoxExample() {
return (
No matching animals.Type to filter the list.
);
}
```
## API Reference
### ComboBox
Extends the `Select` root — all selection and open-state props are supported. `presentation` is not configurable (popover only).
| prop | type | default | description |
| ------------------- | ------------------------------------ | ---------- | ------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements (Label, ComboBox.Trigger, ComboBox.Portal, Description) |
| `selectionMode` | `'single' \| 'multiple'` | `'single'` | Whether one or many options can be selected |
| `value` | `ComboBoxOption \| ComboBoxOption[]` | - | Controlled selected option(s) |
| `defaultValue` | `ComboBoxOption \| ComboBoxOption[]` | - | Default selected option(s) for uncontrolled usage |
| `isOpen` | `boolean` | - | Controlled open state of the popover |
| `isDefaultOpen` | `boolean` | - | Initial open state for uncontrolled usage |
| `inputValue` | `string` | - | Controlled text shown in `ComboBox.Input` |
| `defaultInputValue` | `string` | `""` | Uncontrolled initial input text |
| `menuTrigger` | `ComboBoxMenuTrigger` | `'focus'` | Interaction that opens the popover |
| `filter` | `ComboBoxFilter` | contains | Predicate deciding whether an item matches the input text |
| `onClear` | `() => void` | - | Called after `ComboBox.ClearButton` clears the selection and input text |
| `isDisabled` | `boolean` | `false` | Whether the entire field is disabled |
| `isInvalid` | `boolean` | `false` | Whether the field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the field is required |
| `className` | `string` | - | Additional CSS classes for the root container |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the combo box subtree |
| `onValueChange` | `(value) => void` | - | Handler called when the selection changes |
| `onOpenChange` | `(open: boolean) => void` | - | Handler called when the open state changes |
| `onInputChange` | `(value: string) => void` | - | Handler called when the input text changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ComboBoxOption
| property | type | description |
| -------- | -------- | --------------------------------- |
| `value` | `string` | Unique value identifying the item |
| `label` | `string` | Display label for the item |
#### ComboBoxMenuTrigger
* `'focus'` — opens when the input gains focus (and while typing) (default)
* `'input'` — opens while typing into the input
* `'manual'` — opens only from `ComboBox.Trigger` presses
### ComboBox.Trigger
Pass-through to `Select.Trigger` (always `unstyled`). Wrap it around `ComboBox.InputGroup`.
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Trigger content (ComboBox.InputGroup) |
| `isDisabled` | `boolean` | - | Whether the trigger is disabled |
| `className` | `string` | - | Additional CSS classes |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### ComboBox.InputGroup
Same as `InputGroup`; `isDisabled` defaults to the root `isDisabled`.
### ComboBox.Input
`InputGroup.Input` wired to the combo box (`value` and `defaultValue` are managed by the root).
| prop | type | default | description |
| --------------- | ------------------------- | ------- | -------------------------------------------------------------- |
| `placeholder` | `string` | - | Placeholder text shown when the input is empty |
| `isDisabled` | `boolean` | - | Whether the input is disabled; inherits from root when omitted |
| `onChangeText` | `(text: string) => void` | - | Side-effect handler called after the internal change handler |
| `onFocus` | `(e: FocusEvent) => void` | - | Side-effect handler called after the internal open logic |
| `onBlur` | `(e: BlurEvent) => void` | - | Side-effect handler called after the internal commit logic |
| `...InputProps` | `InputGroupInputProps` | - | All InputGroup.Input props except `value` and `defaultValue` |
### ComboBox.Prefix / ComboBox.Suffix
Same as `InputGroup.Prefix` / `InputGroup.Suffix`.
### ComboBox.TriggerIndicator
Same as `Select.TriggerIndicator` (animated chevron that rotates with the open state).
### ComboBox.ClearButton
Extends `CloseButton`. Rendered inside the input group suffix; hidden while there is no selection and the input is empty. Pressing it clears the selection (`undefined` in single mode, `[]` in multiple mode) and the input text, then calls `onClear` from the root.
| prop | type | default | description |
| --------------------- | ------------------ | ------------------- | -------------------------------------------------- |
| `isDisabled` | `boolean` | - | Whether the button is disabled; inherits from root |
| `accessibilityLabel` | `string` | `'Clear selection'` | Screen reader label |
| `className` | `string` | - | Additional CSS classes |
| `...CloseButtonProps` | `CloseButtonProps` | - | All CloseButton props are supported |
### ComboBox.Value
Same as `Select.Value` with an optional placeholder.
| prop | type | default | description |
| ------------- | -------- | --------------------- | -------------------------------- |
| `placeholder` | `string` | `'No items selected'` | Shown when no option is selected |
### ComboBox.Portal
Same as `Select.Portal`. Re-provides the combo box context across the portal boundary.
### ComboBox.Overlay
Same as `Select.Overlay`. Transparent by default; pressing it dismisses the popover.
### ComboBox.Content
Popover subset of `Select.Content` — `presentation` is fixed to `"popover"`. The popover has no keyboard avoidance; use `placement="top"` when the field sits in the lower half of the screen so the open keyboard cannot cover the list.
| prop | type | default | description |
| -------------- | ------------------------------------------------ | ----------- | ----------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content (ComboBox.List, ComboBox.Empty) |
| `width` | `'content-fit' \| 'trigger' \| 'full' \| number` | `'trigger'` | Content width sizing strategy |
| `placement` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | Popover placement relative to the input group |
| `offset` | `number` | `4` | Gap between the input group and the popover |
| `className` | `string` | - | Additional CSS classes for the content container |
| `animation` | `SelectContentPopoverAnimation` | - | Keyframe animation configuration for entering/exiting |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### ComboBox.List
| prop | type | default | description |
| --------------------------- | ----------------- | ----------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | List content (ComboBox.Item, ComboBox.ListLabel) |
| `keyboardShouldPersistTaps` | `string` | `'handled'` | Keeps item taps working while the keyboard is open |
| `className` | `string` | - | Additional CSS classes |
| `...ScrollViewProps` | `ScrollViewProps` | - | All standard React Native ScrollView props |
### ComboBox.Item
Extends `Select.Item` with a filter text override.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------------------- |
| `value` | `string` | - | The value of this item |
| `label` | `string` | - | The label to display for this item |
| `textValue` | `string` | - | Text used for filtering instead of `label` |
| `closeOnPress` | `boolean` | mode | Defaults to `true` in single mode and `false` in multiple mode |
| `children` | `ReactNode \| fn` | - | Custom item content, or a render function receiving item state |
### ComboBox.ItemLabel / ComboBox.ItemDescription / ComboBox.ItemIndicator / ComboBox.ListLabel
Same as the corresponding `Select` parts.
### ComboBox.Empty
| prop | type | default | description |
| ------------ | --------------------------------------------- | --------------------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | `'No results found.'` | Fallback content (strings render styled) |
| `className` | `string` | - | Additional CSS classes for the container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for the container and text slots |
| `styles` | `{ container?: ViewStyle; text?: TextStyle }` | - | Styles for the container and text slots |
## Hooks
### useComboBox
Hook to access the combo box context. Must be used within a `ComboBox` component.
```tsx
import { useComboBox } from 'heroui-native-pro';
const { inputValue, filterText, visibleItemCount, openMenu } = useComboBox();
```
#### Returns: ComboBoxContextValue
| property | type | description |
| ------------------ | ------------------------- | ----------------------------------------------------------------------------------- |
| `inputValue` | `string` | Current text shown in `ComboBox.Input` |
| `filterText` | `string` | Text driving item filtering (empty while showing a committed single-mode selection) |
| `onInputChange` | `(value: string) => void` | Updates the input text |
| `onInputBlur` | `() => void` | Commits the input text on blur while the popover is closed |
| `openMenu` | `() => void` | Opens the popover (measures the trigger first) |
| `menuTrigger` | `ComboBoxMenuTrigger` | Interaction that opens the popover |
| `filter` | `ComboBoxFilter` | Predicate deciding whether an item matches the filter text |
| `visibleItemCount` | `number` | Number of registered items matching the current filter text |
| `isDisabledRoot` | `boolean` | `isDisabled` from the root |
# NumberField
**Category**: native
**URL**: https://heroui.pro/docs/native/components/number-field
> A numeric input with increment and decrement buttons for precise value entry.
## Import
```tsx
import { NumberField } from 'heroui-native-pro';
```
## Anatomy
```tsx
...
```
* **NumberField**: Root container that manages numeric state, provides form field context (for Label, Description, FieldError), and cascades animation settings to children. Supports controlled and uncontrolled modes with min/max/step constraints and Intl number formatting.
* **NumberField.Group**: Plain View wrapper that contains the decrement button, input, and increment button. Accepts a render function for accessing group state.
* **NumberField.Input**: Pass-through to the Input component. Displays the formatted numeric value and automatically adds horizontal padding to avoid overlapping with the buttons. Commits the value on blur.
* **NumberField.DecrementButton**: Absolutely positioned button anchored to the left side of the input. Decrements the value by one step. Auto-disabled when the value reaches minValue. Supports long-press repeat.
* **NumberField.IncrementButton**: Absolutely positioned button anchored to the right side of the input. Increments the value by one step. Auto-disabled when the value reaches maxValue. Supports long-press repeat.
## Usage
### Basic Usage
The NumberField component uses compound parts to create a numeric input with number stepper buttons.
```tsx
```
### With Description
Add context below the input with a Description component.
```tsx
Enter the width in pixels
```
### Controlled Value
Use `value` and `onChange` to control the numeric state externally.
```tsx
const [value, setValue] = useState(1024);
```
### Step Values
Configure the step size for increment and decrement operations.
```tsx
```
### Format Options
Use `Intl.NumberFormatOptions` to format the displayed value as currency, percent, unit, or decimal.
```tsx
```
### Percentage Format
Format the value as a percentage where `0.5` displays as `50%`.
```tsx
```
### Disabled State
Disable the entire number field and its children.
```tsx
```
### Invalid State with FieldError
Combine `isInvalid` with FieldError to display validation messages.
```tsx
100} minValue={0} value={value} onChange={setValue}>
Enter a value between 0 and 100Quantity must be 100 or less
```
### Render Function Group
Use a render function on `NumberField.Group` to access group state.
```tsx
{({ canDecrement, canIncrement }) => (
<>
{canDecrement && }
{canIncrement && }
>
)}
```
## Example
```tsx
import { Description, FieldError, Label, NumberField, Surface } from 'heroui-native-pro';
import { useState } from 'react';
import { View } from 'react-native';
export default function NumberFieldExample() {
const [value, setValue] = useState(150);
const isInvalid = value > 100;
return (
Enter the width in pixelsEnter a value between 0 and 100Quantity must be 100 or less
);
}
```
## API Reference
### NumberField
| prop | type | default | description |
| --------------- | -------------------------- | ------- | ----------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements (Label, NumberField.Group, Description, FieldError) |
| `value` | `number` | - | Controlled numeric value |
| `defaultValue` | `number` | - | Default value for uncontrolled usage |
| `minValue` | `number` | - | Minimum allowed value; disables decrement button at limit |
| `maxValue` | `number` | - | Maximum allowed value; disables increment button at limit |
| `step` | `number` | `1` | Step value for increment and decrement operations |
| `formatOptions` | `Intl.NumberFormatOptions` | - | Intl.NumberFormat options for formatting (currency, percent, unit, etc) |
| `isDisabled` | `boolean` | `false` | Whether the entire number field and its children are disabled |
| `isInvalid` | `boolean` | `false` | Whether the number field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the number field is required |
| `className` | `string` | - | Additional CSS classes |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for number field |
| `onChange` | `(value: number) => void` | - | Handler called when the numeric value changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### AnimationRootDisableAll
Animation configuration for the NumberField root component. Can be:
* `"disable-all"`: Disable all animations including children (cascades down)
* `undefined`: Use default animations
### NumberField.Group
| prop | type | default | description |
| -------------- | ------------------------------------------------------------------------------ | ------- | ------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: NumberFieldGroupRenderProps) => React.ReactNode)` | - | Children elements, or a render function receiving group state |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### NumberFieldGroupRenderProps
| property | type | description |
| ---------------------- | --------- | ---------------------------------------------------- |
| `numberValue` | `number` | Current numeric value (NaN when empty) |
| `displayValue` | `string` | Display string shown in the input |
| `canIncrement` | `boolean` | Whether the value can be incremented (not at max) |
| `canDecrement` | `boolean` | Whether the value can be decremented (not at min) |
| `isDisabled` | `boolean` | Whether the number field is disabled |
| `isInvalid` | `boolean` | Whether the number field is in an invalid state |
| `isRequired` | `boolean` | Whether the number field is required |
| `decrementButtonWidth` | `number` | Measured width of the decrement button (0 if absent) |
| `incrementButtonWidth` | `number` | Measured width of the increment button (0 if absent) |
### NumberField.Input
| prop | type | default | description |
| --------------------- | ------------ | ------- | --------------------------------------------------------------------- |
| `isAutoPaddingActive` | `boolean` | `true` | Whether auto padding is added to avoid overlapping with buttons |
| `autoPaddingAddon` | `number` | `12` | Extra horizontal spacing (in px) between button edge and text content |
| `...InputProps` | `InputProps` | - | All Input component props are supported |
### NumberField.DecrementButton
| prop | type | default | description |
| ----------------------- | -------------------------------------------------- | ------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Button content; defaults to MinusIcon when omitted |
| `style` | `StyleProp` | - | Style applied to the outer Pressable container |
| `className` | `string` | - | Additional CSS classes for the outer container slot |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Styles for individual slots |
| `animation` | `NumberFieldButtonAnimation` | - | Animation configuration for button press scale feedback |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles are applied to the contentContainer |
| `iconProps` | `NumberFieldButtonIconProps` | - | Props forwarded to the default icon; ignored with custom children |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### `ElementSlots`
| prop | type | description |
| ------------------ | -------- | ----------------------------------------------------- |
| `container` | `string` | Custom class name for the outer Pressable container |
| `contentContainer` | `string` | Custom class name for the inner animated content view |
#### `styles`
| prop | type | description |
| ------------------ | ----------- | ------------------------------------------ |
| `container` | `ViewStyle` | Styles for the outer Pressable container |
| `contentContainer` | `ViewStyle` | Styles for the inner animated content view |
#### NumberFieldButtonIconProps
| prop | type | default | description |
| ------- | -------- | ------------ | --------------------------------------------------- |
| `size` | `number` | `16` | Icon size in logical pixels |
| `color` | `string` | `foreground` | Icon fill color; defaults to theme foreground color |
#### NumberFieldButtonAnimation
Animation configuration for increment/decrement button press feedback. Can be:
* `false` or `"disabled"`: Disable button press animation
* `undefined`: Use default animation
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------------- | ------------------ | ------------------- | ---------------------------------- |
| `scale.value` | `[number, number]` | `[1, 0.9]` | Scale values \[unpressed, pressed] |
| `scale.timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Animation timing configuration |
### NumberField.IncrementButton
| prop | type | default | description |
| ----------------------- | -------------------------------------------------- | ------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Button content; defaults to PlusIcon when omitted |
| `style` | `StyleProp` | - | Style applied to the outer Pressable container |
| `className` | `string` | - | Additional CSS classes for the outer container slot |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual slots |
| `styles` | `Partial>` | - | Styles for individual slots |
| `animation` | `NumberFieldButtonAnimation` | - | Animation configuration for button press scale feedback |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles are applied to the contentContainer |
| `iconProps` | `NumberFieldButtonIconProps` | - | Props forwarded to the default icon; ignored with custom children |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
The IncrementButton shares the same slot structure as DecrementButton. See `ElementSlots`, `styles`, `NumberFieldButtonIconProps`, and `NumberFieldButtonAnimation` above.
# NumberPad
**Category**: native
**URL**: https://heroui.pro/docs/native/components/number-pad
> A numeric keypad for entering PINs, codes, and amounts with subtle press animations.
## Import
```tsx
import { NumberPad } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **NumberPad**: Root column container that manages the value state and provides context to sub-components. Auto-renders the default 3×4 digit layout when no children are provided. Supports both controlled and uncontrolled modes.
* **NumberPad.Row**: Horizontal container that lays out a row of cells with equal widths. Required when composing keys manually.
* **NumberPad.Key**: Pressable digit key with a subtle press animation. Appends its `value` to the pad value by default. Renders a default `NumberPad.KeyLabel` showing its value when no children are provided.
* **NumberPad.KeyBackground**: Optional theme-aware background container rendered behind the key surface. Mounted automatically for keys when the active theme registers default background content (e.g. `glass`); `NumberPad.Backspace` and `NumberPad.Spacer` default it to `null` (transparent surfaces). Replace or remove it via the `background` prop.
* **NumberPad.KeyLabel**: Text label rendered inside a key. Defaults to the parent key's value.
* **NumberPad.Backspace**: Delete key. Press removes one character; long-press clears the entire value. Renders a backspace icon by default.
* **NumberPad.Spacer**: Grid cell that preserves alignment. Renders an inert empty cell by default; behaves like a `NumberPad.Key` when given children.
## Usage
### Basic Usage
Render the default 3×4 keypad by omitting children.
```tsx
const [value, setValue] = useState('');
;
```
### Manual Composition
Compose the keypad explicitly with rows and keys.
```tsx
...
```
### Max Length
Cap the input length and react when it fills up.
```tsx
verify(code)}
/>
```
### Custom Key Content
Use a render function to access key state and style the label.
```tsx
{({ isPressed }) => (
1
)}
...
...
```
### Spacer Action
Give the spacer cell children to turn it into an action key.
```tsx
...
```
### Disabled
Disable the entire keypad.
```tsx
```
## Example
```tsx
import { NumberPad } from 'heroui-native-pro';
import { useState } from 'react';
import { Text, View } from 'react-native';
export default function NumberPadExample() {
const [value, setValue] = useState('');
const [isComplete, setIsComplete] = useState(false);
return (
{value.padEnd(4, '•')}
{
setValue(next);
setIsComplete(false);
}}
onComplete={() => setIsComplete(true)}
/>
);
}
```
## API Reference
### NumberPad
| prop | type | default | description |
| ------------------ | ------------------------------------------ | ------- | ------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Pad content. When omitted, the default digit grid is rendered automatically |
| `value` | `string` | - | Controlled value string |
| `defaultValue` | `string` | `""` | Default value for uncontrolled mode |
| `maxLength` | `number` | - | Maximum number of characters. Extra key presses are ignored once reached |
| `isDisabled` | `boolean` | `false` | Whether the entire pad is disabled |
| `className` | `string` | - | Additional CSS classes for the root grid container |
| `onValueChange` | `(value: string) => void` | - | Callback fired when the value changes |
| `onKeyPress` | `(key: string, nextValue: string) => void` | - | Callback fired when a digit key is pressed |
| `onBackspacePress` | `(value: string) => void` | - | Callback fired when backspace is pressed, with the value after deletion |
| `onSpacerPress` | `() => void` | - | Default press handler for a spacer rendered as a key without its own `onPress` |
| `onClear` | `() => void` | - | Callback fired when the value is cleared via backspace long-press |
| `onComplete` | `(value: string) => void` | - | Callback fired when the value reaches `maxLength` |
| `animation` | `NumberPadRootAnimation` | - | Animation configuration for the root component |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### NumberPadRootAnimation
Animation configuration for the number pad root component. Can be:
* `"disable-all"`: Disable all animations including children
* `undefined`: Use default animations
### NumberPad.Row
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------- |
| `children` | `React.ReactNode` | - | Row content, typically `Key`, `Backspace`, `Spacer` |
| `className` | `string` | - | Additional CSS classes for the row container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### NumberPad.Key
| prop | type | default | description |
| ----------------------- | -------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `value` | `string` | - | Digit or character value appended to the pad value |
| `children` | `React.ReactNode \| ((props: NumberPadKeyRenderProps) => React.ReactNode)` | - | Custom content or render function. Defaults to a `KeyLabel` |
| `isDisabled` | `boolean` | `false` | Whether this key is disabled independently of the root pad |
| `className` | `string` | - | Additional CSS classes for the key container |
| `animation` | `NumberPadKeyAnimation` | - | Animation configuration for the key press feedback |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `background` | `React.ReactNode` | - | Background layer behind the key surface. `undefined` renders the theme-aware default; custom node replaces it; `null` removes it |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### NumberPadKeyRenderProps
| prop | type | description |
| ------------ | --------- | ------------------------------------ |
| `value` | `string` | The key's value |
| `isPressed` | `boolean` | Whether the key is currently pressed |
| `isDisabled` | `boolean` | Whether the key is disabled |
#### NumberPadKeyAnimation
Animation configuration for the key press scale effect. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------- | ----------------------------------------------------- | ------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `scale` | `{ value?: number; timingConfig?: WithTimingConfig }` | - | Scale press feedback configuration |
| `scale` prop | type | default | description |
| -------------- | ------------------ | ------------------- | --------------------------------------------- |
| `value` | `number` | `0.97` | Scale value applied when the key is pressed |
| `timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Timing configuration for the scale transition |
### NumberPad.KeyBackground
Absolute-fill container rendered behind the key surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### NumberPad.KeyLabel
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom label content. Defaults to the parent key's value |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
### NumberPad.Backspace
| prop | type | default | description |
| ----------------------- | ----------------------------- | ------- | ---------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content for the backspace key. Defaults to an icon |
| `isDisabled` | `boolean` | `false` | Whether this key is disabled independently of the root pad |
| `className` | `string` | - | Additional CSS classes for the key container |
| `iconProps` | `NumberPadBackspaceIconProps` | - | Props forwarded to the default backspace icon. Ignored when `children` is provided |
| `animation` | `NumberPadKeyAnimation` | - | Animation configuration for the key press feedback |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### NumberPadBackspaceIconProps
| prop | type | default | description |
| ------- | -------- | ------------ | -------------------------- |
| `size` | `number` | `24` | Size of the icon in pixels |
| `color` | `string` | `foreground` | Color of the icon |
### NumberPad.Spacer
| prop | type | default | description |
| ----------------------- | ----------------------- | ------- | -------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Optional content. When provided, the spacer behaves like a key |
| `isDisabled` | `boolean` | `false` | Whether this cell is disabled independently of the root pad |
| `className` | `string` | - | Additional CSS classes for the cell container |
| `animation` | `NumberPadKeyAnimation` | - | Animation configuration for the press feedback (with children) |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
# Number Stepper
**Category**: native
**URL**: https://heroui.pro/docs/native/components/number-stepper
> A React Native numeric input with increment and decrement controls, bounds, step intervals, and formatting.
## Import
```tsx
import { NumberStepper } from 'heroui-native';
```
## Anatomy
```tsx
```
* **NumberStepper**: Root container that manages numeric value state, and provides context to sub-components. Supports both controlled and uncontrolled modes.
* **NumberStepper.RootBackground**: Optional theme-aware background container rendered behind the root surface. Mounted automatically when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **NumberStepper.DecrementButton**: Pressable button that decreases the value by one step. Auto-disables at the minimum boundary. Renders a minus icon by default.
* **NumberStepper.Value**: Displays the current numeric value with direction-aware flip animations on change.
* **NumberStepper.IncrementButton**: Pressable button that increases the value by one step. Auto-disables at the maximum boundary. Renders a plus icon by default.
## Usage
### Basic Usage
The NumberStepper component uses compound parts to create a numeric stepper control.
```tsx
```
### Custom Step
Configure the increment/decrement amount per press.
```tsx
```
### Disabled
Disable the entire number stepper or let boundaries auto-disable individual buttons.
```tsx
```
### Controlled
Control the value externally with `value` and `onValueChange`.
```tsx
const [value, setValue] = useState(5);
;
```
### Render Function Children
Use a render function to access number stepper state for conditional rendering.
```tsx
{({ isAtMin }) => (
<>
{
if (isAtMin) {
Alert.alert('Removed', 'Item removed from cart');
}
}}
>
{isAtMin ? : undefined}
>
)}
```
## Example
```tsx
import { NumberStepper } from 'heroui-native';
import { useState } from 'react';
import { Alert, View } from 'react-native';
export default function NumberStepperExample() {
const [quantity, setQuantity] = useState(1);
return (
{({ isAtMin }) => (
<>
{
if (isAtMin) {
Alert.alert('Removed', 'Item removed from cart');
}
}}
>
{isAtMin ? : undefined}
>
)}
);
}
```
## API Reference
### NumberStepper
| prop | type | default | description |
| --------------- | ------------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: NumberStepperRootRenderProps) => React.ReactNode)` | - | Children elements or render function receiving number stepper state |
| `value` | `number` | - | Controlled numeric value |
| `defaultValue` | `number` | `0` | Default value for uncontrolled mode |
| `minValue` | `number` | `-Infinity` | Minimum allowed value |
| `maxValue` | `number` | `Infinity` | Maximum allowed value |
| `step` | `number` | `1` | Step amount for increment/decrement operations |
| `isDisabled` | `boolean` | `false` | Whether the number stepper is disabled |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onValueChange` | `(value: number) => void` | - | Callback fired when the value changes |
| `animation` | `AnimationRootDisableAll` | - | Animation configuration for the root component |
| `background` | `React.ReactNode` | - | Background layer behind the root surface. `undefined` renders the theme-aware default; custom node replaces it; `null` removes it |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### NumberStepperRootRenderProps
| prop | type | description |
| ------------ | --------- | ---------------------------------------------------- |
| `value` | `number` | Current numeric value of the number stepper |
| `isAtMin` | `boolean` | Whether the current value is at or below the minimum |
| `isAtMax` | `boolean` | Whether the current value is at or above the maximum |
| `isDisabled` | `boolean` | Whether the entire number stepper is disabled |
| `step` | `number` | Step increment/decrement amount |
| `minValue` | `number` | Minimum allowed value |
| `maxValue` | `number` | Maximum allowed value |
#### AnimationRootDisableAll
Animation configuration for the number stepper root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
### NumberStepper.RootBackground
Absolute-fill container rendered behind the root surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### NumberStepper.DecrementButton
| prop | type | default | description |
| ----------------------- | ------------------------------ | ------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content for the button. Defaults to a minus icon |
| `isDisabled` | `boolean` | - | Whether this button is individually disabled. Overrides context and boundary auto-disable |
| `keepActiveAtBoundary` | `boolean` | `false` | When true, the button stays interactive at the min boundary instead of auto-disabling |
| `className` | `string` | - | Additional CSS classes |
| `iconProps` | `NumberStepperButtonIconProps` | - | Props forwarded to the default minus icon. Ignored when `children` is provided |
| `animation` | `NumberStepperButtonAnimation` | - | Animation configuration for the button press scale effect |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
### NumberStepper.Value
| prop | type | default | description |
| ----------------------- | ----------------------------- | ------- | ----------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content to display instead of the default value text |
| `className` | `string` | - | Additional CSS classes for the value text |
| `animation` | `NumberStepperValueAnimation` | - | Animation configuration for the value display |
| `...Animated.TextProps` | `Animated.TextProps` | - | All Reanimated Animated.Text props are supported |
#### NumberStepperValueAnimation
Animation configuration for the value component. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------------- | ----------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `entering` | `NumberStepperDirectionalAnimation` | Direction-aware Keyframe slide + scale, 400ms | Entering animation played when the new value mounts |
| `exiting` | `NumberStepperDirectionalAnimation` | Direction-aware Keyframe slide + scale, 400ms | Exiting animation played when the old value unmounts |
| `translateYDistance` | `number` | `16` | Vertical slide distance in pixels. Ignored when custom `entering`/`exiting` are provided |
| `scaleValue` | `number` | `0.7` | Scale at the start/end of transitions. Ignored when custom `entering`/`exiting` are provided |
#### NumberStepperDirectionalAnimation
A single animation or a per-direction pair. Pass a plain value to use the same animation for both directions, or an object with `increase`/`decrease` keys for direction-aware control.
| type | description |
| ------------------------------------------------------------------------ | ----------------------------------------- |
| `EntryOrExitLayoutType` | Single animation used for both directions |
| `{ increase?: EntryOrExitLayoutType; decrease?: EntryOrExitLayoutType }` | Per-direction animations |
### NumberStepper.IncrementButton
| prop | type | default | description |
| ----------------------- | ------------------------------ | ------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content for the button. Defaults to a plus icon |
| `isDisabled` | `boolean` | - | Whether this button is individually disabled. Overrides context and boundary auto-disable |
| `keepActiveAtBoundary` | `boolean` | `false` | When true, the button stays interactive at the max boundary instead of auto-disabling |
| `className` | `string` | - | Additional CSS classes |
| `iconProps` | `NumberStepperButtonIconProps` | - | Props forwarded to the default plus icon. Ignored when `children` is provided |
| `animation` | `NumberStepperButtonAnimation` | - | Animation configuration for the button press scale effect |
| `isAnimatedStyleActive` | `boolean` | `true` | Whether animated styles (react-native-reanimated) are active |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### NumberStepperButtonIconProps
| prop | type | default | description |
| ------- | -------- | ------------ | -------------------------- |
| `size` | `number` | `18` | Size of the icon in pixels |
| `color` | `string` | `foreground` | Color of the icon |
#### NumberStepperButtonAnimation
Animation configuration for button press scale effect. Can be:
* `false` or `"disabled"`: Disable all animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| -------------- | ----------------------- | ------------------- | ----------------------------------------------- |
| `state` | `'disabled' \| boolean` | - | Disable animations while customizing properties |
| `value` | `number` | `0.95` | Scale value applied when the button is pressed |
| `timingConfig` | `WithTimingConfig` | `{ duration: 150 }` | Timing configuration for the scale transition |
## Hooks
### useNumberStepper
Hook to access the number stepper root context. Must be used within a `NumberStepper` component.
```tsx
import { useNumberStepper } from 'heroui-native';
const {
value,
isAtMin,
isAtMax,
isDisabled,
step,
minValue,
maxValue,
direction,
decrement,
increment,
} = useNumberStepper();
```
#### Returns
| property | type | description |
| ------------ | ------------------------ | ------------------------------------------------------------------------ |
| `value` | `number` | Current numeric value of the number stepper |
| `step` | `number` | Step increment/decrement amount |
| `minValue` | `number` | Minimum allowed value |
| `maxValue` | `number` | Maximum allowed value |
| `isDisabled` | `boolean` | Whether the entire number stepper is disabled |
| `isAtMin` | `boolean` | Whether the current value is at or below the minimum |
| `isAtMax` | `boolean` | Whether the current value is at or above the maximum |
| `direction` | `NumberStepperDirection` | Direction of the most recent value change (`'increase'` or `'decrease'`) |
| `decrement` | `() => void` | Decrement the value by one step |
| `increment` | `() => void` | Increment the value by one step |
# PhoneNumberField
**Category**: native
**URL**: https://heroui.pro/docs/native/components/phone-number-field
> An international phone number field with per-country as-you-type formatting, validation, E.164 output, smart paste, and a searchable country picker.
## Import
```tsx
import {PhoneNumberField} from "heroui-native-pro";
```
## Anatomy
```tsx
.........
```
* **PhoneNumberField**: Root container. Owns the number, country, and picker open state; provides form field context (for Label, Description, FieldError). Controlled and uncontrolled modes for all three states.
* **PhoneNumberField.InputGroup**: Layout container for the input row (re-exported `InputGroup`).
* **PhoneNumberField.Prefix**: Leading slot of the input row hosting the country picker trigger.
* **PhoneNumberField.Suffix**: Optional trailing slot for custom decorators.
* **PhoneNumberField.Input**: Masked national number input. `value` / `onChangeText` are driven by context; the placeholder defaults to a mask-derived example for the selected country.
* **PhoneNumberField.Select**: Country picker `Select` root wired to the field state (dialog presentation, single selection).
* **PhoneNumberField.Trigger**: Picker trigger. Renders the selected country flag and dial code by default; pass `children` to override.
* **PhoneNumberField.Portal**: Portal wrapper that re-provides the field context across the portal boundary.
* **PhoneNumberField.Overlay**: Backdrop behind the picker surface.
* **PhoneNumberField.Content**: Dialog picker surface (swipeable to dismiss).
* **PhoneNumberField.ContentBackground**: Theme-aware background layer of the picker surface (re-exported from `Select.ContentBackground`); pass a customized instance to the `background` prop of `PhoneNumberField.Content`.
* **PhoneNumberField.ContentHandle**: Decorative drag-handle bar signaling the dialog can be swiped to dismiss.
* **PhoneNumberField.SearchInput**: `SearchField` filtering the country list by name, ISO code, or dial code.
* **PhoneNumberField.CountryList**: Virtualized (`FlatList`), search-filtered country list with default rows and an empty fallback.
* **PhoneNumberField.CountryItem**: Single selectable country row — flag, dial code, name, and a selection indicator by default.
## Usage
### Basic Usage
The field resolves its initial country from `defaultCountry`, then the device locale region, then `"US"`. Typing formats the number as-you-type for the selected country.
```tsx
```
### Value Details and Validation
`onValueChange` receives the full value details on every change — unformatted digits, the formatted display value, the E.164 representation, the selected country, and validity flags.
```tsx
const [details, setDetails] = useState();
const isInvalid = details !== undefined && details.nationalNumber !== "" && !details.isValid;
...We'll text a verification code.Enter a valid phone number;
```
### Picking a Country
The picker opens with the selected country centred in the visible area, and picking a different one clears the number: a national number only means something inside its own numbering plan, so keeping the digits would leave a number that formats as valid while belonging to neither country. Both `onCountryChange` and `onValueChange` fire, the latter with empty digits.
Country changes that come from the number itself — smart paste and dial code typing — keep the digits, since those already belong to the detected country.
### Smart Paste
Text that starts with a `+` is read as an international number: the country is detected from the dial code and the remainder is kept as the national number. Pasting `+49 30 901820` switches the field to Germany and fills `30 901820`.
Typing works the same way. A dial code that is still ambiguous (`+`, `+3`) stays visible as typed until enough digits identify a country, at which point the field switches and the remaining digits continue as the national number.
### Controlled
The national number digits, the country, and the picker open state can each be controlled independently.
```tsx
const [digits, setDigits] = useState("");
const [country, setCountry] = useState("DE");
const [isOpen, setIsOpen] = useState(false);
setDigits(details.nationalNumber)}
onCountryChange={(next) => setCountry(next.code)}
onOpenChange={setIsOpen}
>
...
;
```
### Restricting the Country List
Pass a filtered `countries` array to restrict, reorder, or relabel the available countries. The full built-in dataset is exported as `PHONE_NUMBER_FIELD_COUNTRIES`.
```tsx
import {PHONE_NUMBER_FIELD_COUNTRIES} from "heroui-native-pro";
const NORTH_AMERICA = PHONE_NUMBER_FIELD_COUNTRIES.filter((country) =>
["US", "CA", "MX"].includes(country.code),
);
...
;
```
### Custom Trigger and Rows
`PhoneNumberField.Trigger` and `PhoneNumberField.CountryItem` accept `children` to replace the default content; `PhoneNumberField.CountryList` accepts `renderCountry` to replace the default rows.
```tsx
(
{country.flag}{country.name}{country.dialCode}
)}
/>
```
### libphonenumber-js
Install the optional peer dependency for metadata-driven validation, E.164 output, region detection from a pasted number, and per-prefix length limits:
```sh
npm install libphonenumber-js
```
Without it, `isValid` degrades to a completeness check, `e164` is the dial code with the digits appended, and lengths are capped by the country mask and the 15-digit E.164 budget. Formatting is the same either way, since it comes from the built-in `#`-template masks.
The masks are generated from `libphonenumber-js` metadata, so a country groups its digits the same way with or without the package installed, and the placeholder matches the value the user types: Ukraine reads `00 000 0000` and formats to `50 123 4567`, keystroke by keystroke. A literal appears exactly when the official formatting reveals it — a bracket once the group it wraps is full (`20` stays bare, `201` becomes `(201)`), a separator once the next group receives a digit. Regenerate the table with `node scripts/generate-phone-number-masks.js` after upgrading `libphonenumber-js`.
One layout per country, always the placeholder's: the value never rearranges digits the user has already seen. As-you-type grouping from `libphonenumber-js` is not used for numbers that fit the mask, because its rules are picked per prefix and per length and therefore move separators mid-word — an Albanian number walks through `77 777`, `777 777` and `777 77777` before landing on the placeholder's `77 777 7777`, and a Belize number starting with a zero, which no rule covers, reads `0-000-000` against a `000-0000` placeholder.
The layout also holds for numbers longer than the mask, which take their extra digits onto the last group. A mask describes a country's common format, and plenty of plans reach further: Belize adds an eleven-digit toll-free range (`0800…`) to its seven-digit numbers, and German numbers run from four digits to fifteen against an eleven-digit mask. Those lengths are accepted — the cap comes from the numbering plan, not from the mask — they just keep the country's one layout, so a long German number reads `3012 3456789012` rather than regrouping to `30 1234567890` at the twelfth keystroke.
The trade is that a number whose format differs from the placeholder's is grouped like the placeholder rather than in its own national style: a Berlin landline reads `3090 1820` (the grouping `libphonenumber-js` own as-you-type formatter also gives it) instead of `30 901820`, and a Belize toll-free number reads `080-01234567` rather than `0-800-1234-567`. Grouping is presentation only — `nationalNumber`, `e164`, `isValid` and `isComplete` come from the digits and are unaffected.
So the placeholder is one example of the country's numbers, in the way the iOS Contacts field shows one, and not a statement of how long a number may be. A Belize field placeholds `000-0000` and still accepts `080-01234567`, because both belong to the plan. Where a field should promise a single length — a form that only takes mobile numbers, say — pass `maxLength` on `PhoneNumberField.Input`.
## Example
```tsx
import {Description, Label} from "heroui-native";
import {PhoneNumberField} from "heroui-native-pro";
import {View} from "react-native";
export default function PhoneNumberFieldExample() {
return (
We'll send a verification code to this number
);
}
```
## API Reference
### PhoneNumberField
| prop | type | default | description |
| ----------------- | ------------------------------------------------- | ------- | --------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Children elements (Label, InputGroup, Description, FieldError) |
| `value` | `string` | - | Controlled national number digits (unformatted, e.g. `"5551234567"`) |
| `defaultValue` | `string` | - | Uncontrolled initial national number digits |
| `country` | `string` | - | Controlled selected country as an ISO 3166-1 alpha-2 code (e.g. `"US"`) |
| `defaultCountry` | `string` | locale | Uncontrolled initial country; falls back to the device locale region, then `"US"` |
| `isOpen` | `boolean` | - | Controlled open state of the country picker |
| `isDefaultOpen` | `boolean` | - | Uncontrolled initial open state of the country picker |
| `countries` | `PhoneNumberFieldCountry[]` | all | Custom country list (restrict, reorder, or relabel) |
| `isDisabled` | `boolean` | `false` | Whether the entire field is disabled |
| `isInvalid` | `boolean` | `false` | Whether the field is in an invalid state |
| `isRequired` | `boolean` | `false` | Whether the field is required |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onValueChange` | `(details: PhoneNumberFieldValueDetails) => void` | - | Called when the number or country changes with the full value details |
| `onCountryChange` | `(country: PhoneNumberFieldCountry) => void` | - | Called when the selected country changes (picker, smart paste, dial code typing) |
| `onOpenChange` | `(open: boolean) => void` | - | Called when the country picker open state changes |
| `animation` | `AnimationRootDisableAll` | - | `"disable-all"` disables all animations in the subtree |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### PhoneNumberFieldCountry
| property | type | description |
| ---------- | -------- | ----------------------------------------------------------------- |
| `code` | `string` | ISO 3166-1 alpha-2 country code (e.g. `"US"`) |
| `name` | `string` | English display name |
| `dialCode` | `string` | International dial code including the leading plus sign (`"+44"`) |
| `flag` | `string` | Flag emoji derived from the ISO code |
#### PhoneNumberFieldValueDetails
| property | type | description |
| ----------------- | ------------------------- | -------------------------------------------------------------------------------- |
| `nationalNumber` | `string` | National number digits without formatting (`"5551234567"`) |
| `formattedNumber` | `string` | Formatted national number as displayed (`"(555) 123-4567"`) |
| `e164` | `string` | Full number in E.164 format (`"+15551234567"`); empty when no digits are entered |
| `country` | `PhoneNumberFieldCountry` | The currently selected country |
| `isValid` | `boolean` | Whether the number is valid for the selected country |
| `isComplete` | `boolean` | Whether the number has a plausible length for the selected country |
### PhoneNumberField.InputGroup / PhoneNumberField.Prefix / PhoneNumberField.Suffix
Same props as the corresponding heroui-native `InputGroup` parts.
### PhoneNumberField.Input
Extends `InputGroup.Input` (minus `value` / `onChangeText`, which come from the field context).
Once the selected country's numbering plan has no room for another digit, the input caps its own `maxLength` at the current text length, so the platform stops accepting keystrokes the field would trim away. The limit follows the number's prefix rather than the country alone, because a plan often allows different lengths for different prefixes: a Ukrainian `50…` number stops at nine digits while a `90…` number takes ten, and countries whose numbers genuinely vary in length keep growing. The cap lifts while text is selected — pasting a longer number over a full one stays possible — and passing `maxLength` replaces the behavior with a fixed limit.
| prop | type | default | description |
| -------------------- | ------------------------ | ---------------- | ------------------------------------------------------------------------ |
| `placeholder` | `string` | mask example | Placeholder; defaults to a mask-derived example for the selected country |
| `maxLength` | `number` | country maximum | Character limit; defaults to the selected country's maximum length |
| `keyboardType` | `KeyboardTypeOptions` | `'phone-pad'` | Keyboard type |
| `textAlign` | `'left' \| ...` | `'left'` | Deliberately physical: phone numbers read left-to-right in every locale |
| `isDisabled` | `boolean` | root value | Whether the input is disabled |
| `accessibilityLabel` | `string` | `'Phone number'` | Screen reader label |
| `onChangeText` | `(text: string) => void` | - | Runs after the internal handler with the raw text |
| `...InputProps` | `InputGroupInputProps` | - | All `InputGroup.Input` props are supported |
### PhoneNumberField.Select
`Select` root wired to the field state. All `Select` root props are supported except the state props (`value`, `isOpen`, `onValueChange`, `onOpenChange`, …), `selectionMode`, and `presentation`, which are owned by the field.
### PhoneNumberField.Trigger
Extends `Select.Trigger` (minus `variant`, fixed to `"unstyled"`). Dismisses the keyboard on press.
| prop | type | default | description |
| -------------------- | --------------------------------------------------- | ---------------- | -------------------------------------------- |
| `children` | `React.ReactNode` | flag + dial code | Custom trigger content replacing the default |
| `classNames` | `{ base?, flag?, dialCode? }` | - | CSS classes per slot |
| `styles` | `{ base?: ViewStyle; flag?, dialCode?: TextStyle }` | - | Styles per slot |
| `accessibilityLabel` | `string` | country + code | Screen reader label |
| `...TriggerProps` | `SelectTriggerProps` | - | All `Select.Trigger` props are supported |
### PhoneNumberField.Portal / PhoneNumberField.Overlay / PhoneNumberField.ContentBackground
Same props as the corresponding `Select` parts. `PhoneNumberField.Portal` re-provides the field context across the portal boundary.
### PhoneNumberField.Content
Same props as `Select.Content`, always with the `"dialog"` presentation.
Unlike a plain `Select` dialog, the surface is pinned below the top safe area instead of being centered, and its height defaults to half the space below that. The search input takes focus as soon as the picker opens, so the lower part of the screen belongs to the keyboard; anchoring the surface at the top and capping its height keeps all of it visible without any keyboard avoidance. No sizing is needed at the call site:
```tsx
```
Every part of that is overridable — `style` (or `styles.content`) for the height and the `marginTop` offset, `classNames.wrapper` to center the surface again:
```tsx
```
A taller surface may end up behind the keyboard; pair a centered or full-height surface with `autoFocus={false}` on `PhoneNumberField.SearchInput`.
### PhoneNumberField.ContentHandle
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes for the handle bar |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### PhoneNumberField.SearchInput
Extends `SearchField` root props (minus `value` / `onChange`, which come from the field context). Renders the default `SearchField.Group` anatomy (`SearchIcon` + `Input` + `ClearButton`); pass `children` to compose the `SearchField.*` parts yourself.
The default input focuses when the picker opens, so the keyboard is ready for typing right away — the dialog portal unmounts its content on close, which makes this a plain mount-time focus that repeats on every open. Pass `autoFocus={false}` for a picker that is mostly browsed by scrolling.
| prop | type | default | description |
| --------------------- | ------------------------ | ---------- | --------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom `SearchField` composition; replaces the default when provided |
| `autoFocus` | `boolean` | `true` | Whether the default input focuses when the picker opens (ignored with `children`) |
| `inputProps` | `SearchFieldInputProps` | - | Props for the default input (`variant` defaults to `"secondary"`) |
| `isDisabled` | `boolean` | root value | Whether the search field is disabled |
| `onChange` | `(text: string) => void` | - | Runs after the internal search query update |
| `className` | `string` | - | Additional CSS classes |
| `...SearchFieldProps` | `SearchFieldProps` | - | All `SearchField` props are supported |
### PhoneNumberField.CountryList
Extends `FlatList` props (minus `data` / `renderItem`). The list mounts with the selected country centred in the visible area, so there are rows to scroll through in both directions; pass `initialScrollIndex={null}` to open at the top instead.
Jumping hundreds of rows down needs to know how tall a row is, so the list measures its first row and treats the rest as equally tall — true for the default rows and for most custom ones. Rows that deliberately vary in height should come with their own `getItemLayout`.
| prop | type | default | description |
| -------------------- | ------------------------------------------------------ | ---------------------- | -------------------------------------------------------------- |
| `countries` | `PhoneNumberFieldCountry[]` | filtered list | Custom data source |
| `renderCountry` | `(info: PhoneNumberFieldCountryRenderInfo) => element` | `CountryItem` | Custom row renderer |
| `emptyText` | `string` | `'No countries found'` | Message when the search matches no countries |
| `className` | `string` | - | Additional CSS classes for the list container |
| `classNames` | `{ base?, empty?, emptyText? }` | - | CSS classes per slot |
| `styles` | `{ base?, empty?: ViewStyle; emptyText?: TextStyle }` | - | Styles per slot |
| `initialScrollIndex` | `number \| null` | selected country row | Row the list centres on when it opens; `null` opens at the top |
| `getItemLayout` | `FlatListProps['getItemLayout']` | measured row height | Row geometry; override for variable-height rows |
| `...FlatListProps` | `FlatListProps` | - | All standard FlatList props are supported |
### PhoneNumberField.CountryItem
Extends `Select.Item` (minus `value` / `label`, derived from `country`).
| prop | type | default | description |
| -------------- | ---------------------------------------- | ----------- | ---------------------------------------------------- |
| `country` | `PhoneNumberFieldCountry` | - | The country entry rendered by this row |
| `children` | `React.ReactNode` | default row | Custom row content replacing flag / dial code / name |
| `classNames` | `{ flag?, dialCode?, name? }` | - | CSS classes per slot |
| `styles` | `{ flag?, dialCode?, name?: TextStyle }` | - | Styles per slot |
| `...ItemProps` | `SelectItemProps` | - | All `Select.Item` props are supported |
## Hooks
### usePhoneNumberField
Hook to access the PhoneNumberField context. Must be used within a `PhoneNumberField` component.
```tsx
import {usePhoneNumberField} from "heroui-native-pro";
const {country, nationalNumber, formattedNumber, isOpen} = usePhoneNumberField();
```
#### Returns: PhoneNumberFieldContextValue
| property | type | description |
| --------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `country` | `PhoneNumberFieldCountry` | The currently selected country |
| `countries` | `PhoneNumberFieldCountry[]` | Full country list available in the picker |
| `filteredCountries` | `PhoneNumberFieldCountry[]` | Country list filtered by the current search query |
| `nationalNumber` | `string` | National number digits without formatting |
| `formattedNumber` | `string` | Formatted national number |
| `inputValue` | `string` | Text displayed by the input — the formatted national number, or the raw prefix while a dial code is being typed |
| `placeholder` | `string` | Mask-derived placeholder for the selected country |
| `isOpen` | `boolean` | Whether the country picker is open |
| `searchQuery` | `string` | Current country search query |
| `isDisabledRoot` | `boolean` | Whether the root field is disabled |
| `onInputChangeText` | `(text: string) => void` | Commits raw text typed into the phone input |
| `onCountrySelect` | `(country: PhoneNumberFieldCountry) => void` | Commits a country selection |
| `onOpenChange` | `(open: boolean) => void` | Changes the picker open state |
| `onSearchQueryChange` | `(query: string) => void` | Changes the country search query |
## Utilities
Exported helpers for working with phone numbers outside the component:
| export | signature | description |
| ------------------------------ | ----------------------------------------------- | ------------------------------------------------------------------- |
| `PHONE_NUMBER_FIELD_COUNTRIES` | `PhoneNumberFieldCountry[]` | The full built-in country dataset |
| `buildE164PhoneNumber` | `(digits, country) => string` | E.164 representation (`"+15551234567"`) |
| `getIsValidPhoneNumber` | `(digits, country) => boolean` | Validity for the country (numbering plans with `libphonenumber-js`) |
| `getIsCompletePhoneNumber` | `(digits, country) => boolean` | Plausible-length check for the country |
| `findCountryByCode` | `(countries, code) => country \| undefined` | Lookup by ISO 3166-1 alpha-2 code (case-insensitive) |
| `findCountryByDialCode` | `(countries, dialCode) => country \| undefined` | Lookup by dial code, resolving shared codes (e.g. NANP `+1`) |
# RadioButtonGroup
**Category**: native
**URL**: https://heroui.pro/docs/native/components/radio-button-group
> A compound radio group for choosing one option, with flexible item content and styling hooks for selected state and variants.
## Import
```tsx
import { RadioButtonGroup } from 'heroui-native-pro';
```
## Anatomy
```tsx
...
```
* **RadioButtonGroup**: Root wrapper around HeroUI Native `RadioGroup`. Holds the selected `value`, `onValueChange`, and optional group `variant`. Exposes the same context as `RadioGroup` (`useRadioButtonGroup` matches `useRadioGroup`).
* **RadioButtonGroup.Item**: Wraps `RadioGroup.Item`. Sets `data-selected` and `data-variant` for Tailwind variants, merges default item styles, and maps your `variant` to the underlying item variant so row styling stays consistent.
* **RadioButtonGroup.ItemBackground**: Optional theme-aware background container rendered behind the item surface. Mounted automatically for the unselected `secondary` variant when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **RadioButtonGroup.ItemContent**: Optional row container (`flex-1`) for label text, descriptions, and `Radio` / `Radio.Indicator`. Place the `Radio` inside the item as sibling or child depending on layout.
* **Radio**: The radio control component.
## Usage
### Basic usage
Controlled group with two items and centered content inside each row.
```tsx
const [subscription, setSubscription] = useState('quarterly');
...
...
;
```
### Group variant
Set `variant` on the root so items inherit `primary` or `secondary` surface styling unless an item overrides `variant`.
```tsx
...
```
### With radio and labels
Use `Radio` with `Label` and `Description` inside an item for a typical list row.
```tsx
...
```
### Render function children
Use a render function on `RadioButtonGroup.Item` to read `isSelected` and compose a custom `Radio.Indicator`.
```tsx
{({ isSelected }) => (
<>
...{isSelected && ...}
>
)}
```
## Example
```tsx
import { Chip } from 'heroui-native';
import { RadioButtonGroup } from 'heroui-native-pro';
import { useState } from 'react';
import { View } from 'react-native';
import { AppText } from '../../../components/app-text';
const PrimaryVariantContent = () => {
const [subscription, setSubscription] = useState('quarterly');
return (
Choose a subscription
Choose the plan that best fits your needs
3
months
$3.99/wk
Save 73%
1
month
$5.77/wk
Popular
);
};
```
## API Reference
Full prop tables, render-prop shapes, and hook return values are documented for HeroUI Native `RadioGroup` here:
**[RadioGroup API reference](https://heroui.com/docs/native/components/radio-group#api-reference)**
Inheritance in this package:
* **`RadioButtonGroup`** — Same API as **`RadioGroup`** ([`RadioGroupProps`](https://heroui.com/docs/native/components/radio-group#api-reference)). Forwarded to the underlying `RadioGroup` root.
* **`RadioButtonGroup.Item`** — Same API as **`RadioGroup.Item`** ([`RadioGroupItemProps`](https://heroui.com/docs/native/components/radio-group#api-reference), including `RadioGroupItemRenderProps` for render children). Adds `data-selected`, `data-variant`, default item styling, and variant mapping for the wrapped row; see the source if you rely on those details. Also adds a `background` prop (`React.ReactNode`) for the layer behind the item surface: `undefined` renders the theme-aware default for the unselected `secondary` variant; a custom node replaces it; `null` removes it.
* **`RadioButtonGroup.ItemBackground`** — Not part of HeroUI `RadioGroup`. Absolute-fill container rendered behind the item surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping. Extends React Native **`View`** with optional `children` and `className` (see `RadioButtonGroupItemBackgroundProps` in this repo).
* **`RadioButtonGroup.ItemContent`** — Not part of HeroUI `RadioGroup`. Extends React Native **`View`** with an optional `className` (see `RadioButtonGroupItemContentProps` in this repo).
* **`useRadioButtonGroup`** — Same behavior as **`useRadioGroup`** from `heroui-native`.
* **`useRadioButtonGroupItem`** — Same behavior as **`useRadioGroupItem`** from `heroui-native`.
# WheelPickerGroup
**Category**: native
**URL**: https://heroui.pro/docs/native/components/wheel-picker-group
> A coordinated row of `WheelPicker` instances that share layout, emit a composite values record, and report when every wheel has come to rest.
## Import
```tsx
import { WheelPicker, WheelPickerGroup } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **WheelPickerGroup**: Root container. Owns a shared controllable `values` record keyed by each child wheel's `name`, broadcasts `itemHeight` / `visibleCount` to the group, coordinates cross-wheel scroll syncing, and cascades `animation="disable-all"` to all child wheels. Child wheels nested in the group automatically receive `flex-1` so they distribute the row evenly.
* **WheelPickerGroup.Indicator**: Optional shared selection band spanning every wheel at the center of the group viewport. Replaces the per-wheel indicator when a `WheelPicker` is nested in the group.
* **WheelPickerGroup.IndicatorBackground**: Optional theme-aware background container rendered behind the highlight band's surface. Mounted automatically when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **WheelPickerGroup.Mask**: Optional top / bottom fade overlays spanning the full group viewport.
## Usage
### Basic usage
Compose a row of `WheelPicker` instances inside the group. Each child wheel declares a unique `name` and the group keys the shared values record by that name.
```tsx
const [values, setValues] = useState({ currency: 'USD', amount: 100 });
;
```
### Uncontrolled
Pass `defaultValues` to seed the initial selections without managing external state.
```tsx
console.log(next)}
>
```
### Item height and visible count
`itemHeight` and `visibleCount` are set on the group and shared across every wheel. `visibleCount` must be odd.
```tsx
```
### Custom indicator
Style the shared selection band via `classNames` on `WheelPickerGroup.Indicator`, or pass any `children` for decorative content rendered inside the `highlight` slot (patterns, gradients, icons). The indicator spans every wheel column. Pair with `overflow-hidden` on the highlight when the children should be clipped to the rounded corners.
```tsx
```
### Custom mask color
Override the fade color when the group sits on a non-`background` surface. Combine with `height` (number or percentage) to control how far the fade extends.
```tsx
const overlayColor = useThemeColor('overlay');
;
```
### Commit on rest
`onValuesCommit` fires exactly once after every wheel in the group has come to rest. Use it to commit a composite selection without listening to intermediate scroll updates.
```tsx
submitOrder(next)}
>
```
### Disable animations
Pass `animation="disable-all"` on the group to cascade the disabled state to every child wheel (rows snap without fading or scaling, and any animated descendants of a child wheel are also disabled).
```tsx
```
### Disabled
Block interaction and dim the group with `isDisabled`. Each child wheel can still set its own `isDisabled` independently.
```tsx
```
## Example
```tsx
import { WheelPicker, WheelPickerGroup } from 'heroui-native-pro';
import { useState } from 'react';
import { Text, View } from 'react-native';
const CURRENCY_ITEMS = [
{ value: 'USD', label: 'USD' },
{ value: 'EUR', label: 'EUR' },
{ value: 'GBP', label: 'GBP' },
{ value: 'JPY', label: 'JPY' },
{ value: 'CAD', label: 'CAD' },
];
const AMOUNT_ITEMS = [50, 100, 200, 500, 1000, 2500, 5000].map((amount) => ({
value: amount,
label: String(amount),
}));
export default function TransferAmountPicker() {
const [values, setValues] = useState>({
currency: 'USD',
amount: 500,
});
return (
Send
{String(values.currency)} {String(values.amount)}
);
}
```
## API Reference
### WheelPickerGroup
| prop | type | default | description |
| ---------------- | ------------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound children. Typically a sequence of `WheelPicker` instances, optionally accompanied by `WheelPickerGroup.Indicator` and `Mask` |
| `values` | `WheelPickerGroupValues` | - | Controlled values record. Keyed by each child wheel's `name` |
| `defaultValues` | `WheelPickerGroupValues` | - | Uncontrolled initial values record. Keyed by each child wheel's `name` |
| `itemHeight` | `number` | `44` | Pixel height of a single row, shared by all child wheels |
| `visibleCount` | `number` | `5` | Number of visible rows, shared by all child wheels. Must be odd |
| `isDisabled` | `boolean` | `false` | Disables interaction for the whole group. Each child wheel may also be disabled locally |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onValuesChange` | `(values: WheelPickerGroupValues) => void` | - | Fires when any wheel's value changes — during scroll, on tap-to-focus, and on imperative scrolls. Not fired on external `values` updates |
| `onValuesCommit` | `(values: WheelPickerGroupValues) => void` | - | Fires exactly once after every wheel in the group has come to rest |
| `animation` | `WheelPickerGroupRootAnimation` | - | Animation configuration. Cascades `disable-all` to every child wheel |
| `ref` | `WheelPickerGroupRootRef` | - | Forwarded to the underlying root `View` |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported |
#### WheelPickerGroupValues
Shared values record exchanged with the group through `values`, `defaultValues`, and `onValuesChange` / `onValuesCommit`.
| key | type | description |
| -------- | --------- | ---------------------------------------------------------------------------------------------------------------------- |
| `[name]` | `unknown` | One entry per child wheel, keyed by the wheel's `name` prop. The value type is `unknown` to allow heterogeneous wheels |
#### WheelPickerGroupRootAnimation
Animation configuration for the group root. The group has no animated styles of its own — this prop only controls the `disable-all` cascade to every child wheel.
* `"disable-all"`: Disable all animations including children (cascades to every child `WheelPicker`)
* `undefined`: Use default animations
### WheelPickerGroup.Indicator
| prop | type | default | description |
| -------------- | ------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Optional content rendered inside the indicator's `highlight` slot (patterns, gradients, icons). Pair with `overflow-hidden` on the highlight so the content is clipped to the rounded corners |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual indicator slots |
| `styles` | `Partial>` | - | Inline styles for individual indicator slots |
| `background` | `React.ReactNode` | - | Background layer behind the highlight band's surface. `undefined` renders the theme-aware default; custom node replaces it; `null` removes it |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ----------- | --------------------------------------------------------- |
| `wrapper` | Absolutely-positioned band centered on the group viewport |
| `highlight` | Filled rectangle rendered inside the wrapper |
#### styles
| slot | type | description |
| ----------- | ----------- | ---------------------------------------- |
| `wrapper` | `ViewStyle` | Inline style for the indicator wrapper |
| `highlight` | `ViewStyle` | Inline style for the indicator highlight |
### WheelPickerGroup.IndicatorBackground
Absolute-fill container rendered behind the highlight band's surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### WheelPickerGroup.Mask
| prop | type | default | description |
| -------------- | ------------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `color` | `string` | `useThemeColor('background')` | Solid color the gradient fades from. Accepts any RN color string. Falls back to the theme `background` color when omitted |
| `height` | `number \| string` | `"100%"` | Height of each mask half. `number` = raw pixels; percentage scales the default fade height (`((visibleCount - 1) / 4) * itemHeight`) |
| `className` | `string` | - | Additional CSS classes applied to both mask halves |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual mask slots |
| `styles` | `Partial>` | - | Inline styles for individual mask slots |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| -------- | ------------------- |
| `top` | Top fade overlay |
| `bottom` | Bottom fade overlay |
#### styles
| slot | type | description |
| -------- | ----------- | ---------------------------------------- |
| `top` | `ViewStyle` | Inline style for the top fade overlay |
| `bottom` | `ViewStyle` | Inline style for the bottom fade overlay |
## Hooks
### useWheelPickerGroup
Hook to access the WheelPickerGroup context. Must be used within a `WheelPickerGroup` component.
```tsx
import { useWheelPickerGroup } from 'heroui-native-pro';
const { itemHeight, visibleCount, getValue, setValue } = useWheelPickerGroup();
```
#### Returns: WheelPickerGroupContextValue
| property | type | description |
| --------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `itemHeight` | `number` | Resolved row height shared across the group's wheels |
| `visibleCount` | `number` | Number of visible rows shared across the group's wheels |
| `isDisabled` | `boolean` | Whether the group as a whole is disabled |
| `getValue` | `(name: string) => unknown` | Read the current value for the wheel identified by `name` |
| `setValue` | `(name: string, value: unknown) => void` | Write the value for the wheel identified by `name`. Merges into the group's values record |
| `notifyScrollState` | `(isScrolling: boolean) => void` | Notify the group that one of its wheels has started or stopped scrolling. Used internally by child wheels |
| `isAnyWheelScrolling` | `() => boolean` | Returns `true` while at least one wheel in the group is scrolling |
# WheelPicker
**Category**: native
**URL**: https://heroui.pro/docs/native/components/wheel-picker
> A vertical wheel picker with snap-to-row selection, distance-based fade and scale, and optional fade overlays.
## Import
```tsx
import { WheelPicker } from 'heroui-native-pro';
```
## Anatomy
```tsx
```
* **WheelPicker**: Root container. Manages controllable selection through `value` / `defaultValue` / `onValueChange`, owns the shared scroll offset, and renders a data-driven scrolling list of rows. Cascades animation settings to children and auto-renders a default indicator when no compound children are passed.
* **WheelPicker.Item**: Animated row container. Auto-rendered for every option, or used as the outer element inside a custom `renderItem`. When no children are provided, renders `WheelPicker.ItemLabel` with the option's label. Tapping a row scrolls the wheel to focus it.
* **WheelPicker.ItemLabel**: Default label primitive. Used by the auto-fallback inside `WheelPicker.Item`; reuse inside a custom `renderItem` to keep the default label styling.
* **WheelPicker.Indicator**: Optional selection band rendered absolutely at the center of the viewport. Purely visual — selection logic lives on the root.
* **WheelPicker.IndicatorBackground**: Optional theme-aware background container rendered behind the highlight band's surface. Mounted automatically when the active theme registers default background content (e.g. `glass`). Replace or remove it via the `background` prop.
* **WheelPicker.Mask**: Optional top / bottom fade overlays that soften the wheel into the surrounding background.
## Usage
### Basic usage
Pass a list of `{ value, label }` items and bind `value` / `onValueChange`. The root renders a default indicator when no compound children are present.
```tsx
const [year, setYear] = useState(1995);
;
```
### With mask
Add a `WheelPicker.Mask` for top / bottom fade overlays. Explicit children replace the auto-rendered indicator, so include `WheelPicker.Indicator` too.
```tsx
```
### Uncontrolled
Use `defaultValue` to seed the initial selection without managing external state.
```tsx
```
### Item height and visible count
Customize row height and the number of visible rows. `visibleCount` must be odd so a single row sits centered on the indicator.
```tsx
```
### Custom item render
Pass `renderItem` to compose custom content per row. Use `WheelPicker.Item` as the outer wrapper to preserve sizing and tap-to-focus behavior.
```tsx
(
{item.label}
)}
>
```
### Custom indicator
Style the selection band via `classNames` on `WheelPicker.Indicator`, or pass any `children` for decorative content rendered inside the `highlight` slot (patterns, gradients, icons). Pair with `overflow-hidden` on the highlight when the children should be clipped to the rounded corners.
```tsx
```
### Custom mask color
Override the fade color when the wheel sits on a non-`background` surface. Combine with `height` (number or percentage) to control how far the fade extends.
```tsx
const overlayColor = useThemeColor('overlay');
;
```
### Custom animation
Tune the per-item `[edge, center]` opacity, scale, and label color ranges. The label color animation is always active (defaulting to theme `[foreground, accent-soft-foreground]`) — `text-*` color classes on `classNames.itemLabel` are overridden by the animated value.
```tsx
```
### Disabled
Block interaction and dim the wheel with `isDisabled`.
```tsx
```
### Programmatic scroll
Use the ref to drive the selection imperatively. `scrollToValue` finds the matching row via `Object.is` equality and is a no-op when the value is not in `items`.
```tsx
const ref = useRef(null);
;
ref.current?.scrollToValue(2000);
ref.current?.scrollToIndex({ index: 0, animated: false });
```
## Example
```tsx
import { WheelPicker } from 'heroui-native-pro';
import { useState } from 'react';
import { Text, View } from 'react-native';
const CURRENT_YEAR = new Date().getFullYear();
const YEAR_ITEMS = Array.from({ length: 80 }, (_, index) => {
const year = CURRENT_YEAR - 18 - index;
return { value: year, label: String(year) };
});
export default function BirthYearPicker() {
const [year, setYear] = useState(1995);
return (
Date of birth
{year}
);
}
```
## API Reference
### WheelPicker
| prop | type | default | description |
| --------------- | ------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Compound parts. When omitted, a default `WheelPicker.Indicator` is rendered (skipped inside a `WheelPickerGroup`) |
| `items` | `ReadonlyArray>` | - | List of `{ value, label }` rows rendered by the wheel |
| `itemHeight` | `number` | `44` | Pixel height of a single row. Drives snapping, layout, and animation math. Ignored when nested in a `WheelPickerGroup` (inherited from the group) |
| `visibleCount` | `number` | `5` | Number of rows visible inside the viewport. Must be odd so one row sits centered on the indicator. Ignored when nested in a `WheelPickerGroup` (inherited from the group) |
| `value` | `T` | - | Controlled selected value. The row whose `item.value` matches becomes the selected row |
| `defaultValue` | `T` | - | Initial value used when the wheel is uncontrolled |
| `name` | `string` | - | Identifies this wheel inside a `WheelPickerGroup`. When set and a group context exists, the wheel reads / writes its value via the group |
| `isDisabled` | `boolean` | `false` | Disables interaction. The wheel still renders the current selection |
| `className` | `string` | - | Additional CSS classes for the root container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual root slots |
| `styles` | `WheelPickerRootStyles` | - | Inline styles for individual root slots |
| `renderItem` | `WheelPickerRenderItem` | - | Custom row renderer. When omitted, the default renderer shows `item.label` inside a `WheelPicker.ItemLabel` |
| `keyExtractor` | `(item: WheelPickerOption, index: number) => string` | Primitive-aware default | Key extractor for the underlying `FlatList`. Defaults to `` `${value}:${index}` `` for primitives and `String(index)` otherwise |
| `onValueChange` | `(value: T) => void` | - | Fires when the selected row changes during scroll, on tap-to-focus, and on imperative `scrollToIndex` / `scrollToValue` |
| `animation` | `WheelPickerRootAnimation` | - | Animation configuration for the per-item opacity / scale interpolation |
| `ref` | `WheelPickerRootRef` | - | Imperative ref exposing `scrollToIndex` and `scrollToValue` in addition to the underlying view |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported |
#### WheelPickerOption
Single picker entry rendered as a row in the wheel.
| prop | type | description |
| ------- | -------- | --------------------------------------------------------------------- |
| `value` | `T` | Unique value used for selection comparison and `scrollToValue` lookup |
| `label` | `string` | Display label rendered by the default item renderer |
#### WheelPickerRenderItemInfo
Argument passed to `renderItem`.
| prop | type | description |
| ------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `item` | `WheelPickerOption` | The option being rendered |
| `index` | `number` | Zero-based index of the option in `items` |
| `isSelected` | `boolean` | Whether the option sits on the center selection band |
| `scrollY` | `SharedValue` | Shared scroll offset (UI thread) used to drive per-item animations |
| `itemHeight` | `number` | Resolved row height used by layout and animation math |
| `absDistance` | `SharedValue` | Per-row absolute distance from center, in row units (`0` = centered, `0.5` = selection boundary, `1+` = one full row or more away). Drive your own `interpolate` / `interpolateColor` on it for custom row animations |
#### ElementSlots\
| slot | description |
| ------------------ | ------------------------------------------------------------------------- |
| `container` | Outer viewport wrapping the scroll list and overlays |
| `contentContainer` | Scroll content container carrier; receives the vertical centering padding |
| `item` | Per-row animated container (see animated property notes below) |
| `itemLabel` | Default label text inside a row |
The `item` slot animates `opacity` and `transform` (scale) for distance-based fade and scale. These properties cannot be overridden via `className`; use the `animation` prop to customize, or `animation="disabled"` to remove them entirely.
#### styles
| slot | type | description |
| ------------------ | ----------- | --------------------------------------------- |
| `container` | `ViewStyle` | Inline style for the outer viewport |
| `contentContainer` | `ViewStyle` | Inline style for the scroll content container |
| `item` | `ViewStyle` | Inline style for the per-row container |
| `itemLabel` | `TextStyle` | Inline style for the default label text |
#### WheelPickerRootAnimation
Animation configuration for the per-item opacity, scale, and (optionally) label color interpolation. Can be:
* `false` or `"disabled"`: Disable per-item fade, scale, and label color (rows snap without interpolation)
* `"disable-all"`: Disable the wheel's animations and cascade `disable-all` to animated descendants
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------ | ------------------ | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `opacity.value` | `[number, number]` | `[0.5, 1]` | `[edge, center]` opacity values. `value[0]` is opacity at the farthest visible offset |
| `scale.value` | `[number, number]` | `[0.85, 1]` | `[edge, center]` scale values. `value[0]` is scale at the farthest visible offset |
| `labelColor.value` | `[string, string]` | Theme `[foreground, accent-soft-foreground]` | `[edge, center]` color values for the row label, interpolated via `interpolateColor` strictly inside the half-row selection band — non-selected rows resolve to exactly `value[0]` (edge) |
#### WheelPickerImperativeMethods
Exposed via the root `ref` on top of the underlying `View` ref.
| method | signature | description |
| --------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `scrollToIndex` | `(params: { index: number; animated?: boolean }) => void` | Scroll the wheel so the given index becomes the selected row |
| `scrollToValue` | `(value: unknown, options?: { animated?: boolean }) => void` | Scroll the wheel so the row matching `value` becomes the selected row. No-op when the value is not in `items` |
### WheelPicker.Indicator
| prop | type | default | description |
| -------------- | ------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Optional content rendered inside the indicator's `highlight` slot (patterns, gradients, icons). Pair with `overflow-hidden` on the highlight so the content is clipped to the rounded corners |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual indicator slots |
| `styles` | `Partial>` | - | Inline styles for individual indicator slots |
| `background` | `React.ReactNode` | - | Background layer behind the highlight band's surface. `undefined` renders the theme-aware default; custom node replaces it; `null` removes it |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| ----------- | --------------------------------------------------------- |
| `wrapper` | Absolutely-positioned band centered on the wheel viewport |
| `highlight` | Filled rectangle rendered inside the wrapper |
#### styles
| slot | type | description |
| ----------- | ----------- | ---------------------------------------- |
| `wrapper` | `ViewStyle` | Inline style for the indicator wrapper |
| `highlight` | `ViewStyle` | Inline style for the indicator highlight |
### WheelPicker.IndicatorBackground
Absolute-fill container rendered behind the highlight band's surface. With no children, the active library theme decides the default content (e.g. a glass blur layer); pass children to host custom content with the same positioning and clipping.
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom content inside the background container |
| `className` | `string` | - | Additional CSS classes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### WheelPicker.Mask
| prop | type | default | description |
| -------------- | -------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `color` | `string` | `useThemeColor('background')` | Solid color the gradient fades from. Accepts any RN color string. Falls back to the theme `background` color when omitted |
| `height` | `number \| string` | `"100%"` | Height of each mask half. `number` = raw pixels; percentage scales the default fade height (`((visibleCount - 1) / 4) * itemHeight`) |
| `className` | `string` | - | Additional CSS classes applied to both mask halves |
| `classNames` | `ElementSlots` | - | Additional CSS classes for individual mask slots |
| `styles` | `Partial>` | - | Inline styles for individual mask slots |
| `...ViewProps` | `Omit` | - | All standard React Native View props are supported |
#### ElementSlots\
| slot | description |
| -------- | ------------------- |
| `top` | Top fade overlay |
| `bottom` | Bottom fade overlay |
#### styles
| slot | type | description |
| -------- | ----------- | ---------------------------------------- |
| `top` | `ViewStyle` | Inline style for the top fade overlay |
| `bottom` | `ViewStyle` | Inline style for the bottom fade overlay |
### WheelPicker.Item
`WheelPicker.Item` is the per-row animated container. When no `children` are provided, it auto-renders `{item.label}`. Tapping the row scrolls the wheel to focus it; a consumer `onPress` runs first.
| prop | type | default | description |
| ------------------- | --------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom row content. When omitted, the row renders `WheelPicker.ItemLabel` with the option's label |
| `className` | `string` | - | Additional CSS classes for the row container (see animated property notes below) |
| `style` | `ViewStyle` | - | Inline style for the row container. Merged after the root `styles.item` cascade and the animated transform style |
| `...PressableProps` | `Omit` | - | All standard React Native Pressable props are supported (`onPressIn`, `hitSlop`, `disabled`, etc.) |
The row container animates `opacity` and `transform` (scale) for distance-based fade and scale. These properties cannot be overridden via `className`; use the `animation` prop on the root to customize, or `animation="disabled"` to remove them entirely.
### WheelPicker.ItemLabel
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes applied to the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
## Hooks
### useWheelPicker
Hook to access the WheelPicker root context. Must be used within a `WheelPicker` component.
```tsx
import { useWheelPicker } from 'heroui-native-pro';
const { itemHeight, visibleCount, scrollY, isDisabled } = useWheelPicker();
```
#### Returns: WheelPickerContextValue
| property | type | description |
| ------------------------- | --------------------------------------------------------- | ------------------------------------------------------------ |
| `itemHeight` | `number` | Resolved row height in pixels |
| `visibleCount` | `number` | Number of visible rows (always odd) |
| `isDisabled` | `boolean` | Whether the wheel is disabled |
| `scrollY` | `SharedValue` | Shared scroll offset (UI thread) driving per-item animations |
| `isInsideGroup` | `boolean` | Whether a `WheelPickerGroup` parent provided the value |
| `resolvedAnimation` | `WheelPickerResolvedAnimationConfig` | Resolved `[edge, center]` opacity and scale ranges |
| `isItemAnimationDisabled` | `boolean` | Whether per-item animation is disabled (own + cascade) |
| `scrollToIndex` | `(params: { index: number; animated?: boolean }) => void` | Imperative helper to scroll the wheel to a row index |
### useWheelPickerItem
Hook to access the per-row item context. Must be used within a `WheelPicker.Item`, `WheelPicker.ItemLabel`, or any component rendered inside a custom `renderItem`. The context erases the generic to `unknown`; cast at the call site (`as WheelPickerItemRenderProps`) when you need strict typing on `item.value`.
```tsx
import { useWheelPickerItem } from 'heroui-native-pro';
const { item, index, isSelected, absDistance } = useWheelPickerItem();
```
Use `absDistance` together with `useAnimatedStyle` / `useAnimatedProps` to build your own row-content animations (icons, badges, halos, indicators, etc.) without recomputing distance math yourself.
#### Returns: WheelPickerItemRenderProps
| property | type | description |
| ------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `item` | `WheelPickerOption` | The option being rendered |
| `index` | `number` | Zero-based index of the option in `items` |
| `isSelected` | `boolean` | Whether the option matches the current value |
| `absDistance` | `SharedValue` | Per-row absolute distance from center, in row units (`0` = centered, `0.5` = selection boundary, `1+` = one full row or more away). Read with `.get()` inside a worklet |
# Segment
**Category**: native
**URL**: https://heroui.pro/docs/native/components/segment
> A React Native segmented control for switching between a small set of mutually exclusive mobile options.
## Import
```tsx
import { Segment } from 'heroui-native-pro';
```
## Anatomy
```tsx
...
```
* **Segment**: Root container. Manages controlled / uncontrolled selection through `value`, `defaultValue`, and `onValueChange`, cascades `size` and `isDisabled` through context, and forwards animation settings to the underlying `Tabs`.
* **Segment.Group**: Row container for items, indicator, and separators. Wraps `Tabs.List` and applies size-aware padding and rounding.
* **Segment.ScrollView**: Optional horizontally scrollable wrapper for items, used when the row exceeds the available width. Wraps `Tabs.ScrollView`.
* **Segment.Indicator**: Animated pill that slides between selected items. Width, height, position, and opacity are driven by `react-native-reanimated`.
* **Segment.Item**: Selectable trigger for a single segment. Wraps `Tabs.Trigger`; merges its own `isDisabled` with the root `isDisabled` flag.
* **Segment.Label**: Text label rendered inside an item. Wraps `Tabs.Label`.
* **Segment.Separator**: Vertical divider between items. Visibility is driven by `betweenValues` relative to the current selection.
## Usage
### Basic usage
Wrap items inside `Segment.Group` with an `Indicator` and per-item `Label`s. Each item declares a unique `value`.
```tsx
DashboardAnalytics
```
### With separators
Insert `Segment.Separator`s between items and pass `betweenValues` so the divider hides automatically when one of its neighbors is selected.
```tsx
DashboardAnalytics
```
### Sizes
Use the `size` prop to scale padding, indicator radius, and label typography across every compound part.
```tsx
.........
```
### Controlled
Drive selection externally with `value` and `onValueChange`.
```tsx
const [selected, setSelected] = useState('dashboard');
DashboardAnalytics;
```
### Scrollable
Wrap items in `Segment.ScrollView` to enable horizontal scrolling when the row overflows. The selected item is centered automatically.
```tsx
DashboardAnalyticsReportsSettings
```
### With icons
Items accept arbitrary children, including icon-only or icon-with-label compositions.
```tsx
```
### Disabled
Set `isDisabled` on the root to dim and block presses on every item.
```tsx
DashboardAnalytics
```
### Disabled item
Disable an individual item with its own `isDisabled` while keeping the rest interactive.
```tsx
DashboardAnalytics
```
## Example
```tsx
import Feather from '@expo/vector-icons/Feather';
import { Segment } from 'heroui-native-pro';
import { useState } from 'react';
import { View } from 'react-native';
import { withUniwind } from 'uniwind';
const StyledFeather = withUniwind(Feather);
export default function ThemeSwitcher() {
const [theme, setTheme] = useState('system');
return (
);
}
```
## API Reference
### Segment
| prop | type | default | description |
| --------------- | ------------------------- | ------- | -------------------------------------------------------------------------- |
| `value` | `string` | - | Controlled selected segment value |
| `defaultValue` | `string` | - | Initial selected segment in uncontrolled mode |
| `size` | `SegmentSize` | `'md'` | Visual size affecting padding, typography, and radii across every part |
| `isDisabled` | `boolean` | `false` | Disables interaction for every item (merged with each item's `isDisabled`) |
| `onValueChange` | `(value: string) => void` | - | Handler called when the selected segment changes |
Inherits remaining props from [`Tabs`](https://heroui.com/docs/native/components/tabs#tabs) except for `value`, `onValueChange`, and `variant` (fixed to `"primary"`).
#### SegmentSize
| value | description |
| ------ | ----------------- |
| `'sm'` | Compact size tier |
| `'md'` | Default size tier |
| `'lg'` | Large size tier |
### Segment.Group
Same API as [`Tabs.List`](https://heroui.com/docs/native/components/tabs#tabslist).
### Segment.ScrollView
Same API as [`Tabs.ScrollView`](https://heroui.com/docs/native/components/tabs#tabsscrollview).
### Segment.Indicator
Same API as [`Tabs.Indicator`](https://heroui.com/docs/native/components/tabs#tabsindicator), including [`TabsIndicatorAnimation`](https://heroui.com/docs/native/components/tabs#tabsindicatoranimation).
### Segment.Item
Same API as [`Tabs.Trigger`](https://heroui.com/docs/native/components/tabs#tabstrigger), including [`TabsTriggerRenderProps`](https://heroui.com/docs/native/components/tabs#tabstriggerrenderprops) for render children.
### Segment.Label
Same API as [`Tabs.Label`](https://heroui.com/docs/native/components/tabs#tabslabel).
### Segment.Separator
Same API as [`Tabs.Separator`](https://heroui.com/docs/native/components/tabs#tabsseparator), including [`TabsSeparatorAnimation`](https://heroui.com/docs/native/components/tabs#tabsseparatoranimation).
## Hooks
### useSegment
Hook to access the segment root context. Must be used within a `Segment` component.
```tsx
import { useSegment } from 'heroui-native-pro';
const { value, onValueChange, size, isDisabled } = useSegment();
```
#### Returns: SegmentContextValue
| property | type | description |
| --------------- | ------------------------- | ------------------------------------------------------------------------- |
| `value` | `string` | Currently selected segment value (resolves to `""` when no item selected) |
| `onValueChange` | `(value: string) => void` | Updates the selected segment |
| `size` | `SegmentSize` | Current size tier propagated from the root |
| `isDisabled` | `boolean` | Whether selection is prevented for every item from the root |
# SplitView
**Category**: native
**URL**: https://heroui.pro/docs/native/components/split-view
> A vertical split layout with a draggable divider between a top and bottom section.
## Import
```tsx
import { SplitView } from 'heroui-native-pro';
```
## Anatomy
```tsx
......
```
* **SplitView**: Root container that manages snap points, drag state, and layout. Exposes layout state and snap controls via `useSplitView` and render props.
* **SplitView\.TopSection**: Top pane with animated height driven by drag and snap state. Content should scroll internally when it exceeds the current height.
* **SplitView\.DragArea**: Pan gesture hit region with an extended touch target. Typically wraps `SplitView.DragHandle`.
* **SplitView\.DragHandle**: Visual pill handle. Scales slightly while dragging when animations are enabled.
* **SplitView\.BottomSection**: Bottom pane that fills remaining space below the drag area.
## Usage
### Basic usage
The SplitView uses compound parts to build a vertical split layout with a draggable divider.
```tsx
......
```
### Custom snap points
Pass `snapPoints` as ratios of container height (`0`..`1`) or absolute px (`> 1`). Combine with `defaultSnapIndex` and `minHeight` for uncontrolled layouts.
```tsx
......
```
### Skip initial animation
By default (`skipInitialAnimation` is `true`) the divider appears already positioned at its starting snap point instead of animating into place on mount or screen focus. Subsequent snaps and drags still animate. Set `skipInitialAnimation={false}` to animate the divider into place on first render.
```tsx
......
```
### Controlled snap index
Control the active snap index externally with `snapIndex` and `onSnapIndexChange`.
```tsx
const [snapIndex, setSnapIndex] = useState(1);
......;
```
### Render function children
Use a render function to read layout state and drive animations inside children.
```tsx
{({ topSectionHeight, minPx, maxPx }) => (
<>
......
>
)}
```
### Accessing context from children
Use the `useSplitView` hook inside any descendant to read animated layout values and trigger snap transitions.
```tsx
const { topSectionHeight, minPx, snapTo } = useSplitView();
```
### Disabled animation
Disable all animations for the subtree with `animation="disable-all"`.
```tsx
......
```
## Example
```tsx
import { SplitView } from 'heroui-native-pro';
import { Text, View } from 'react-native';
export default function SplitViewExample() {
return (
Top section — drag the handle to resize.
Bottom section fills remaining space.
);
}
```
## API Reference
### SplitView
| prop | type | default | description |
| ---------------------- | ----------------------------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((props: SplitViewRenderProps) => React.ReactNode)` | - | Child elements or render function receiving layout state |
| `snapPoints` | `readonly number[]` | `[0.2, 0.5, 0.8]` | Snap points as ratios (`0`..`1`) of container height or absolute px (`> 1`) |
| `minHeight` | `number` | `100` | Minimum height of the top section as px or ratio (`0`..`1`) |
| `maxHeight` | `number` | - | Maximum height of the top section as px, ratio (`0`..`1`), or negative offset |
| `defaultSnapIndex` | `number` | `1` | Default snap index for uncontrolled usage |
| `skipInitialAnimation` | `boolean` | `true` | Skip the spring on the first snap so the divider starts already in place |
| `snapIndex` | `number` | - | Controlled snap index |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onSnapIndexChange` | `(index: number) => void` | - | Callback fired when the snap index changes |
| `onSnap` | `(snapIndex: number, topHeightPx: number) => void` | - | Callback fired after a snap completes with the resolved index and top height |
| `animation` | `SplitViewRootAnimation` | - | Root animation configuration |
| `asChild` | `boolean` | `false` | Merge props onto the child element instead of rendering a wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SplitViewSnapPoint
| type | description |
| -------- | ------------------------------------------------------------------ |
| `number` | Ratio of container height when in `[0, 1]`; absolute px when `> 1` |
#### SplitViewRenderProps
| prop | type | description |
| -------------------- | ---------------------- | -------------------------------------------------- |
| `topSectionHeight` | `SharedValue` | Animated height of the top section in px |
| `isDragging` | `SharedValue` | Whether the user is currently dragging the divider |
| `containerHeight` | `SharedValue` | Measured container height in px |
| `snapIndex` | `number` | Current snap index into `resolvedSnapPoints` |
| `resolvedSnapPoints` | `readonly number[]` | Snap heights in px, clamped and sorted |
| `minPx` | `number` | Minimum allowed top section height in px |
| `maxPx` | `number` | Maximum allowed top section height in px |
#### SplitViewRootAnimation
Animation configuration for the root component. Can be:
* `false` or `"disabled"`: Disable only root animations
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `snapSpringConfig` | `WithSpringConfig` | `{ damping: 25, stiffness: 300, mass: 0.8, overshootClamping: false, restDisplacementThreshold: 0.01, restSpeedThreshold: 0.01 }` | Spring used when snapping the top section after drag release or `snapTo` |
### SplitView\.TopSection
| prop | type | default | description |
| -------------- | ----------------- | ------- | --------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content rendered inside the top pane |
| `className` | `string` | - | Additional CSS classes for the top section (the `height` style is reserved) |
| `asChild` | `boolean` | `false` | Merge props onto the child element instead of rendering a wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### SplitView\.DragArea
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content rendered inside the drag hit region |
| `className` | `string` | - | Additional CSS classes for the drag area |
| `asChild` | `boolean` | `false` | Merge props onto the child element instead of rendering a wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### SplitView\.DragHandle
| prop | type | default | description |
| ----------------------- | ------------------------------ | ------- | ------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom handle content. Defaults to a pill indicator |
| `className` | `string` | - | Additional CSS classes for the handle (the `transform` style is reserved) |
| `isAnimatedStyleActive` | `boolean` | `true` | When `false`, animated scale styles are not applied |
| `animation` | `SplitViewDragHandleAnimation` | - | Animation configuration for the handle scale |
| `asChild` | `boolean` | `false` | Merge props onto the child element instead of rendering a wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### SplitViewDragHandleAnimation
Animation configuration for the drag handle. Can be:
* `false` or `"disabled"`: Disable all handle animations
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | description |
| ------- | ----------------------------------- | ------------------------------------------------ |
| `scale` | `SplitViewDragHandleScaleAnimation` | Scale animation between idle and dragging states |
#### SplitViewDragHandleScaleAnimation
| prop | type | default | description |
| -------------- | ------------------ | -------------------------------------------- | -------------------------------------------------------------- |
| `value` | `[number, number]` | `[1, 1.15]` | Scale values `[idle, dragging]` |
| `springConfig` | `WithSpringConfig` | `{ damping: 18, stiffness: 300, mass: 0.8 }` | Spring used when transitioning between idle and dragging scale |
### SplitView\.BottomSection
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content rendered inside the bottom pane |
| `className` | `string` | - | Additional CSS classes for the bottom section |
| `asChild` | `boolean` | `false` | Merge props onto the child element instead of rendering a wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
# Stepper
**Category**: native
**URL**: https://heroui.pro/docs/native/components/stepper
> A multi-step progress indicator for guiding users through sequential workflows.
## Import
```tsx
import { Stepper } from 'heroui-native-pro';
```
## Anatomy
```tsx
......
```
* **Stepper**: Root container that manages step state, orientation, and animation context. Controls which step is active via controlled or uncontrolled mode.
* **Stepper.Step**: Pressable container for an individual step. Automatically receives its index and status (`inactive`, `active`, `complete`).
* **Stepper.Rail**: Relative wrapper for the indicator and separator. Renders `Indicator` and `Separator` by default when children are omitted; `Separator` is omitted on step index `0`.
* **Stepper.Indicator**: Visual circle for each step. Renders `IndicatorCheck` and `IndicatorNumber` by default when children are omitted.
* **Stepper.IndicatorCheck**: Animated check icon that draws in when the step is complete.
* **Stepper.IndicatorNumber**: 1-based step number label displayed inside the indicator.
* **Stepper.Separator**: Connector line between steps. Renders `SeparatorTrack` and `SeparatorFill` by default when children are omitted.
* **Stepper.SeparatorTrack**: Static background track behind the separator fill.
* **Stepper.SeparatorFill**: Animated accent fill layered on the track, driven by root progress.
* **Stepper.Content**: Container for step title, description, and any additional content.
* **Stepper.Title**: Text label for the step title.
* **Stepper.Description**: Text label for the step description.
## Usage
### Basic Usage
The Stepper component uses compound parts to create a step-by-step indicator. Steps are pressable by default.
```tsx
AccountCreate your accountProfileSet up your profile
```
### Horizontal Orientation
Display steps in a horizontal layout.
```tsx
CartShippingPayment
```
### Controlled Step
Control the active step externally and respond to step changes.
```tsx
const [currentStep, setCurrentStep] = useState(0);
......
```
### Custom Indicator
Replace the default indicator content with custom icons per step.
```tsx
...
```
### Custom Separator Colors
Override separator and indicator colors using className on each compound part.
```tsx
...
...
```
### Disabled Animation
Disable all stepper animations using the root `animation` prop.
```tsx
...
```
## Example
```tsx
import { Button } from 'heroui-native';
import { Stepper } from 'heroui-native-pro';
import { useState } from 'react';
import { Text, View } from 'react-native';
const STEPS = [
{ description: 'Create your account', title: 'Account' },
{ description: 'Set up your profile', title: 'Profile' },
{ description: 'Configure preferences', title: 'Settings' },
{ description: 'Review and confirm', title: 'Review' },
];
export default function StepperExample() {
const [currentStep, setCurrentStep] = useState(0);
return (
{STEPS.map((s) => (
{s.title}{s.description}
))}
{`${currentStep + 1} / ${STEPS.length}`}
);
}
```
## API Reference
### Stepper
| prop | type | default | description |
| -------------- | ---------------------------- | ------------ | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Step elements to render inside the stepper |
| `currentStep` | `number` | - | Controlled active step index |
| `defaultStep` | `number` | `0` | Initial step index in uncontrolled mode |
| `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | Main axis of the step sequence |
| `animation` | `StepperRootAnimation` | - | Root animation configuration |
| `className` | `string` | - | Additional CSS classes for the root container |
| `onStepChange` | `(step: number) => void` | - | Callback when the active step index changes |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Data Attributes
| attribute | values | description |
| ------------------ | ---------------------------- | -------------------------- |
| `data-orientation` | `"horizontal" \| "vertical"` | Current layout orientation |
#### StepperRootAnimation
Animation configuration for the stepper root. Can be:
* `false` or `"disabled"`: Disable only root progress animation
* `"disable-all"`: Disable all animations including children
* `true` or `undefined`: Use default animations
* `object`: Custom animation configuration
| prop | type | default | description |
| ----------------------- | ---------------------------------------- | ---------------------------------------------------- | ------------------------------------------------ |
| `state` | `'disabled' \| 'disable-all' \| boolean` | - | Disable animations while customizing properties |
| `progress.timingConfig` | `WithTimingConfig` | `{ duration: 200, easing: Easing.out(Easing.ease) }` | Timing configuration for step progress animation |
### Stepper.Step
| prop | type | default | description |
| ------------------- | ----------------- | ------- | ------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Step content (Rail, Content, etc.) |
| `className` | `string` | - | Additional CSS classes for the step container |
| `...PressableProps` | `PressableProps` | - | All standard React Native Pressable props are supported |
#### Data Attributes
| attribute | values | description |
| ------------------ | ---------------------------- | -------------------------- |
| `data-orientation` | `"horizontal" \| "vertical"` | Current layout orientation |
### Stepper.Rail
| prop | type | default | description |
| -------------- | ----------------- | ------- | ----------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom rail content; when omitted, renders `Indicator` and `Separator` (except on step 0) |
| `className` | `string` | - | Additional CSS classes for the rail container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Stepper.Indicator
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------------------------------------------- |
| `children` | `React.ReactNode` | - | Custom indicator content; when omitted, renders `IndicatorCheck` and `IndicatorNumber` |
| `className` | `string` | - | Additional CSS classes for the indicator container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Data Attributes
| attribute | values | description |
| ------------- | -------------------------------------- | --------------------------------------- |
| `data-status` | `"inactive" \| "active" \| "complete"` | Step status relative to the active step |
### Stepper.IndicatorCheck
| prop | type | default | description |
| --------------- | ----------- | ------------------- | -------------------------------------------------- |
| `size` | `number` | `16` | Icon size in logical pixels |
| `strokeWidth` | `number` | - | Stroke width for the check path |
| `color` | `string` | `accent-foreground` | Stroke color of the check icon |
| `enterDuration` | `number` | `200` | Duration (ms) when check draws in |
| `exitDuration` | `number` | `0` | Duration (ms) when check draws out |
| `className` | `string` | - | Additional CSS classes for the wrapper |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Stepper.IndicatorNumber
| prop | type | default | description |
| -------------- | --------------------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `children` | `React.ReactNode \| ((index: number) => React.ReactNode)` | - | Custom label; static node or function receiving zero-based index |
| `className` | `string` | - | Additional CSS classes for the label text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
#### Data Attributes
| attribute | values | description |
| ------------- | -------------------------------------- | --------------------------------------- |
| `data-status` | `"inactive" \| "active" \| "complete"` | Step status relative to the active step |
### Stepper.Separator
| prop | type | default | description |
| -------------- | ----------------- | ------- | ------------------------------------------------------------------------------------ |
| `children` | `React.ReactNode` | - | Custom separator content; when omitted, renders `SeparatorTrack` and `SeparatorFill` |
| `force` | `boolean` | `false` | Render the separator on the last step |
| `progress` | `number` | - | Explicit fill amount (0–1); when omitted, derived from `currentStep` |
| `className` | `string` | - | Additional CSS classes for the separator container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Data Attributes
| attribute | values | description |
| ------------------ | -------------------------------------- | --------------------------------------- |
| `data-orientation` | `"horizontal" \| "vertical"` | Current layout orientation |
| `data-status` | `"inactive" \| "active" \| "complete"` | Step status relative to the active step |
### Stepper.SeparatorTrack
| prop | type | default | description |
| -------------- | ----------- | ------- | -------------------------------------------------- |
| `className` | `string` | - | Additional CSS classes for the track view |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Stepper.SeparatorFill
| prop | type | default | description |
| ----------------------- | --------------------- | ------- | --------------------------------------------------- |
| `animation` | `false \| 'disabled'` | - | Disable the fill scale animation for this separator |
| `isAnimatedStyleActive` | `boolean` | `true` | When `false`, animated scale styles are not applied |
| `className` | `string` | - | Additional CSS classes for the fill view |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
### Stepper.Content
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Content to render (title, description, etc.) |
| `className` | `string` | - | Additional CSS classes for the content container |
| `...ViewProps` | `ViewProps` | - | All standard React Native View props are supported |
#### Data Attributes
| attribute | values | description |
| ------------------ | ---------------------------- | -------------------------- |
| `data-orientation` | `"horizontal" \| "vertical"` | Current layout orientation |
### Stepper.Title
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Title text for the step |
| `className` | `string` | - | Additional CSS classes for the title text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
#### Data Attributes
| attribute | values | description |
| ------------------ | -------------------------------------- | --------------------------------------- |
| `data-orientation` | `"horizontal" \| "vertical"` | Current layout orientation |
| `data-status` | `"inactive" \| "active" \| "complete"` | Step status relative to the active step |
### Stepper.Description
| prop | type | default | description |
| -------------- | ----------------- | ------- | -------------------------------------------------- |
| `children` | `React.ReactNode` | - | Description text for the step |
| `className` | `string` | - | Additional CSS classes for the description text |
| `...TextProps` | `TextProps` | - | All standard React Native Text props are supported |
#### Data Attributes
| attribute | values | description |
| ------------------ | -------------------------------------- | --------------------------------------- |
| `data-orientation` | `"horizontal" \| "vertical"` | Current layout orientation |
| `data-status` | `"inactive" \| "active" \| "complete"` | Step status relative to the active step |
## Hooks
### useStepper
Hook to access the stepper root context. Must be used within a `Stepper` component.
```tsx
import { useStepper } from 'heroui-native-pro';
const { currentStep, onStepChange, orientation } = useStepper();
```
#### Returns
| property | type | description |
| -------------------- | ------------------------------------------------------------- | ------------------------------------- |
| `currentStep` | `number` | Currently active step index |
| `onStepChange` | `(step: number) => void` | Callback to update the active step |
| `orientation` | `'horizontal' \| 'vertical'` | Current layout orientation |
| `measurements` | `Record` | Per-step layout measurements |
| `setStepMeasurement` | `(index: number, partial: Partial) => void` | Update layout measurements for a step |
### useStepperStep
Hook to access the per-step context. Must be used within a `Stepper.Step` component.
```tsx
import { useStepperStep } from 'heroui-native-pro';
const { index, isLast, status } = useStepperStep();
```
#### Returns
| property | type | description |
| -------- | -------------------------------------- | -------------------------------------- |
| `index` | `number` | Zero-based index of the step |
| `isLast` | `boolean` | Whether this is the last step |
| `status` | `'inactive' \| 'active' \| 'complete'` | Current status relative to active step |
### useStepperAnimation
Hook to access the stepper animation context. Must be used within a `Stepper` component.
```tsx
import { useStepperAnimation } from 'heroui-native-pro';
const { progress } = useStepperAnimation();
```
#### Returns
| property | type | description |
| ---------- | --------------------- | ---------------------------------------------------- |
| `progress` | `SharedValue` | Animated progress aligned with the active step index |
# Chain Of Thought
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chain-of-thought
> A collapsible reasoning timeline for assistant thinking, progress, and agent traces.
## Usage
Use `ChainOfThought` to show assistant reasoning, progress, tool discovery, or agent traces without coupling to an AI SDK.
## Streaming
Set `isStreaming` to shimmer the trigger while reasoning is still in progress.
## Agent Trace
Complex traces can be composed from multiple collapsible reasoning blocks and nested steps.
## Agent Trace Streaming
Use the same trace structure with `isStreaming` when the agent is still working.
## Anatomy
```tsx
import {ChainOfThought} from "@heroui-pro/react";
Thought for 2sRead layout and globals.
```
## CSS Classes
* `.chain-of-thought` - Root disclosure wrapper
* `.chain-of-thought__trigger` - Collapsible trigger
* `.chain-of-thought__content` - Disclosure content panel
* `.chain-of-thought__steps` - Vertical step timeline
* `.chain-of-thought__step` - One step in the timeline
* `.chain-of-thought__step-label` - Optional step label
* `.chain-of-thought__step-content` - Step body content
## API Reference
### ChainOfThought
Extends HeroUI `Disclosure` props.
| Prop | Type | Default | Description |
| ----------------- | ----------- | ------- | ------------------------------------------------ |
| `children` | `ReactNode` | - | Trigger and content |
| `isStreaming` | `boolean` | `false` | Applies streaming shimmer styling to the trigger |
| `defaultExpanded` | `boolean` | - | Open by default for uncontrolled usage |
| `isExpanded` | `boolean` | - | Controlled expanded state |
### ChainOfThought.Trigger
Extends HeroUI `Button` props. Renders the disclosure trigger.
### ChainOfThought.Content
Extends `Disclosure.Content` props. Wraps the expanded content body.
### ChainOfThought.Steps
Renders the vertical timeline container. Also supports native `div` props.
### ChainOfThought.Step
Renders one timeline step. Also supports native `div` props.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ---------------------------------------------- |
| `label` | `ReactNode` | - | Optional label rendered above the step content |
| `children` | `ReactNode` | - | Step body content |
# Chat Attachment
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chat-attachment
> Attachment previews and composer file input helpers for AI chat surfaces.
## Usage
Use `ChatAttachment` for compact file previews in messages or prompt composers. Image and video
attachments render as a square thumbnail (with the file name revealed on hover), while other file
types render as a horizontal card with a format-specific icon, file name, and size.
## Composer
Pair `ChatAttachmentInput` with `PromptInput` to support file picker and drag-and-drop uploads.
When generating local image or video previews with `URL.createObjectURL`, revoke each `blob:` URL
when the attachment is removed, when the composer is cleared, and when the component unmounts.
## Grouped
Use `ChatAttachmentGroup` to arrange multiple attachments.
## Anatomy
```tsx
import {ChatAttachment, ChatAttachmentGroup, ChatAttachmentInput, PromptInput} from "@heroui-pro/react";
{/* composer content */}}
/>
```
## CSS Classes
The root exposes a `data-variant` attribute (`"media"` for image/video thumbnails, `"file"` for the
horizontal file card) that styles the two layouts.
* `.chat-attachment` - Attachment root (thumbnail tile or file card)
* `.chat-attachment__preview` - Preview media wrapper (thumbnail or icon tile)
* `.chat-attachment__preview-image` - Image preview
* `.chat-attachment__preview-video` - Video preview
* `.chat-attachment__icon` - Format-specific file-type icon
* `.chat-attachment__info` - File name and size wrapper (hover overlay for media)
* `.chat-attachment__name` - File name
* `.chat-attachment__size` - Formatted file size
* `.chat-attachment__remove` - Remove button
* `.chat-attachment-group` - Attachment group wrapper
## API Reference
### ChatAttachment
| Prop | Type | Default | Description |
| ----------- | ---------------------------------------------------------- | --------------- | --------------------------------------------------------------- |
| `mediaType` | `'audio' \| 'document' \| 'image' \| 'unknown' \| 'video'` | inferred | Attachment media type |
| `mimeType` | `string` | - | MIME type used to infer media type and icon |
| `name` | `string` | - | Attachment file name (also used to infer the icon by extension) |
| `size` | `number` | - | File size in bytes, formatted for display |
| `src` | `string` | - | Preview URL for image or video attachments |
| `children` | `ReactNode` | default content | Custom attachment content |
### ChatAttachment.Preview
Renders the image, video, or format-specific icon preview. Accepts `children` to replace the preview content.
### ChatAttachment.Icon
Renders the format-specific file-type icon inferred from `mimeType`/`name` (PDF, archive, spreadsheet, document, code, audio, video, image, or generic). Accepts `children` to override the icon.
### ChatAttachment.Info
Wrapper for the file name and size. Defaults to `ChatAttachment.Name` and `ChatAttachment.Size` when no `children` are provided. In the media variant it renders as a hover overlay.
### ChatAttachment.Name
Renders the file name. Defaults to the `name` prop.
### ChatAttachment.Size
Renders the formatted file size from the `size` prop (bytes). Renders nothing when no size is available.
### ChatAttachment.Remove
Extends HeroUI `CloseButton` props. Use it to remove an attachment from composer state.
### Helpers
* `inferChatAttachmentFileKind(mimeType?, name?)` - Resolves the file kind used to pick the icon.
* `formatChatAttachmentSize(bytes?)` - Formats a byte count as `B`/`KB`/`MB`/`GB`.
### ChatAttachmentInput
Provides file-picker and drag-and-drop behavior.
| Prop | Type | Default | Description |
| ----------------- | ------------------------- | ------- | ----------------------------------------- |
| `accept` | `string` | - | Native file input accept filter |
| `multiple` | `boolean` | `true` | Allow multiple files |
| `disabled` | `boolean` | `false` | Disable picker and drop behavior |
| `onFilesSelected` | `(files: File[]) => void` | - | Called when files are selected or dropped |
### ChatAttachmentInput.Trigger
Opens the hidden file input. Use `render` to wire it to another button.
### ChatAttachmentInput.Dropzone
Adds drag-and-drop file handling. Use `render` to attach drop behavior to `PromptInput.Shell`.
# Chat Conversation
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chat-conversation
> A stick-to-bottom conversation viewport for streaming chat messages.
## Usage
Use `ChatConversation` as the scrollable message viewport for assistant and user messages.
## Full Chat
Combine `ChatConversation`, `ChatMessage`, `ChainOfThought`, and `PromptInput` for a complete chat surface.
## Scroll Button
Add `ChatConversation.ScrollButton` only when your product needs an explicit jump-to-bottom control.
## Anatomy
```tsx
import {ChatConversation} from "@heroui-pro/react";
{messages}
```
## CSS Classes
* `.chat-conversation` - Scrollable root viewport
* `.chat-conversation__content` - Message column
* `.chat-conversation__scroll-button` - Jump-to-bottom button
* `.chat-conversation__scroll-button-container` - Jump-to-bottom button positioner
* `.chat-conversation__scroll-anchor` - Bottom scroll anchor
## API Reference
### ChatConversation
Root scroll viewport. Supports native `div` props.
### ChatConversation.Content
Centers and stacks conversation content. Supports native `div` props.
### ChatConversation.ScrollButton
Optional button that appears when the viewport is away from the bottom. Extends HeroUI `Button` props.
### ChatConversation.ScrollAnchor
Bottom anchor used by the stick-to-bottom behavior. Supports native `div` props.
# Chat List View
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chat-list-view
> Thread list rows for chat sidebars and conversation pickers.
## Usage
Use `ChatListView` to render recent conversations or saved threads.
## Compact
Use the compact variant for dense sidebars.
## Anatomy
```tsx
import {ChatListView} from "@heroui-pro/react";
Project planningLast assistant reply preview
```
## CSS Classes
* `.chat-list-view` - Root list
* `.chat-list-view__item` - Thread row
* `.chat-list-view__icon` - Leading icon/avatar
* `.chat-list-view__item-content` - Row text content
* `.chat-list-view__title` - Thread title
* `.chat-list-view__preview` - Thread preview
* `.chat-list-view__meta` - Optional metadata
## API Reference
### ChatListView
Root list container. Supports native `div` props.
### ChatListView\.Item
Thread row. Supports native button/link composition depending on usage.
### ChatListView\.Icon
Leading icon slot.
### ChatListView\.ItemContent
Text content wrapper.
### ChatListView\.Title
Thread title slot.
### ChatListView\.Preview
Thread description or last-message preview slot.
### ChatListView\.Meta
Optional metadata slot, such as date or unread counts.
# Chat Loader
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chat-loader
> Loading skeletons and typing placeholders for assistant responses.
## Usage
Use `ChatLoader` while an assistant response or thread is loading.
## Anatomy
```tsx
import {ChatLoader} from "@heroui-pro/react";
```
## CSS Classes
* `.chat-loader` - Root loading layout
* `.chat-loader__avatar` - Avatar placeholder
* `.chat-loader__content` - Loading line group
* `.chat-loader__line` - Individual loading line
## API Reference
### ChatLoader.Dots
Animated dot loader. Supports native `div` props.
### ChatLoader.Pulse
Pulse loader. Supports native `div` props.
### ChatLoader.Spinner
Spinner loader. Supports native `div` props.
### ChatLoader.Skeleton
Chat-message-shaped loading skeleton. Supports native `div` props.
### ChatLoader.SkeletonAvatar, SkeletonBlock, SkeletonLine
Composable skeleton primitives used by `ChatLoader.Skeleton`.
# Chat Message Actions
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chat-message-actions
> Inline action buttons for assistant and user messages.
## Usage
Use `ChatMessageActions` for copy, retry, rating, share, or custom message actions.
## Minimal
Use a smaller action set when only one or two actions are needed.
## Custom Icons
Actions are composable, so you can bring your own icon set.
## Anatomy
```tsx
import {ChatMessageActions} from "@heroui-pro/react";
......
```
## CSS Classes
* `.chat-message-actions` - Root action row
* `.chat-message-actions__action` - Individual icon button
## API Reference
### ChatMessageActions
Root action group. Supports native `div` props.
### ChatMessageActions.Action
Individual action button. Extends HeroUI `Button` props.
# Chat Message
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chat-message
> Composable user and assistant message layouts for AI chat.
## Usage
Use `ChatMessage` to compose assistant and user turns with avatars, bubbles, media, markdown, actions, and attachments.
## With Markdown
Render assistant responses with rich markdown content.
## Loading
Use the loading demo for pending assistant turns.
## Anatomy
```tsx
import {ChatMessage} from "@heroui-pro/react";
import {Markdown} from "@heroui-pro/react/markdown";
{response}Hello
```
## CSS Classes
* `.chat-message--assistant` - Assistant message row
* `.chat-message--user` - User message wrapper
* `.chat-message__avatar` - Avatar slot
* `.chat-message__body` - Assistant body column
* `.chat-message__bubble` - User bubble
* `.chat-message__content` - Message content
* `.chat-message__actions` - Action row
## API Reference
### ChatMessage.Assistant
Assistant message root. Supports native `div` props.
### ChatMessage.User
User message root. Supports native `div` props.
### ChatMessage.Avatar
Avatar slot for assistant messages. Extends HeroUI `Avatar` props.
### ChatMessage.Body
Assistant content column.
### ChatMessage.Bubble
User message bubble.
### ChatMessage.Content
Message text/content slot.
### ChatMessage.Actions
Container for message actions.
# Chat Source
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chat-source
> Citation chips and grouped source lists for AI responses.
## Usage
Use `ChatSource` to cite a URL or uploaded document inline with an assistant answer.
## Document
Set `sourceType="document"` for uploaded or local files.
## Grouped
Use `ChatSources` to group multiple citations behind a collapsible trigger.
## Stacked Favicons
For a compact source button, render stacked favicon avatars in the `ChatSources.Trigger`.
## Composable
Use compound parts when you need custom trigger content.
## Anatomy
```tsx
import {ChatSource, ChatSources} from "@heroui-pro/react";
3 sources
```
## CSS Classes
* `.chat-source` - Source root
* `.chat-source__trigger` - Trigger wrapper
* `.chat-source__trigger-link` - Link or document pill
* `.chat-source__icon` - Favicon image or custom icon
* `.chat-source__icon-fallback` - Initial fallback for URL sources
* `.chat-source__document-icon` - Document source icon
* `.chat-source__preview` - Hover preview popover
* `.chat-sources` - Grouped source disclosure
* `.chat-sources__trigger` - Group trigger
* `.chat-sources__list` - Expanded source list
## API Reference
### ChatSource
| Prop | Type | Default | Description |
| --------------- | --------------------- | ----------------------- | -------------------------------------------------------------------------- |
| `href` | `string` | - | URL source link |
| `title` | `string` | domain | Display title |
| `description` | `string` | - | Enables and populates the hover preview |
| `enablePreview` | `boolean` | auto | Force-enable custom preview content or disable the automatic hover preview |
| `faviconUrl` | `string` | - | Favicon image shown in trigger and preview |
| `sourceType` | `'url' \| 'document'` | inferred | Source type |
| `children` | `ReactNode` | default trigger/preview | Custom source composition |
### ChatSource.Trigger
Renders the source pill trigger. Extends native anchor props for URL sources.
### ChatSource.Icon
Renders a custom icon or favicon.
| Prop | Type | Default | Description |
| ------------ | ----------- | ---------------- | ------------------- |
| `faviconUrl` | `string` | - | Favicon image URL |
| `children` | `ReactNode` | fallback initial | Custom icon element |
### ChatSource.Title
Renders the source title.
### ChatSource.Preview
Renders the hover preview content for URL sources with title or description. If you provide a
custom preview without root `title` or `description`, set `enablePreview` on `ChatSource` so the
required hover-card wrapper is mounted.
### ChatSources
Grouped source disclosure. Extends HeroUI `Disclosure` props.
### ChatSources.Trigger
Renders the grouped source trigger.
### ChatSources.Content
Renders the expanded grouped source content.
### ChatSources.List
Renders the flex-wrapped source list.
# Chat Tool
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chat-tool
> Collapsible tool-call cards for inputs, outputs, errors, approvals, and grouped tool activity.
## Usage
Use `ChatTool` to show tool calls emitted by an agent or assistant.
## Streaming
Use streaming state while tool input is still being generated.
## Error
Use error state for failed tool calls.
## Approval
Use approval state when a tool requires user confirmation.
## Grouped
Use `ChatToolGroup` to group consecutive tool calls.
## Composable
Tool cards expose slots for custom trigger and content layouts.
## Anatomy
`ChatTool` is a subpath-only import (`@heroui-pro/react/chat-tool`) because it renders a `CodeBlock` and depends on the optional `shiki` peer. It is not exported from the package root, so SSR apps that don't use it never need `shiki` installed.
```tsx
import {ChatTool, ChatToolGroup} from "@heroui-pro/react/chat-tool";
Used tool: searchDocs
```
## CSS Classes
* `.chat-tool` - Tool card root
* `.chat-tool__trigger` - Collapsible trigger
* `.chat-tool__content` - Disclosure content panel
* `.chat-tool__args` - Tool input content
* `.chat-tool__result` - Tool output content
* `.chat-tool__error` - Tool error content
* `.chat-tool-group` - Grouped tool root
## API Reference
### ChatTool
| Prop | Type | Default | Description |
| ----------------- | ----------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------- |
| `toolName` | `string` | - | Tool display name |
| `state` | `'input-streaming' \| 'input-available' \| 'output-available' \| 'output-error' \| 'requires-action'` | - | Tool state |
| `triggerPrefix` | `ReactNode` | - | Optional label rendered before `toolName` in preset mode |
| `input` | `unknown` | - | Tool input rendered as JSON in preset mode |
| `output` | `unknown` | - | Tool output rendered as JSON in preset mode |
| `argsText` | `string` | - | Preformatted tool input text, useful while streaming partial JSON |
| `errorText` | `string` | - | Error details rendered for `output-error` state |
| `onApprove` | `() => void` | - | Called by the preset approval action in `requires-action` state |
| `onReject` | `() => void` | - | Called by the preset rejection action in `requires-action` state |
| `defaultExpanded` | `boolean` | - | Open by default |
| `isExpanded` | `boolean` | - | Controlled expanded state |
### ChatTool.Trigger
Renders the collapsible tool trigger.
### ChatTool.Content
Renders the expanded tool body.
### ChatTool.Args, Result, Error, Approval
Semantic slots for tool input, output, error details, and approval controls.
### ChatToolGroup
Groups multiple `ChatTool` items. Extends HeroUI `Disclosure` props.
# Code Block
**Category**: react
**URL**: https://heroui.pro/docs/react/components/code-block
> Syntax-highlighted code blocks with language labels and copy actions for AI markdown.
## Usage
Use `CodeBlock` for fenced code output, tool snippets, and AI-generated examples.
## Anatomy
`CodeBlock` is a subpath-only import (`@heroui-pro/react/code-block`) because it depends on the optional `shiki` peer. It is not exported from the package root, so SSR apps that don't use it never need `shiki` installed.
```tsx
import {CodeBlock} from "@heroui-pro/react/code-block";
typescript
```
## CSS Classes
* `.code-block` - Root code block surface
* `.code-block__header` - Header row
* `.code-block__code` - Code scroll region
* `.code-block__copy-button` - Copy/check icon button
## API Reference
### CodeBlock
Root container. Also supports native `div` props.
### CodeBlock.Header
Header slot for language labels and actions. Also supports native `div` props.
### CodeBlock.Code
| Prop | Type | Default | Description |
| ---------- | -------- | ---------------- | ----------------- |
| `code` | `string` | - | Code to render |
| `language` | `string` | `'plaintext'` | Shiki language id |
| `theme` | `string` | `'github-light'` | Shiki theme |
Also supports native `div` props.
### CodeBlock.CopyButton
| Prop | Type | Default | Description |
| ------------ | -------- | ------------- | ------------------------ |
| `code` | `string` | - | Code copied to clipboard |
| `aria-label` | `string` | `'Copy code'` | Accessible label |
| `className` | `string` | - | Additional class |
# Markdown
**Category**: react
**URL**: https://heroui.pro/docs/react/components/markdown
> Render AI responses with rich markdown, memoized streaming blocks, and optional Streamdown rendering.
## Usage
Use `Markdown` for AI responses that need headings, lists, inline code, and fenced code blocks with HeroUI Pro styling.
## Streaming
The built-in Markdown component splits content into memoized blocks so token updates only re-render the blocks that changed.
## With Streamdown
Use Streamdown when you want incomplete markdown repair, streaming animation, and a caret while the assistant response is still being generated.
```tsx
import {Streamdown} from "streamdown";
import "streamdown/styles.css";
{response}
```
## Anatomy
`Markdown` and `StreamMarkdown` are subpath-only imports (`@heroui-pro/react/markdown`) because they depend on the optional `streamdown`, `react-markdown`, `marked`, and `remark-*` peers. They are not exported from the package root, so SSR apps that don't use them never need those peers installed.
```tsx
import {Markdown} from "@heroui-pro/react/markdown";
{response}
```
## CSS Classes
### Base Classes
* `.markdown` - Root markdown content wrapper
* `.markdown__block` - Memoized block wrapper for each parsed markdown block
### Element Classes
* `.markdown__inline-code` - Inline code styling
## API Reference
### Markdown
The root component. Renders markdown content with HeroUI Pro typography and code block styling.
| Prop | Type | Default | Description |
| ------------ | --------------------- | --------- | ------------------------------------------- |
| `children` | `string` | - | Markdown content to render |
| `components` | `Partial` | - | Custom `react-markdown` component overrides |
| `id` | `string` | generated | Stable id seed used for memoized block keys |
| `className` | `string` | - | Additional CSS class |
Also supports all native `div` HTML attributes.
# Prompt Input
**Category**: react
**URL**: https://heroui.pro/docs/react/components/prompt-input
> A composable AI prompt composer with attachments, toolbar actions, send states, queued prompts, and drag-and-drop support.
## Usage
Use `PromptInput` as the message composer for chat and agent interfaces.
## Secondary
Use the secondary variant when the composer sits on a default surface.
## Inline
Use the inline layout when attachment, input, and send controls should stay aligned in one row while the textarea autosizes as users type or add line breaks.
## Compact
Use the compact layout when the composer should start as a single-row pill and switch to the stacked composer once text wraps or attachments are added.
## Review Composer
Compose `PromptInput` with workflow controls to build compact agent surfaces such as review follow-ups and commit actions.
## With Suggestions
Compose `PromptInput` with `PromptSuggestion` to create a landing-style composer with quick-start prompts.
## Run State
Prompt input supports `submitted`, `streaming`, `ready`, and `error` states for send/stop behavior.
## Queue
Use `PromptInput.Queue` to show queued follow-up prompts in a compact card above the composer shell. Place it as a sibling of `PromptInput.Shell` inside `PromptInput`. Keep queue rows text-only for a clean surface — store attachments in your queue model and restore them to `PromptInput.Attachments` when the user edits a queued prompt.
## With Attachments
Pair `PromptInput` with `ChatAttachmentInput.Dropzone` to accept dropped files. While a file is dragged over the shell, the composer shows a dotted accent border.
## Anatomy
```tsx
import {PromptInput} from "@heroui-pro/react";
{queuedPrompts.map((prompt) => (
Queued prompt text
))}
...AI can make mistakes. Check important info.;
```
## CSS Classes
* `.prompt-input` - Root state wrapper
* `.prompt-input__shell` - Composer surface
* `.prompt-input__content` - Textarea and attachment content area
* `.prompt-input__attachments` - Attachment preview row
* `.prompt-input__textarea` - Text area
* `.prompt-input__toolbar` - Absolute toolbar row
* `.prompt-input__toolbar-start` - Leading toolbar actions
* `.prompt-input__toolbar-end` - Trailing toolbar actions
* `.prompt-input__footer` - Disclaimer/footer text
* `.prompt-input__send` - Send/stop button
* `.prompt-input__queue` - Queued prompts container
* `.prompt-input__queue-list` - Scrollable queue list
* `.prompt-input__queue-item` - Single queued prompt row
* `.prompt-input__queue-item-handle` - Drag handle (presentational)
* `.prompt-input__queue-item-icon` - Leading queue item icon
* `.prompt-input__queue-item-body` - Main queue row content stack
* `.prompt-input__queue-item-content` - Clamped prompt text (2 lines)
* `.prompt-input__queue-item-description` - Secondary queue item text
* `.prompt-input__queue-item-actions` - Trailing row actions
* `.prompt-input__queue-item-attachments` - Attachment preview row inside a queue item
* `.prompt-input__queue-item-attachments-overflow` - Hidden attachment count label
## API Reference
### PromptInput
| Prop | Type | Default | Description |
| ---------------- | -------------------------------------------------- | ----------- | --------------------------------------------- |
| `value` | `string` | internal | Controlled input value |
| `onValueChange` | `(value: string) => void` | - | Called when the text changes |
| `onSubmit` | `() => void` | - | Called when submit is requested |
| `onStop` | `() => void` | - | Called by the send button in stoppable states |
| `status` | `'ready' \| 'submitted' \| 'streaming' \| 'error'` | `'ready'` | Composer run state |
| `isDisabled` | `boolean` | `false` | Disable composer controls |
| `lockInputOnRun` | `boolean` | `true` | Disable textarea while submitted/streaming |
| `maxHeight` | `number \| string` | `240` | Autosize textarea max height |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Composer size |
| `variant` | `'primary' \| 'secondary'` | `'primary'` | Surface styling variant |
| `layout` | `'stacked' \| 'compact' \| 'inline'` | `'stacked'` | Composer layout and toolbar placement |
### PromptInput.TextArea
Extends HeroUI `TextArea` props.
| Prop | Type | Default | Description |
| ----------------- | --------- | ------- | ----------------------------------- |
| `disableAutosize` | `boolean` | `false` | Disable automatic height adjustment |
### PromptInput.Action
Extends HeroUI `Button` props.
| Prop | Type | Default | Description |
| --------- | ----------- | ------- | ------------------------ |
| `tooltip` | `ReactNode` | - | Optional tooltip content |
### PromptInput.Send
Extends HeroUI `Button` props. Uses the current prompt status to render send, loading, stop, or error icons.
### PromptInput.Shell, Content, Attachments, Toolbar, ToolbarStart, ToolbarEnd, Footer
Layout slots for composing the prompt input. Each slot supports the corresponding native element props.
### PromptInput.Queue
| Prop | Type | Default | Description |
| ------------------- | --------------------- | --------- | ---------------------------------- |
| `actionsVisibility` | `'always' \| 'hover'` | `'hover'` | When queue row actions are visible |
### PromptInput.Queue.List
| Prop | Type | Default | Description |
| ----------- | ----------------------- | ------- | --------------------------------------------------------------------------- |
| `values` | `T[]` | - | Controlled queue values. Pass with `onReorder` to enable Motion reordering. |
| `onReorder` | `(values: T[]) => void` | - | Called when items are reordered via drag and drop. |
| `axis` | `'x' \| 'y'` | `'y'` | Reorder axis when drag and drop is enabled. |
### PromptInput.Queue.Item
| Prop | Type | Default | Description |
| ------- | ---- | ------- | -------------------------------------------------------------- |
| `value` | `T` | - | Item value from `values`. Required when reordering is enabled. |
### PromptInput.Queue.Item.Body, Handle, Icon, Content, Description, Attachments, Actions
Presentational slots for queue row structure. Wrap `Icon` and `Content` in `Body` so the icon sits beside the clamped text and stays vertically centered. `Content` clamps text to two lines with an ellipsis.
### PromptInput.Queue.Item.AttachmentsOverflow
Optional overflow label when you choose to render attachment previews inside a queue row.
| Prop | Type | Default | Description |
| ------------- | -------- | --------- | -------------------------------------------------------- |
| `hiddenCount` | `number` | - | Number of attachments hidden beyond the visible previews |
| `noun` | `string` | `'files'` | Noun used in the overflow label |
### PromptInput.Queue.Item.Remove, More, Action
Action buttons for queue rows. `Remove` and `More` include default labels and icons. Extend HeroUI `Button` props.
# Prompt Suggestion
**Category**: react
**URL**: https://heroui.pro/docs/react/components/prompt-suggestion
> Suggested prompts and starter actions for AI chat empty states.
## Usage
Use `PromptSuggestion` to show suggested prompts near a composer or empty chat state.
## Cards
Use card layout for richer prompt starters.
## Anatomy
```tsx
import {PromptSuggestion} from "@heroui-pro/react";
What can I help with?Start from a suggested prompt.Summarize this documentCreate a concise summary.
```
## CSS Classes
* `.prompt-suggestion` - Root suggestion item
* `.prompt-suggestion__header` - Header wrapper
* `.prompt-suggestion__title` - Header title
* `.prompt-suggestion__description` - Header description
* `.prompt-suggestion__items` - Suggestion item grid/list
* `.prompt-suggestion__item` - Individual suggestion item
## API Reference
### PromptSuggestion
Root suggestion group. Supports native `div` props.
### PromptSuggestion.Header, Title, Description
Header slots for title and description.
### PromptSuggestion.Items
Container for suggestion items.
### PromptSuggestion.Item
Individual suggestion item. Extends HeroUI `Button` props.
### PromptSuggestion.ItemTitle, ItemDescription, ItemMeta, ItemTags, ItemFooter
Slots for composing rich suggestion items.
# Text Shimmer
**Category**: react
**URL**: https://heroui.pro/docs/react/components/text-shimmer
> Animated shimmer text for streaming, thinking, and loading labels.
## Usage
Use `TextShimmer` for short labels that indicate active generation or background work.
## Color
The shimmer uses `currentColor`, so you can pass text color utilities such as `text-muted` and keep the animation visible.
```tsx
Thinking...
```
## Anatomy
```tsx
import {TextShimmer} from "@heroui-pro/react";
Thinking...
```
## CSS Classes
* `.text-shimmer` - Root animated text element
## API Reference
### TextShimmer
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ----------------------------------------------------------- |
| `children` | `ReactNode` | - | Text content |
| `className` | `string` | - | Additional classes, including color and text size utilities |
Also supports render props from HeroUI `dom.span`.
# Area Chart
**Category**: react
**URL**: https://heroui.pro/docs/react/components/area-chart
> An area chart for visualizing trends with gradient fills, stacked series, and sparkline variants.
## Usage
## Anatomy
Import the AreaChart component and access all parts using dot notation.
`AreaChart` is a subpath-only import (`@heroui-pro/react/area-chart`) because it depends on the optional `recharts` peer. It is not exported from the package root, so SSR apps that don't use it never need `recharts` installed.
```tsx
import {AreaChart} from "@heroui-pro/react/area-chart";
```
## Custom Tooltip
## KPI With Area Chart
## Multi Area
## Sparkline
## Stacked
## CSS Classes
### Element Classes
* `.area-chart` — Root container wrapping `ResponsiveContainer` and the Recharts chart.
### Recharts Theming
The following CSS rules target Recharts internal class names to apply HeroUI design tokens automatically:
* `.area-chart .recharts-cartesian-axis-tick-value` — Axis tick labels. 10px muted text.
* `.area-chart .recharts-cartesian-axis-line` — Axis lines. Hidden by default.
* `.area-chart .recharts-cartesian-axis-tick-line` — Tick lines. Hidden by default.
* `.area-chart .recharts-cartesian-grid line` — Cartesian grid lines. Muted stroke at 0.15 opacity.
* `.area-chart .recharts-tooltip-cursor` — Tooltip cursor. Dashed vertical line on hover.
* `.area-chart .recharts-active-dot circle` — Active dot. Outlined with surface color for contrast.
## API Reference
### AreaChart
The root wrapper. Renders a `ResponsiveContainer` + Recharts `AreaChart` with HeroUI CSS theming applied automatically.
| Prop | Type | Default | Description |
| ---------- | ------------------------------------------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------- |
| `data` | `Record[]` | — | Chart data — array of objects with numeric/string fields for each series. |
| `height` | `number` | `300` | Chart height in pixels. |
| `width` | ``number \| `${number}%` `` | `"100%"` | Chart width in pixels or percentage string. |
| `margin` | `{ top?: number; right?: number; bottom?: number; left?: number }` | `{ top: 8, right: 8, bottom: 0, left: 0 }` | Recharts margin around the chart area. |
| `children` | `ReactNode` | — | Recharts child components (`AreaChart.Area`, `AreaChart.XAxis`, etc.). |
Also supports all native `div` HTML attributes.
### AreaChart.Area
Re-exported Recharts `Area` component. Follows the [Recharts Area API](https://recharts.github.io/en-US/api/Area/).
### AreaChart.XAxis
Re-exported Recharts `XAxis` component. Follows the [Recharts XAxis API](https://recharts.github.io/en-US/api/XAxis/).
### AreaChart.YAxis
Re-exported Recharts `YAxis` component. Follows the [Recharts YAxis API](https://recharts.github.io/en-US/api/YAxis/).
### AreaChart.Grid
Re-exported Recharts `CartesianGrid` component. Follows the [Recharts CartesianGrid API](https://recharts.github.io/en-US/api/CartesianGrid/).
### AreaChart.Tooltip
Re-exported Recharts `Tooltip` component. Follows the [Recharts Tooltip API](https://recharts.github.io/en-US/api/Tooltip/). Use with `AreaChart.TooltipContent` for styled tooltips.
### AreaChart.TooltipContent
Pre-built tooltip renderer for Recharts. Pass as the `content` prop of `AreaChart.Tooltip`. See [ChartTooltip](https://heroui.pro/docs/react/components/chart-tooltip) for full props.
# Bar Chart
**Category**: react
**URL**: https://heroui.pro/docs/react/components/bar-chart
> A bar chart for comparing categorical data with grouped, stacked, and horizontal layout support.
## Usage
## Anatomy
Import the BarChart component and access all parts using dot notation.
`BarChart` is a subpath-only import (`@heroui-pro/react/bar-chart`) because it depends on the optional `recharts` peer. It is not exported from the package root, so SSR apps that don't use it never need `recharts` installed.
```tsx
import {BarChart} from "@heroui-pro/react/bar-chart";
```
## Comparison
## Custom Tooltip
## Grouped
## Horizontal
## Horizontal Stacked
## KPIWith Bar Chart
## Stacked
## CSS Classes
### Element Classes
* `.bar-chart` — Root container wrapping `ResponsiveContainer` and the Recharts chart.
### Recharts Theming
The following CSS rules target Recharts internal class names to apply HeroUI design tokens automatically:
* `.bar-chart .recharts-cartesian-axis-tick-value` — Axis tick labels. 10px muted text.
* `.bar-chart .recharts-cartesian-axis-line` — Axis lines. Hidden by default.
* `.bar-chart .recharts-cartesian-axis-tick-line` — Tick lines. Hidden by default.
* `.bar-chart .recharts-cartesian-grid line` — Cartesian grid lines. Muted stroke at 0.15 opacity.
* `.bar-chart .recharts-tooltip-cursor` — Tooltip cursor. Subtle filled rectangle behind the hovered bar.
## API Reference
### BarChart
The root wrapper. Renders a `ResponsiveContainer` + Recharts `BarChart` with HeroUI CSS theming applied automatically.
| Prop | Type | Default | Description |
| ---------- | ------------------------------------------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------- |
| `data` | `Record[]` | — | Chart data — array of objects with numeric/string fields for each series. |
| `height` | `number` | `300` | Chart height in pixels. |
| `width` | ``number \| `${number}%` `` | `"100%"` | Chart width in pixels or percentage string. |
| `layout` | `"horizontal" \| "vertical"` | `"horizontal"` | Bar layout direction. Use `"vertical"` for horizontal bar charts. |
| `margin` | `{ top?: number; right?: number; bottom?: number; left?: number }` | `{ top: 8, right: 8, bottom: 0, left: 0 }` | Recharts margin around the chart area. |
| `children` | `ReactNode` | — | Recharts child components (`BarChart.Bar`, `BarChart.XAxis`, etc.). |
Also supports all native `div` HTML attributes.
### BarChart.Bar
Re-exported Recharts `Bar` component. Follows the [Recharts Bar API](https://recharts.github.io/en-US/api/Bar/).
### BarChart.XAxis
Re-exported Recharts `XAxis` component. Follows the [Recharts XAxis API](https://recharts.github.io/en-US/api/XAxis/).
### BarChart.YAxis
Re-exported Recharts `YAxis` component. Follows the [Recharts YAxis API](https://recharts.github.io/en-US/api/YAxis/).
### BarChart.Grid
Re-exported Recharts `CartesianGrid` component. Follows the [Recharts CartesianGrid API](https://recharts.github.io/en-US/api/CartesianGrid/).
### BarChart.Tooltip
Re-exported Recharts `Tooltip` component. Follows the [Recharts Tooltip API](https://recharts.github.io/en-US/api/Tooltip/). Use with `BarChart.TooltipContent` for styled tooltips.
### BarChart.TooltipContent
Pre-built tooltip renderer for Recharts. Pass as the `content` prop of `BarChart.Tooltip`. See [ChartTooltip](https://heroui.pro/docs/react/components/chart-tooltip) for full props.
# Chart Tooltip
**Category**: react
**URL**: https://heroui.pro/docs/react/components/chart-tooltip
> A composable React tooltip for chart data points with customizable indicators, labels, and value formatters.
## Usage
## Anatomy
Import the ChartTooltip component and access all parts using dot notation.
```tsx
import {ChartTooltip} from "@heroui-pro/react";
```
## Auto Content
## Chart Colors
## Custom Formatters
## Inactive
## Line Indicator
## No Header
## CSS Classes
### Element Classes
* `.chart-tooltip` — Root tooltip card container. Rounded border with surface background and overlay shadow.
* `.chart-tooltip__header` — Optional title row (e.g., the X-axis label). Muted 12px medium text.
* `.chart-tooltip__item` — A single series entry row. Flex layout with gap.
* `.chart-tooltip__indicator` — Color marker next to the series name.
* `.chart-tooltip__indicator--dot` — Dot-shaped indicator. Small circle (8px).
* `.chart-tooltip__indicator--line` — Line-shaped indicator. Tall narrow pill (12px × 4px).
* `.chart-tooltip__label` — Series name text. Muted 12px, fills available space.
* `.chart-tooltip__value` — Series data value. Semibold 12px foreground text.
## API Reference
### ChartTooltip
The root tooltip container. Controls visibility and indicator style via context.
| Prop | Type | Default | Description |
| ----------- | ----------------- | ------- | ----------------------------------------------------------------------------------------------- |
| `active` | `boolean` | `true` | Controls visibility. When `false`, the tooltip is not rendered. |
| `indicator` | `"dot" \| "line"` | `"dot"` | Shape of the color indicator next to each series name. |
| `children` | `ReactNode` | — | Tooltip content — typically `Header`, `Item`, `Indicator`, `Label`, and `Value` sub-components. |
Also supports all native `div` HTML attributes.
### ChartTooltip.Content
Auto-renders a tooltip from Recharts payload data. Pass as the `content` prop of a Recharts ``:
```tsx
} />
```
| Prop | Type | Default | Description |
| ---------------- | ---------------------------------------- | ------- | ------------------------------------------------------------------- |
| `active` | `boolean` | — | Provided by Recharts — whether the tooltip is active. |
| `label` | `number \| string` | — | Provided by Recharts — the X-axis label for the hovered data point. |
| `payload` | `RechartsPayloadEntry[]` | — | Provided by Recharts — array of series data for the hovered point. |
| `hideHeader` | `boolean` | `false` | Hide the header row. |
| `indicator` | `"dot" \| "line"` | `"dot"` | Shape of the color indicator. |
| `labelFormatter` | `(label: number \| string) => ReactNode` | — | Custom formatter for the header label. |
| `valueFormatter` | `(value: number \| string) => ReactNode` | — | Custom formatter for series values. |
### ChartTooltip.Header
Optional title row rendered above the series items.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------------------------------- |
| `children` | `ReactNode` | — | Header content (typically the X-axis label text). |
Also supports all native `div` HTML attributes.
### ChartTooltip.Item
A single series entry row containing an indicator, label, and value.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------------------------------------------------------- |
| `children` | `ReactNode` | — | Row content — typically `Indicator`, `Label`, and `Value` sub-components. |
Also supports all native `div` HTML attributes.
### ChartTooltip.Indicator
Color marker rendered next to the series name. Shape is controlled by the root `indicator` variant.
| Prop | Type | Default | Description |
| ------- | -------- | ------- | --------------------------------------------- |
| `color` | `string` | — | CSS color value for the indicator background. |
Also supports all native `span` HTML attributes.
### ChartTooltip.Label
Series name text displayed between the indicator and value.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------------------------------------- |
| `children` | `ReactNode` | — | Label content (typically the series name or `dataKey`). |
Also supports all native `span` HTML attributes.
### ChartTooltip.Value
Series data value displayed at the end of each item row.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ------------------------------------------------------ |
| `children` | `ReactNode` | — | Value content (typically the formatted numeric value). |
Also supports all native `span` HTML attributes.
# Composed Chart
**Category**: react
**URL**: https://heroui.pro/docs/react/components/composed-chart
> A React composed chart that combines bar, line, and area series for multi-metric web dashboards.
## Usage
## Anatomy
Import the ComposedChart component and access all parts using dot notation.
`ComposedChart` is a subpath-only import (`@heroui-pro/react/composed-chart`) because it depends on the optional `recharts` peer. It is not exported from the package root, so SSR apps that don't use it never need `recharts` installed.
```tsx
import {ComposedChart} from "@heroui-pro/react/composed-chart";
```
## Stacked Bar With Line
## Area With Line
## Bar With Area
## Multi Type
## CSS Classes
### Element Classes
* `.composed-chart` — Root container wrapping `ResponsiveContainer` and the Recharts chart.
### Recharts Theming
The following CSS rules target Recharts internal class names to apply HeroUI design tokens automatically:
* `.composed-chart .recharts-cartesian-axis-tick-value` — Axis tick labels. 10px muted text.
* `.composed-chart .recharts-cartesian-axis-line` — Axis lines. Hidden by default.
* `.composed-chart .recharts-cartesian-axis-tick-line` — Tick lines. Hidden by default.
* `.composed-chart .recharts-cartesian-grid line` — Cartesian grid lines. Muted stroke at 0.15 opacity.
* `.composed-chart .recharts-tooltip-cursor` — Tooltip cursor. Dashed vertical line on hover.
* `.composed-chart .recharts-active-dot circle` — Active dot. Outlined with surface color for contrast.
## API Reference
### ComposedChart
The root wrapper. Renders a `ResponsiveContainer` + Recharts `ComposedChart` with HeroUI CSS theming applied automatically.
| Prop | Type | Default | Description |
| ---------- | ------------------------------------------------------------------ | ------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| `data` | `Record[]` | — | Chart data — array of objects with numeric/string fields for each series. |
| `height` | `number` | `300` | Chart height in pixels. |
| `width` | ``number \| `${number}%` `` | `"100%"` | Chart width in pixels or percentage string. |
| `margin` | `{ top?: number; right?: number; bottom?: number; left?: number }` | `{ top: 8, right: 8, bottom: 0, left: 0 }` | Recharts margin around the chart area. |
| `children` | `ReactNode` | — | Recharts child components (`ComposedChart.Bar`, `ComposedChart.Line`, `ComposedChart.Area`, etc.). |
Also supports all native `div` HTML attributes.
### ComposedChart.Bar
Re-exported Recharts `Bar` component. Follows the [Recharts Bar API](https://recharts.github.io/en-US/api/Bar/).
### ComposedChart.Line
Re-exported Recharts `Line` component. Follows the [Recharts Line API](https://recharts.github.io/en-US/api/Line/).
### ComposedChart.Area
Re-exported Recharts `Area` component. Follows the [Recharts Area API](https://recharts.github.io/en-US/api/Area/).
### ComposedChart.XAxis
Re-exported Recharts `XAxis` component. Follows the [Recharts XAxis API](https://recharts.github.io/en-US/api/XAxis/).
### ComposedChart.YAxis
Re-exported Recharts `YAxis` component. Follows the [Recharts YAxis API](https://recharts.github.io/en-US/api/YAxis/).
### ComposedChart.Grid
Re-exported Recharts `CartesianGrid` component. Follows the [Recharts CartesianGrid API](https://recharts.github.io/en-US/api/CartesianGrid/).
### ComposedChart.Tooltip
Re-exported Recharts `Tooltip` component. Follows the [Recharts Tooltip API](https://recharts.github.io/en-US/api/Tooltip/). Use with `ComposedChart.TooltipContent` for styled tooltips.
### ComposedChart.TooltipContent
Pre-built tooltip renderer for Recharts. Pass as the `content` prop of `ComposedChart.Tooltip`. See [ChartTooltip](https://heroui.pro/docs/react/components/chart-tooltip) for full props.
# Line Chart
**Category**: react
**URL**: https://heroui.pro/docs/react/components/line-chart
> A React line chart for web trend data with multi-series, sparkline, and custom tooltip support.
## Usage
## Anatomy
Import the LineChart component and access all parts using dot notation.
`LineChart` is a subpath-only import (`@heroui-pro/react/line-chart`) because it depends on the optional `recharts` peer. It is not exported from the package root, so SSR apps that don't use it never need `recharts` installed.
```tsx
import {LineChart} from "@heroui-pro/react/line-chart";
```
## Dashed Comparison
## KPI With Chart
## Multi Line Chart Colors
## Portfolio
## Sparkline
## Stats With Chart
## Traffic Source
## With Custom Tooltip
## With Dots
## CSS Classes
### Element Classes
* `.line-chart` — Root container wrapping `ResponsiveContainer` and the Recharts chart.
### Recharts Theming
The following CSS rules target Recharts internal class names to apply HeroUI design tokens automatically:
* `.line-chart .recharts-cartesian-axis-tick-value` — Axis tick labels. 10px muted text.
* `.line-chart .recharts-cartesian-axis-line` — Axis lines. Hidden by default.
* `.line-chart .recharts-cartesian-axis-tick-line` — Tick lines. Hidden by default.
* `.line-chart .recharts-cartesian-grid line` — Cartesian grid lines. Muted stroke at 0.15 opacity.
* `.line-chart .recharts-tooltip-cursor` — Tooltip cursor. Dashed vertical line on hover.
* `.line-chart .recharts-active-dot circle` — Active dot. Outlined with surface color for contrast.
## API Reference
### LineChart
The root wrapper. Renders a `ResponsiveContainer` + Recharts `LineChart` with HeroUI CSS theming applied automatically.
| Prop | Type | Default | Description |
| ---------- | ------------------------------------------------------------------ | ------------------------------------------ | ------------------------------------------------------------------------- |
| `data` | `Record[]` | — | Chart data — array of objects with numeric/string fields for each series. |
| `height` | `number` | `300` | Chart height in pixels. |
| `width` | ``number \| `${number}%` `` | `"100%"` | Chart width in pixels or percentage string. |
| `margin` | `{ top?: number; right?: number; bottom?: number; left?: number }` | `{ top: 8, right: 8, bottom: 0, left: 0 }` | Recharts margin around the chart area. |
| `children` | `ReactNode` | — | Recharts child components (`LineChart.Line`, `LineChart.XAxis`, etc.). |
Also supports all native `div` HTML attributes.
### LineChart.Line
Re-exported Recharts `Line` component. Follows the [Recharts Line API](https://recharts.github.io/en-US/api/Line/).
### LineChart.XAxis
Re-exported Recharts `XAxis` component. Follows the [Recharts XAxis API](https://recharts.github.io/en-US/api/XAxis/).
### LineChart.YAxis
Re-exported Recharts `YAxis` component. Follows the [Recharts YAxis API](https://recharts.github.io/en-US/api/YAxis/).
### LineChart.Grid
Re-exported Recharts `CartesianGrid` component. Follows the [Recharts CartesianGrid API](https://recharts.github.io/en-US/api/CartesianGrid/).
### LineChart.Tooltip
Re-exported Recharts `Tooltip` component. Follows the [Recharts Tooltip API](https://recharts.github.io/en-US/api/Tooltip/). Use with `LineChart.TooltipContent` for styled tooltips.
### LineChart.TooltipContent
Pre-built tooltip renderer for Recharts. Pass as the `content` prop of `LineChart.Tooltip`. See [ChartTooltip](https://heroui.pro/docs/react/components/chart-tooltip) for full props.
# Pie Chart
**Category**: react
**URL**: https://heroui.pro/docs/react/components/pie-chart
> A pie and donut chart for proportional data with labels, legends, and nested ring support.
## Usage
## Anatomy
Import the PieChart component and access all parts using dot notation.
`PieChart` is a subpath-only import (`@heroui-pro/react/pie-chart`) because it depends on the optional `recharts` peer. It is not exported from the package root, so SSR apps that don't use it never need `recharts` installed.
```tsx
import {PieChart} from "@heroui-pro/react/pie-chart";
```
## Custom Tooltip
## Donut
## Donut With Content
## Donut With Label
## Nested Donut
## With Breakdown
## CSS Classes
### Element Classes
* `.pie-chart` — Root container wrapping `ResponsiveContainer` and the Recharts chart.
### Recharts Theming
The following CSS rules target Recharts internal class names to apply HeroUI design tokens automatically:
* `.pie-chart .recharts-pie-sector path` — Pie sector strokes. Removed to avoid messy convergence at the center; use `paddingAngle` on `` for clean slice separation.
* `.pie-chart .recharts-pie-label-text` — Outside label text. 11px muted.
* `.pie-chart .recharts-pie-label-line` — Connector lines from slice to label. Muted at 0.3 opacity.
* `.pie-chart .recharts-label` — Center label text for donut charts. Uses foreground color.
* `.pie-chart .recharts-tooltip-cursor` — Tooltip cursor. Hidden for pie charts.
* `.pie-chart .recharts-tooltip-wrapper` — Tooltip wrapper. Elevated z-index to render above center content.
## API Reference
### PieChart
The root wrapper. Renders a `ResponsiveContainer` + Recharts `PieChart` with HeroUI CSS theming applied automatically.
| Prop | Type | Default | Description |
| ---------- | --------------------------- | -------- | ------------------------------------------------------------------ |
| `height` | `number` | `300` | Chart height in pixels. |
| `width` | ``number \| `${number}%` `` | `"100%"` | Chart width in pixels or percentage string. |
| `children` | `ReactNode` | — | Recharts child components (`PieChart.Pie`, `PieChart.Cell`, etc.). |
Also supports all native `div` HTML attributes.
### PieChart.Pie
Re-exported Recharts `Pie` component. Follows the [Recharts Pie API](https://recharts.github.io/en-US/api/Pie/).
### PieChart.Cell
Re-exported Recharts `Cell` component. Follows the [Recharts Cell API](https://recharts.github.io/en-US/api/Cell/).
### PieChart.Label
Re-exported Recharts `Label` component. Follows the [Recharts Label API](https://recharts.github.io/en-US/api/Label/).
### PieChart.Tooltip
Re-exported Recharts `Tooltip` component. Follows the [Recharts Tooltip API](https://recharts.github.io/en-US/api/Tooltip/). Use with `PieChart.TooltipContent` for styled tooltips.
### PieChart.TooltipContent
Pre-built tooltip renderer for Recharts. Pass as the `content` prop of `PieChart.Tooltip`. See [ChartTooltip](https://heroui.pro/docs/react/components/chart-tooltip) for full props.
# Radar Chart
**Category**: react
**URL**: https://heroui.pro/docs/react/components/radar-chart
> A React radar chart for comparing multivariate web data with fill, dots, and multiple series.
## Usage
## Anatomy
Import the RadarChart component and access all parts using dot notation.
`RadarChart` is a subpath-only import (`@heroui-pro/react/radar-chart`) because it depends on the optional `recharts` peer. It is not exported from the package root, so SSR apps that don't use it never need `recharts` installed.
```tsx
import {RadarChart} from "@heroui-pro/react/radar-chart";
```
## Comparison
## Dots Only
## Multi Series
## With Radius Axis
## CSS Classes
### Element Classes
* `.radar-chart` — Root container wrapping `ResponsiveContainer` and the Recharts chart.
### Recharts Theming
The following CSS rules target Recharts internal class names to apply HeroUI design tokens automatically:
* `.radar-chart .recharts-polar-angle-axis-tick-value` — Angle axis tick labels. 11px muted text.
* `.radar-chart .recharts-polar-radius-axis-tick-value` — Radius axis tick labels. 10px muted text.
* `.radar-chart .recharts-polar-grid-concentric-polygon` / `.recharts-polar-grid-concentric-circle` — Concentric grid shapes. Muted stroke at 0.2 opacity.
* `.radar-chart .recharts-polar-grid-angle line` — Angle grid lines. Muted stroke at 0.2 opacity.
* `.radar-chart .recharts-tooltip-cursor` — Tooltip cursor. Hidden for radar charts.
## API Reference
### RadarChart
The root wrapper. Renders a `ResponsiveContainer` + Recharts `RadarChart` with HeroUI CSS theming applied automatically.
| Prop | Type | Default | Description |
| ---------- | ------------------------------------ | -------- | ---------------------------------------------------------------------------- |
| `data` | `Record[]` | — | Chart data — array of objects with a category key and numeric series fields. |
| `height` | `number` | `300` | Chart height in pixels. |
| `width` | ``number \| `${number}%` `` | `"100%"` | Chart width in pixels or percentage string. |
| `children` | `ReactNode` | — | Recharts child components (`RadarChart.Radar`, `RadarChart.Grid`, etc.). |
Also supports all native `div` HTML attributes.
### RadarChart.Radar
Re-exported Recharts `Radar` component. Follows the [Recharts Radar API](https://recharts.github.io/en-US/api/Radar/).
### RadarChart.Grid
Re-exported Recharts `PolarGrid` component. Follows the [Recharts PolarGrid API](https://recharts.github.io/en-US/api/PolarGrid/).
### RadarChart.AngleAxis
Re-exported Recharts `PolarAngleAxis` component. Follows the [Recharts PolarAngleAxis API](https://recharts.github.io/en-US/api/PolarAngleAxis/).
### RadarChart.RadiusAxis
Re-exported Recharts `PolarRadiusAxis` component. Follows the [Recharts PolarRadiusAxis API](https://recharts.github.io/en-US/api/PolarRadiusAxis/).
### RadarChart.Tooltip
Re-exported Recharts `Tooltip` component. Follows the [Recharts Tooltip API](https://recharts.github.io/en-US/api/Tooltip/). Use with `RadarChart.TooltipContent` for styled tooltips.
### RadarChart.TooltipContent
Pre-built tooltip renderer for Recharts. Pass as the `content` prop of `RadarChart.Tooltip`. See [ChartTooltip](https://heroui.pro/docs/react/components/chart-tooltip) for full props.
# Radial Chart
**Category**: react
**URL**: https://heroui.pro/docs/react/components/radial-chart
> A React radial chart for web gauges, progress rings, and circular data with customizable arcs and labels.
## Usage
## Anatomy
Import the RadialChart component and access all parts using dot notation.
`RadialChart` is a subpath-only import (`@heroui-pro/react/radial-chart`) because it depends on the optional `recharts` peer. It is not exported from the package root, so SSR apps that don't use it never need `recharts` installed.
```tsx
import {RadialChart} from "@heroui-pro/react/radial-chart";
```
## Gauge
## Gauge Grid
## Progress Ring
## With Legend
## CSS Classes
### Element Classes
* `.radial-chart` — Root container wrapping `ResponsiveContainer` and the Recharts chart.
### Recharts Theming
The following CSS rules target Recharts internal class names to apply HeroUI design tokens automatically:
* `.radial-chart .recharts-tooltip-cursor` — Tooltip cursor. Hidden for radial charts.
* `.radial-chart .recharts-tooltip-wrapper` — Tooltip wrapper. Elevated z-index to render above center content.
* `.radial-chart .recharts-radial-bar-background-sector` — Bar background track. Uses the separator token for a subtle fill.
## API Reference
### RadialChart
The root wrapper. Renders a `ResponsiveContainer` + Recharts `RadialBarChart` with HeroUI CSS theming applied automatically.
| Prop | Type | Default | Description |
| ------------- | ------------------------------------ | -------- | ------------------------------------------------------------------------ |
| `data` | `Record[]` | — | Chart data — array of objects. Each entry becomes a concentric ring. |
| `height` | `number` | `300` | Chart height in pixels. |
| `width` | ``number \| `${number}%` `` | `"100%"` | Chart width in pixels or percentage string. |
| `barSize` | `number` | `10` | Bar thickness in pixels. |
| `innerRadius` | `number \| string` | `"30%"` | Inner radius of the bar area. |
| `outerRadius` | `number \| string` | `"80%"` | Outer radius of the bar area. |
| `startAngle` | `number` | `90` | Start angle in degrees. |
| `endAngle` | `number` | `-270` | End angle in degrees. |
| `children` | `ReactNode` | — | Recharts child components (`RadialChart.Bar`, `RadialChart.Cell`, etc.). |
Also supports all native `div` HTML attributes.
### RadialChart.Bar
Re-exported Recharts `RadialBar` component. Follows the [Recharts RadialBar API](https://recharts.github.io/en-US/api/RadialBar/).
### RadialChart.Cell
Re-exported Recharts `Cell` component. Follows the [Recharts Cell API](https://recharts.github.io/en-US/api/Cell/).
### RadialChart.AngleAxis
Re-exported Recharts `PolarAngleAxis` component. Follows the [Recharts PolarAngleAxis API](https://recharts.github.io/en-US/api/PolarAngleAxis/).
### RadialChart.Tooltip
Re-exported Recharts `Tooltip` component. Follows the [Recharts Tooltip API](https://recharts.github.io/en-US/api/Tooltip/). Use with `RadialChart.TooltipContent` for styled tooltips.
### RadialChart.TooltipContent
Pre-built tooltip renderer for Recharts. Pass as the `content` prop of `RadialChart.Tooltip`. See [ChartTooltip](https://heroui.pro/docs/react/components/chart-tooltip) for full props.
# Emoji Reaction Button
**Category**: react
**URL**: https://heroui.pro/docs/react/components/emoji-reaction-button
> An animated emoji reaction button with count display and toggle state for social interactions.
## Usage
## Anatomy
Import the EmojiReactionButton component and access all parts using dot notation.
```tsx
import {EmojiReactionButton} from "@heroui-pro/react";
```
## Disabled
## Read-only
## Sizes
## CSS Classes
### Base & Size Classes
* `.emoji-reaction-button` - Base toggle button with rounded-full shape
* `.emoji-reaction-button--sm` - Small size
* `.emoji-reaction-button--md` - Medium size (default)
* `.emoji-reaction-button--lg` - Large size
### Element Classes
* `.emoji-reaction-button__emoji` - The emoji character
* `.emoji-reaction-button__count` - The reaction count
### Interactive States
* **Selected**: `[data-selected="true"]` on root (accent border and background tint; count text turns accent)
* **Hover**: `:hover` or `[data-hovered="true"]` (background change)
* **Focus visible**: `:focus-visible` or `[data-focus-visible="true"]` (focus ring)
* **Pressed**: `:active` or `[data-pressed="true"]` (scale down)
* **Read-only**: `[data-readonly="true"]` on root (default cursor, no press scale, no pointer interaction)
* **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity)
## API Reference
### EmojiReactionButton
The root component. Wraps RAC [ToggleButton](https://react-spectrum.adobe.com/react-aria/ToggleButton.html).
| Prop | Type | Default | Description |
| ----------------- | ------------------------------------------- | ------- | -------------------------------------------------------------------------- |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size variant |
| `isSelected` | `boolean` | - | Whether the button is selected (controlled) |
| `defaultSelected` | `boolean` | - | Default selected state (uncontrolled) |
| `onChange` | `(isSelected: boolean) => void` | - | Handler called when the selected state changes |
| `isReadOnly` | `boolean` | - | Whether the button is read-only and should not respond to user interaction |
| `isDisabled` | `boolean` | - | Whether the button is disabled |
| `children` | `ReactNode \| ((renderProps) => ReactNode)` | - | Emoji and Count elements |
Also supports all RAC [ToggleButton](https://react-spectrum.adobe.com/react-aria/ToggleButton.html) props.
### EmojiReactionButton.Emoji
The emoji character display.
Also supports all native `span` HTML attributes.
### EmojiReactionButton.Count
The reaction count display.
Also supports all native `span` HTML attributes.
# Number Value
**Category**: react
**URL**: https://heroui.pro/docs/react/components/number-value
> A React number display with locale-aware currency, percentage, and compact formatting.
## Usage
## Anatomy
Import the NumberValue component and access all parts using dot notation.
```tsx
import {NumberValue} from "@heroui-pro/react";
```
## Compact
## Currency
## Format Options
## Percent
## Sign Display
## Tabular Nums
## With Prefix Suffix
## CSS Classes
### Base Classes
* `.number-value` - Base inline-flex wrapper
### Element Classes
* `.number-value__prefix` - Text before the formatted number
* `.number-value__value` - The formatted number
* `.number-value__suffix` - Text after the formatted number
## API Reference
### NumberValue
The root component. Formats and displays a number using locale-aware `Intl.NumberFormat`.
| Prop | Type | Default | Description |
| ----------------------- | ---------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------- |
| `value` | `number` | - | **Required.** The numeric value to format |
| `style` | `'decimal' \| 'currency' \| 'percent' \| 'unit'` | `'decimal'` | Formatting style |
| `currency` | `string` | - | Currency code (e.g. `"USD"`). Required when `style` is `"currency"` |
| `unit` | `string` | - | Unit type (e.g. `"degree"`). Required when `style` is `"unit"` |
| `notation` | `'standard' \| 'compact' \| 'scientific' \| 'engineering'` | `'standard'` | Notation style |
| `signDisplay` | `'auto' \| 'always' \| 'exceptZero' \| 'never'` | - | Controls when the sign is displayed |
| `minimumFractionDigits` | `number` | - | Minimum number of fraction digits |
| `maximumFractionDigits` | `number` | - | Maximum number of fraction digits |
| `locale` | `string` | - | Override the locale from the nearest I18nProvider |
| `formatOptions` | `NumberFormatOptions` | - | Format options passed directly to `NumberFormatter`. Overrides individual convenience props |
| `children` | `ReactNode \| ((formatted: string) => ReactNode)` | - | Prefix/Suffix sub-components or a render function receiving the formatted string |
Also supports all native `span` HTML attributes except `children` and `style`.
### NumberValue.Prefix
Text displayed before the formatted number.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ----------- |
| `children` | `ReactNode` | - | Prefix text |
Also supports all native `span` HTML attributes.
### NumberValue.Suffix
Text displayed after the formatted number.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ----------- |
| `children` | `ReactNode` | - | Suffix text |
Also supports all native `span` HTML attributes.
# Pressable Feedback
**Category**: react
**URL**: https://heroui.pro/docs/react/components/pressable-feedback
> A press interaction layer adding ripple, highlight, hold-to-confirm, and progress feedback effects to any element.
## Usage
## Anatomy
Import the PressableFeedback component and access all parts using dot notation.
```tsx
import {PressableFeedback} from "@heroui-pro/react";
```
## Comparison
## Disabled
## Hold Confirm Callback
## Hold Confirm Durations
## Hold Confirm Sweep
## Pressable Cards
## Progress Feedback Callback
## Progress Feedback Durations
## Progress Feedback No Reset
## Progress Feedback Sweep
## Standalone Highlight
## Standalone Ripple
## With Highlight
## With Hold Confirm
## With Progress Feedback
## With Ripple
## CSS Classes
### Base Classes
* `.pressable-feedback` - Base pressable container with relative positioning and overflow hidden
### Element Classes
* `.pressable-feedback__highlight` - Opacity-based press overlay
* `.pressable-feedback__ripple` - M3-style radial ripple container
* `.pressable-feedback__ripple-surface` - Ripple animation surface with `::before` (hover) and `::after` (press) pseudo-elements
* `.pressable-feedback__hold-confirm` - Clip-path hold-to-reveal overlay
* `.pressable-feedback__progress-feedback` - Clip-path click-to-progress overlay
### Interactive States
* **Focus visible**: `:focus-visible` or `[data-focus-visible="true"]` on `.pressable-feedback` (focus ring)
* **Disabled**: `:disabled` or `[aria-disabled="true"]` on `.pressable-feedback` (reduced opacity, no pointer events)
* **Hover**: `:hover` or `[data-hovered="true"]` on parent activates `.pressable-feedback__highlight` opacity
* **Pressed**: `:active` or `[data-pressed="true"]` on parent activates highlight pressed opacity
### Data Attributes
* `[data-sweep="right|left|down|up"]` on hold-confirm and progress-feedback - Clip-path sweep direction
* `[data-holding="true"]` on `.pressable-feedback__hold-confirm` - Currently being held
* `[data-complete="true"]` on hold-confirm/progress-feedback - Action completed
* `[data-progressing="true"]` on `.pressable-feedback__progress-feedback` - Progress is active
### CSS Variables
* `--pressable-feedback-highlight-color` - Highlight overlay color (default: `currentColor`)
* `--pressable-feedback-highlight-opacity` - Hover opacity (default: `0.08`)
* `--pressable-feedback-highlight-pressed-opacity` - Pressed opacity (default: `0.12`)
* `--pressable-feedback-ripple-color` - Ripple color (default: `currentColor`)
* `--pressable-feedback-ripple-hover-opacity` - Ripple hover opacity (default: `0.08`)
* `--pressable-feedback-ripple-pressed-opacity` - Ripple pressed opacity (default: `0.12`)
* `--pressable-feedback-hold-confirm-duration` - Hold duration (default: `2000ms`)
* `--pressable-feedback-hold-confirm-release-duration` - Release snap-back duration (default: `200ms`)
* `--pressable-feedback-progress-feedback-duration` - Progress duration (default: `2000ms`)
* `--pressable-feedback-progress-feedback-release-duration` - Reset snap-back duration (default: `300ms`)
## API Reference
### PressableFeedback
The root pressable container. Renders a `button` element by default.
| Prop | Type | Default | Description |
| ------------ | ------------------- | ------- | --------------------------------------------------------------- |
| `isDisabled` | `boolean` | `false` | Whether the pressable is disabled |
| `children` | `ReactNode` | - | Feedback layers and content |
| `className` | `string` | - | Additional CSS class |
| `render` | `DOMRenderFunction` | - | Custom render function to override the default `button` element |
Also supports all native `button` HTML attributes.
### PressableFeedback.Highlight
Opacity-based hover/press overlay. No additional props beyond standard `div` attributes.
### PressableFeedback.Ripple
M3-style radial ripple effect.
| Prop | Type | Default | Description |
| ---------------------- | --------------- | ------- | -------------------------------------------- |
| `duration` | `number` | `150` | Duration in ms for the ripple grow animation |
| `hoverOpacity` | `number` | `0.08` | Opacity of the hover state |
| `pressedOpacity` | `number` | `0.12` | Opacity of the pressed state |
| `minimumPressDuration` | `number` | `225` | Minimum press duration in ms |
| `isDisabled` | `boolean` | - | Whether the ripple is disabled |
| `className` | `string` | - | Additional CSS class |
| `style` | `CSSProperties` | - | Additional inline styles |
### PressableFeedback.HoldConfirm
Clip-path hold-to-reveal overlay.
| Prop | Type | Default | Description |
| ----------------- | ------------------------------------- | --------- | ----------------------------------------------------- |
| `duration` | `number` | `2000` | Hold duration in ms before the action is confirmed |
| `releaseDuration` | `number` | `200` | Duration in ms for the snap-back animation on release |
| `sweep` | `'right' \| 'left' \| 'down' \| 'up'` | `'right'` | Which edge the clip-path reveal sweeps toward |
| `resetOnComplete` | `boolean` | `true` | Whether to reset the overlay after the hold completes |
| `isDisabled` | `boolean` | - | Whether the hold confirm is disabled |
| `onComplete` | `() => void` | - | Fired when the hold reaches the full duration |
| `children` | `ReactNode` | - | Overlay content shown during the reveal |
| `className` | `string` | - | Additional CSS class |
### PressableFeedback.ProgressFeedback
Clip-path click-to-progress overlay (auto, no hold required).
| Prop | Type | Default | Description |
| ----------------- | ------------------------------------- | --------- | ------------------------------------------------------ |
| `duration` | `number` | `2000` | Progress duration in ms before the action is confirmed |
| `releaseDuration` | `number` | `300` | Duration in ms for the snap-back animation on reset |
| `sweep` | `'right' \| 'left' \| 'down' \| 'up'` | `'right'` | Which edge the clip-path reveal sweeps toward |
| `autoReset` | `boolean` | `true` | Whether to automatically reset after completing |
| `resetDelay` | `number` | `1500` | Delay in ms before resetting after completion |
| `isDisabled` | `boolean` | - | Whether the progress feedback is disabled |
| `onComplete` | `() => void` | - | Fired when the progress reaches the full duration |
| `onReset` | `() => void` | - | Fired when the overlay resets back to idle |
| `children` | `ReactNode` | - | Overlay content shown during the reveal |
| `className` | `string` | - | Additional CSS class |
# Rating
**Category**: react
**URL**: https://heroui.pro/docs/react/components/rating
> A React star rating input with fractional read-only values, custom icons, sizes, and selection.
## Usage
## Anatomy
Import the Rating component and access all parts using dot notation.
```tsx
import {Rating} from "@heroui-pro/react";
```
## Controlled
## Custom Color
## Custom Icon Heart
## Custom Icon Per Item
## Disabled
## Product Review
## Read Only
## Read Only Fractional
## Render Function
## Sizes
## With Label
## CSS Classes
### Base & Size Classes
* `.rating` - Base rating group container
* `.rating--sm` - Small size (no gap, smaller icons)
* `.rating--md` - Medium size (1px gap, default)
* `.rating--lg` - Large size (2px gap, larger icons)
### Element Classes
* `.rating__item` - Individual rating option
* `.rating__item--sm` / `.rating__item--md` / `.rating__item--lg` - Item size variants
* `.rating__icon` - Icon wrapper (star, heart, etc.)
* `.rating__icon-partial` - Overlay for fractional read-only display
### Interactive States
* **Active**: `[data-active="true"]` on `.rating__item` (icon turns active color)
* **Read-only**: `[data-readonly="true"]` on `.rating__item` (default cursor, no press scale)
* **Focus visible**: `[data-focus-visible="true"]` on `.rating` or `.rating__item` (focus ring)
* **Pressed**: `:active` or `[data-pressed="true"]` on `.rating__item` (scale down to 0.8)
* **Disabled**: `[data-disabled="true"]` on `.rating` or `:disabled` / `[aria-disabled="true"]` on `.rating__item` (reduced opacity)
### CSS Variables
* `--rating-active-color` - Color for active/selected stars (default: `var(--color-warning)`)
* `--rating-inactive-color` - Color for inactive stars (default: `var(--color-surface-tertiary)`)
* `--rating-partial` - Width of the partial overlay for fractional display (set via inline style)
## API Reference
### Rating
The root component. Wraps RAC [RadioGroup](https://react-spectrum.adobe.com/react-aria/RadioGroup.html) in horizontal orientation.
| Prop | Type | Default | Description |
| --------------- | ------------------------- | ------- | -------------------------------------------- |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size variant |
| `value` | `number` | - | The current rating value (controlled) |
| `defaultValue` | `number` | - | The initial rating value (uncontrolled) |
| `onValueChange` | `(value: number) => void` | - | Handler called when the rating value changes |
| `icon` | `ReactNode` | - | Custom icon element for all rating items |
Also supports all RAC [RadioGroup](https://react-spectrum.adobe.com/react-aria/RadioGroup.html) props except `defaultValue`, `onChange`, `orientation`, and `value`.
### Rating.Item
An individual rating option. Wraps RAC [Radio](https://react-spectrum.adobe.com/react-aria/Radio.html).
| Prop | Type | Default | Description |
| ---------- | ------------------------------------------------------------ | ------- | --------------------------------------------- |
| `value` | `number` | - | **Required.** The numeric value for this item |
| `children` | `ReactNode \| ((props: RatingItemRenderProps) => ReactNode)` | - | Custom content or render function |
Also supports all RAC [Radio](https://react-spectrum.adobe.com/react-aria/Radio.html) props except `children` and `value`.
### RatingItemRenderProps
When using the render prop pattern on `Rating.Item`:
| Property | Type | Description |
| ---------------- | --------- | ------------------------------------------------------------- |
| `isActive` | `boolean` | Whether this item is at or below the current rating |
| `isPartial` | `boolean` | Whether this item shows a partial fill (read-only fractional) |
| `partialPercent` | `number` | The fill percentage for partial display (0-100) |
# Trend Chip
**Category**: react
**URL**: https://heroui.pro/docs/react/components/trend-chip
> A React trend chip for web metrics with direction, percentage, icon, and contextual suffix.
## Usage
## Anatomy
Import the TrendChip component and access all parts using dot notation.
```tsx
import {TrendChip} from "@heroui-pro/react";
```
## Custom Indicator
## Prefix And Suffix
## Sizes
## Tabular Nums
## Variants
## CSS Classes
### Base & Size Classes
* `.trend-chip` - Base chip wrapper
* `.trend-chip--sm` - Small size (default)
* `.trend-chip--md` - Medium size
* `.trend-chip--lg` - Large size
### Element Classes
* `.trend-chip__indicator` - Trend arrow icon
* `.trend-chip__value` - Numeric value text
* `.trend-chip__prefix` - Text before the value
* `.trend-chip__suffix` - Text after the value
### Data Attributes
* `[data-trend="up"]` / `[data-trend="down"]` / `[data-trend="neutral"]` on the root - Current trend direction
## API Reference
### TrendChip
The root component. Wraps HeroUI [Chip](https://heroui.com/docs/react/components/chip) with trend-aware coloring and arrow icons.
| Prop | Type | Default | Description |
| ---------- | -------------------------------------------------- | -------- | ----------------------------------------------------------------------- |
| `trend` | `'up' \| 'down' \| 'neutral'` | `'up'` | Trend direction; controls arrow icon and color (success/danger/warning) |
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Size variant |
| `variant` | `'primary' \| 'secondary' \| 'soft' \| 'tertiary'` | `'soft'` | Chip style variant |
| `children` | `ReactNode` | - | Value text, optional Indicator, Prefix, and Suffix sub-components |
Also supports all [HeroUI Chip](https://heroui.com/docs/react/components/chip) props except `children`, `color`, and `size`.
### TrendChip.Indicator
Custom trend arrow icon. When omitted, a default directional arrow is rendered based on the `trend` prop.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ----------------------- |
| `children` | `ReactNode` | - | Custom SVG icon element |
Also supports all native `svg` HTML attributes.
### TrendChip.Prefix
Text displayed before the value.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ----------- |
| `children` | `ReactNode` | - | Prefix text |
Also supports all native `span` HTML attributes.
### TrendChip.Suffix
Text displayed after the value.
| Prop | Type | Default | Description |
| ---------- | ----------- | ------- | ----------- |
| `children` | `ReactNode` | - | Suffix text |
Also supports all native `span` HTML attributes.
# Action Bar
**Category**: react
**URL**: https://heroui.pro/docs/react/components/action-bar
> A floating toolbar for contextual actions — bulk selection, editing controls, or any set of actions that appear in response to user interaction.
## Usage
The `ActionBar` component is a floating pill-shaped toolbar that animates in/out based on the `isOpen` prop. It uses a **Prefix / Content / Suffix** structure.
Combine with [ListView](https://heroui.pro/docs/react/components/list-view) for bulk selection rows.
## With Data Grid
Combine with [DataGrid](https://heroui.pro/docs/react/components/data-grid) for bulk selection workflows. The ActionBar appears when rows are selected and provides contextual actions like edit, export, archive, or delete.
## Anatomy
```tsx
import {ActionBar} from "@heroui-pro/react";
import {Button, Chip, Separator, Tooltip} from "@heroui/react";
{count}Clear selection
```
All three sections (`Prefix`, `Content`, `Suffix`) are optional. Use `Separator` from `@heroui/react` between sections as needed.
## Responsive Labels
Use the `action-bar__label` CSS class on any text you want hidden on mobile. Below the `sm` breakpoint (640px), elements with this class become `sr-only` — buttons collapse to icon-only while remaining accessible.
```tsx
```
## CSS Classes
### Base Classes
* `.action-bar` — Outer positioning wrapper. Fixed to viewport bottom-center with `pointer-events: none`.
* `.action-bar__wrapper` — The visible pill surface. Restores `pointer-events: auto`, applies shadow.
### Element Classes
* `.action-bar__prefix` — Leading section (badges, counts).
* `.action-bar__content` — Middle section for main actions.
* `.action-bar__suffix` — Trailing section (dismiss button).
* `.action-bar__label` — Text that collapses to `sr-only` below 640px.
## API Reference
### ActionBar
The root component. Extends `ToolbarProps` from `@heroui/react`.
| Prop | Type | Default | Description |
| ------------- | ---------------------------- | -------------- | ------------------------------------------------------------------------------ |
| `isOpen` | `boolean` | — | Controls visibility with animated enter/exit. Required. |
| `aria-label` | `string` | `"Actions"` | Accessible label for the toolbar. |
| `isAttached` | `boolean` | `true` | Whether the toolbar has a surface background with full rounding. |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | The orientation of the toolbar. |
| `className` | `string` | — | Additional CSS classes applied to the toolbar wrapper. |
| `children` | `ReactNode` | — | Content — typically `Prefix`, `Content`, `Suffix`, and `Separator` components. |
### ActionBar.Prefix
Leading section container.
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | --------------------------------------------------------- |
| `children` | `ReactNode` | — | Content for the leading section (badges, counts, labels). |
| `className` | `string` | — | Additional CSS classes. |
### ActionBar.Content
Middle section container for main actions.
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ------------------------------- |
| `children` | `ReactNode` | — | Action buttons, dropdowns, etc. |
| `className` | `string` | — | Additional CSS classes. |
### ActionBar.Suffix
Trailing section container.
| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | ---------------------------------- |
| `children` | `ReactNode` | — | Dismiss button, secondary actions. |
| `className` | `string` | — | Additional CSS classes. |
# Agenda
**Category**: react
**URL**: https://heroui.pro/docs/react/components/agenda
> A composable calendar component with day, week, and month views for displaying and managing events with drag interactions.
## Usage
## Anatomy
Import the Agenda component and the `useAgenda` hook. Access all subcomponents using dot notation.
```tsx
import {Agenda, useAgenda} from "@heroui-pro/react";
const agenda = useAgenda({
events: [...],
defaultView: "week",
});
{/* Day / Week view */}
all-day
{agenda.allDayLayout.map((item) => (
))}
{agenda.visibleDays.map((day) => (
{agenda.getEventsForDay(day).map((event) => (
))}
))}
{/* Month view */}
{agenda.visibleWeeks.map((week, i) => {
const rowLayout = agenda.getMonthRowLayout(week);
return (
{rowLayout.items.map((item) => (
))}
{week.map((day, colIdx) => (
{agenda.getPerCellEvents(day, week).map((event) => (
))}
))}
);
})}
```
## Views
The Agenda supports three views controlled via the `ViewSelector` or programmatically through `setView`:
* **Day** — single day with full time grid
* **Week** — 7-day (or custom count) view with shared time grid
* **Month** — calendar grid with spanning multi-day events
## Events
Events are defined as an array of `AgendaEvent` objects:
```tsx
interface AgendaEvent {
id: string;
title: string;
start: CalendarDateTime;
end: CalendarDateTime;
color?: string;
isAllDay?: boolean;
isReadOnly?: boolean;
status?: "confirmed" | "unconfirmed";
}
```
### Unconfirmed Events
Set `status: "unconfirmed"` to render an event with a dashed border and transparent background, indicating a tentative or pending event.
### Read-Only Events
Set `isReadOnly: true` to prevent an event from being moved or resized. The event can still be selected but drag interactions are disabled and the resize handle is hidden.
## Drag Interactions
All drag interactions work out of the box when callbacks are provided:
* **Drag to create** — click and drag on an empty time slot to create a new event
* **Drag to move** — drag an event to a different time or day
* **Drag to resize** — drag the bottom edge of an event to change its duration
* **Cross-day move** — drag an event horizontally to move it to a different day
On mobile, drag interactions (create, move, resize) are disabled by default. The consumer controls this by passing `undefined` for the drag callbacks when on a small screen.
### Event Callbacks
```tsx
const agenda = useAgenda({
events,
onEventCreate: (event) => { /* { start, end } */ },
onEventMove: (id, start, end) => { /* moved */ },
onEventResize: (id, start, end) => { /* resized */ },
onEventDelete: (id) => { /* deleted via Delete/Backspace key */ },
onEventSelect: (id) => { /* selected */ },
});
```
## All-Day Events
All-day events appear in a collapsible section above the time grid. Multi-day all-day events span across day columns.
The section includes an expand/collapse toggle. When collapsed, event counts are shown per day (e.g. "2 events"). Customize the collapsed label with the `collapsedLabel` prop on `Agenda.AllDaySection`.
## Month View Features
### Spanning Events
Multi-day all-day events render as bars spanning across the month grid row. Use `getMonthRowLayout(week)` to compute layout positions and `getPerCellEvents(day, week)` for per-cell events.
### Event Overflow
`Agenda.MonthCell` limits visible events via the `maxEvents` prop (default: 2). Overflow shows a "N more" link that navigates to the day view. Customize the label with the `moreLabel` prop.
### Date Navigation
Clicking a date number in the month grid navigates to that date in day view. The first day of each month shows the month name (e.g. "May 1").
## Weekend Highlighting
Saturday and Sunday columns automatically receive a subtle gray background (`data-weekend` attribute) in all views.
## Current Time Indicator
A live indicator shows the current time in the time grid:
* Displays a time label badge (e.g. "10:30 AM") in the time column
* In week view, a faded line spans all columns with an active highlight on today's column
* Nearby hour labels auto-hide to avoid overlap
* Updates every minute
## CSS Classes
### Base
* `.agenda` — Root container. Sets CSS custom properties for sizing.
### CSS Variables
* `--agenda-slot-height` — Height of each hour slot (default: `60px`).
* `--agenda-time-column-width` — Width of the time labels column (default: `58px`).
* `--agenda-current-time-color` — Color of the current time indicator (default: `var(--color-danger)`).
* `--agenda-event-radius` — Border radius of event cards (default: `var(--radius-md)`).
### Header
* `.agenda__header` — Flex container for heading, view selector, and navigation.
* `.agenda__heading` — Month/year title text.
* `.agenda__navigation` — Wrapper for nav buttons and today button.
* `.agenda__nav-button` — Override hook for navigation arrow buttons (uses HeroUI Button).
* `.agenda__today-button` — Override hook for the Today button (uses HeroUI Button).
* `.agenda__view-selector` — Override hook for the view selector (uses HeroUI Segment).
### Day/Week View
* `.agenda__week-header` — Row of day headers above the time grid.
* `.agenda__day-header` — Individual day header with name and date.
* `.agenda__time-grid` — Scrollable time grid container.
* `.agenda__time-labels` — Sticky column of hour labels.
* `.agenda__time-label` — Individual hour label.
* `.agenda__day-column` — Column for a single day's events.
* `.agenda__time-slot` — Individual hour slot row.
### Events
* `.agenda__event` — Positioned event card in the time grid.
* `.agenda__event-title` — Event title text.
* `.agenda__event-time` — Event time range text.
* `.agenda__resize-handle` — Bottom resize handle with hover indicator.
### All-Day Section
* `.agenda__all-day-section` — Grid container for all-day events.
* `.agenda__all-day-toggle` — Expand/collapse chevron button.
* `.agenda__all-day-label` — "all-day" label text.
* `.agenda__all-day-event` — All-day event bar.
* `.agenda__all-day-summary` — Collapsed event count per day.
### Month View
* `.agenda__month-grid` — Month grid container.
* `.agenda__month-weekday-header` — Sticky weekday names row.
* `.agenda__month-row` — Week row in the month grid.
* `.agenda__month-cell` — Individual day cell.
* `.agenda__month-cell-date` — Date number button (navigates to day view).
* `.agenda__month-cell-more` — "N more" overflow link.
* `.agenda__month-event` — Per-cell event in month view.
* `.agenda__month-spanning-event` — Multi-day event bar spanning across cells.
### Interactive States
* `[data-dragging]` — Applied during drag interactions.
* `[data-resizing]` — Applied during resize.
* `[data-selected="true"]` — Applied to selected events.
* `[data-status="unconfirmed"]` — Dashed border style for tentative events.
* `[data-readonly]` — Applied to read-only events.
* `[data-weekend]` — Applied to weekend columns and cells.
* `[data-today]` — Applied to today's date elements.
* `[data-drop-target]` — Applied to the target cell during drag.
### Previews
* `.agenda__create-preview` — Dashed preview rectangle during drag-to-create.
* `.agenda__drop-preview` — Outlined preview at the target position during drag-to-move.
## API Reference
### useAgenda
The main hook for managing agenda state. Returns all data and methods needed by the component.
| Option | Type | Default | Description |
| ----------------- | ------------------------------- | -------- | ------------------------------------------------------------ |
| `events` | `AgendaEvent[]` | — | Array of events to display. Required. |
| `defaultView` | `"day" \| "week" \| "month"` | `"week"` | Initial view. |
| `view` | `"day" \| "week" \| "month"` | — | Controlled view state. |
| `onViewChange` | `(view: AgendaView) => void` | — | Called when the view changes. |
| `defaultDate` | `CalendarDate` | today | Initial focused date. |
| `date` | `CalendarDate` | — | Controlled date state. |
| `onDateChange` | `(date: CalendarDate) => void` | — | Called when the date changes. |
| `startHour` | `number` | `0` | First visible hour in the time grid. |
| `endHour` | `number` | `24` | Last visible hour in the time grid. |
| `slotDuration` | `number` | `60` | Duration of each time slot in minutes. |
| `onEventCreate` | `(event: {start, end}) => void` | — | Called when dragging to create a new event. |
| `onEventDelete` | `(id: string) => void` | — | Called when Delete/Backspace is pressed on a selected event. |
| `onEventMove` | `(id, start, end) => void` | — | Called when an event is dragged to a new position. |
| `onEventResize` | `(id, start, end) => void` | — | Called when an event is resized. |
| `onEventSelect` | `(id: string \| null) => void` | — | Called when an event is selected or deselected. |
| `selectedEventId` | `string \| null` | — | Controlled selected event state. |
### Agenda
Root component. Wraps children in context and Motion providers.
Also supports all HTML `div` props.
### Agenda.Header
Container for heading, view selector, and navigation controls.
Also supports all HTML `div` props.
### Agenda.Heading
Displays the current month and year (e.g. "May 2026").
Also supports all HTML `h1` props.
### Agenda.ViewSelector
Segmented control for switching between day, week, and month views. Built on the HeroUI Segment component.
| Prop | Type | Default | Description |
| ------ | ---------------------- | ------- | ---------------------------- |
| `size` | `"sm" \| "md" \| "lg"` | `"sm"` | Size of the segment control. |
### Agenda.NavButton
Navigation button for previous/next. Built on the HeroUI Button component.
| Prop | Type | Default | Description |
| ------ | ---------------------- | ------- | ------------------------ |
| `slot` | `"previous" \| "next"` | — | Direction of navigation. |
### Agenda.TodayButton
Button to navigate to today's date. Built on the HeroUI Button component.
### Agenda.AllDaySection
Collapsible section for all-day events with a CSS grid layout for spanning events.
| Prop | Type | Default | Description |
| ---------------- | --------------------------- | ------------ | ---------------------------------------- |
| `collapsedLabel` | `(count: number) => string` | `"N events"` | Custom label for collapsed event counts. |
### Agenda.AllDayEvent
An all-day event bar positioned in the grid.
| Prop | Type | Default | Description |
| ---------- | ------------- | ------- | ---------------------------------- |
| `event` | `AgendaEvent` | — | The event data. Required. |
| `colStart` | `number` | — | Grid column start index (0-based). |
| `colSpan` | `number` | — | Number of columns to span. |
| `row` | `number` | — | Row index for stacking. |
### Agenda.Event
A timed event card positioned absolutely in a day column. Supports drag-to-move and drag-to-resize.
| Prop | Type | Default | Description |
| ------- | ------------- | ------- | ------------------------- |
| `event` | `AgendaEvent` | — | The event data. Required. |
### Agenda.MonthCell
A day cell in the month grid. Limits visible events and shows overflow.
| Prop | Type | Default | Description |
| ------------------ | --------------------------- | ---------- | ------------------------------------------------- |
| `date` | `CalendarDate` | — | The date for this cell. Required. |
| `maxEvents` | `number` | `2` | Maximum number of events to show before overflow. |
| `moreLabel` | `(count: number) => string` | `"N more"` | Custom label for the overflow link. |
| `spanningRowCount` | `number` | `0` | Number of spanning event rows above this cell. |
### Agenda.MonthSpanningEvent
A multi-day event bar in the month grid, positioned absolutely across cells.
| Prop | Type | Default | Description |
| ---------- | ------------- | ------- | -------------------------------- |
| `event` | `AgendaEvent` | — | The event data. Required. |
| `colStart` | `number` | — | Column start index (0-based). |
| `colSpan` | `number` | — | Number of columns to span. |
| `row` | `number` | — | Row index for vertical stacking. |
### Agenda.MonthEvent
A per-cell event in the month grid. Supports drag-to-move across cells.
| Prop | Type | Default | Description |
| ------- | ------------- | ------- | ------------------------- |
| `event` | `AgendaEvent` | — | The event data. Required. |
# Carousel
**Category**: react
**URL**: https://heroui.pro/docs/react/components/carousel
> A content browsing component for navigating through a collection of images or items, with thumbnails, dots, and navigation controls.
## Usage
## Anatomy
Import the Carousel component and access all parts using dot notation.
`Carousel` is a subpath-only import (`@heroui-pro/react/carousel`) because it depends on the optional `embla-carousel` and `embla-carousel-react` peers. It is not exported from the package root, so SSR apps that don't use it never need those peers installed.
```tsx
import {Carousel} from "@heroui-pro/react/carousel";
```
## Modal Type
The modal type positions navigation arrows outside the content area, ideal for focused overlay-style viewing.
## Multiple Slides
Show multiple slides per viewport using Tailwind `basis` utility classes on `Carousel.Item`.
## Infinite Loop
Enable infinite looping with `opts={{ loop: true }}`.
## Autoplay
Use the `embla-carousel-autoplay` plugin via the `plugins` prop.
## API Access
Use the `setApi` prop to get the Embla API instance for programmatic control.
## CSS Classes
### Base Classes
* `.carousel` — Root wrapper. Sets `--carousel-gap` for slide spacing.
* `.carousel__viewport-wrapper` — Relative positioning context for navigation buttons.
* `.carousel__viewport` — Overflow-hidden container that clips off-screen slides.
* `.carousel__content` — Flex container holding all slide items.
* `.carousel__item` — Individual slide. Min-width zero, flex-shrink zero, full basis by default.
### Type Modifier Classes
* `.carousel--in-place` — Default type. Navigation arrows positioned inside the viewport area.
* `.carousel--modal` — Overlay-style layout. Arrows positioned far outside the content, flex column with gap.
* `.carousel--miniatures` — Compact layout. Arrows inline with the thumbnail row.
### Navigation Button Classes
* `.carousel__previous` / `.carousel__next` — Absolute-positioned containers for HeroUI Button (variant `tertiary`, size `sm`, icon-only).
* `.carousel__previous--in-place` / `.carousel__next--in-place` — Vertically centered inside the viewport, inset from edges.
* `.carousel__previous--modal` / `.carousel__next--modal` — Vertically centered, positioned outside the viewport bounds.
* `.carousel__previous--miniatures` / `.carousel__next--miniatures` — Relative positioning (inline with thumbnails).
### Dot Indicator Classes
* `.carousel__dots` — Flex container for pagination dots, centered with gap.
* `.carousel__dot` — Individual dot. `bg-default` by default, `bg-accent` when selected. Theme-aware border-radius.
### Thumbnail Classes
* `.carousel__thumbnails` — Flex container for thumbnail navigation. Centered with gap.
* `.carousel__thumbnails--miniatures` — Removes top margin for miniatures type.
* `.carousel__thumbnail` — Individual thumbnail button. `size-16`, `rounded-2xl`. Selected state uses `box-shadow` ring with accent color (no layout shift).
### Interactive States
* **Hover**: `[data-hovered="true"]` on `.carousel__previous` / `.carousel__next` — applies `bg-default-hover`.
* **Pressed**: `[data-pressed="true"]` on `.carousel__previous` / `.carousel__next` — applies `bg-default-hover`.
* **Disabled**: `[aria-disabled="true"]` on `.carousel__previous` / `.carousel__next` — applies disabled opacity.
* **Focus visible**: `[data-focus-visible="true"]` on buttons, dots, and thumbnails — applies focus ring.
* **Dot selected**: `[data-selected="true"]` on `.carousel__dot` — applies `bg-accent`.
* **Thumbnail selected**: `[data-selected="true"]` on `.carousel__thumbnail` — applies accent `box-shadow` ring.
* **Thumbnail hover**: `[data-hovered="true"]` on `.carousel__thumbnail` — applies `opacity: 0.85`.
* **Thumbnail pressed**: `[data-pressed="true"]` on `.carousel__thumbnail` — applies `scale(0.95)`.
* **Reduced motion**: `prefers-reduced-motion: reduce` disables all thumbnail transitions.
### CSS Variables
* `--carousel-gap` — Spacing between slides (default: `calc(var(--spacing) * 4)`).
## API Reference
### Carousel
The root container. Sets up Embla Carousel and provides context to all subcomponents.
| Prop | Type | Default | Description |
| --------- | --------------------------------------- | ------------ | -------------------------------------------------------------------------------------- |
| `opts` | `EmblaOptionsType` | — | Embla Carousel options. See [Embla docs](https://www.embla-carousel.com/api/options/). |
| `plugins` | `EmblaPluginType[]` | — | Embla Carousel plugins. See [Embla plugins](https://www.embla-carousel.com/plugins/). |
| `type` | `"in-place" \| "modal" \| "miniatures"` | `"in-place"` | Layout type controlling navigation button positioning. |
| `setApi` | `(api: EmblaCarouselType) => void` | — | Callback to receive the Embla API instance for programmatic control. |
Also supports all HTML `div` props.
### Carousel.Content
The scrollable slide container. Renders the Embla viewport wrapper and flex content area.
Also supports all HTML `div` props.
### Carousel.Item
An individual slide. Set `className="basis-1/3"` (or similar) to show multiple slides per viewport.
Also supports all HTML `div` props.
### Carousel.Previous
Navigation button to scroll to the previous slide. Automatically disabled when at the start (unless looping).
| Prop | Type | Default | Description |
| ------ | ----------- | ------- | ------------------------------------------- |
| `icon` | `ReactNode` | — | Custom icon to replace the default chevron. |
Also supports all HTML `button` props.
### Carousel.Next
Navigation button to scroll to the next slide. Automatically disabled when at the end (unless looping).
| Prop | Type | Default | Description |
| ------ | ----------- | ------- | ------------------------------------------- |
| `icon` | `ReactNode` | — | Custom icon to replace the default chevron. |
Also supports all HTML `button` props.
### Carousel.Dots
Pagination dot indicators. Renders one dot per scroll snap. Automatically hidden when there is only one snap point.
| Prop | Type | Default | Description |
| ----------- | -------------------------------------------------------------- | ------- | ------------------------------------ |
| `renderDot` | `(props: { index: number; isSelected: boolean }) => ReactNode` | — | Custom render function for each dot. |
Also supports all HTML `div` props.
### Carousel.Thumbnails
Container for thumbnail navigation buttons. Renders as a `tablist`.
Also supports all HTML `div` props.
### Carousel.Thumbnail
An individual thumbnail button linked to a slide index. Clicking navigates the carousel to that slide.
| Prop | Type | Default | Description |
| ------- | -------- | ------- | -------------------------------------------------------------------- |
| `index` | `number` | — | The slide index this thumbnail navigates to (0-based). Required. |
| `src` | `string` | — | Image source URL. Alternatively, pass `children` for custom content. |
| `alt` | `string` | `""` | Alt text for the thumbnail image. |
Also supports all HTML `button` props.
### useCarousel
A hook to access the carousel context from any descendant component.
```tsx
const { api, selectedIndex, scrollSnapCount, canScrollPrev, canScrollNext, scrollPrev, scrollNext, scrollTo } = useCarousel();
```
**Returns:**
| Property | Type | Description |
| ----------------- | -------------------------------- | --------------------------------------- |
| `api` | `EmblaCarouselType \| undefined` | The Embla API instance. |
| `selectedIndex` | `number` | Currently active slide index. |
| `scrollSnapCount` | `number` | Total number of scroll snap points. |
| `canScrollPrev` | `boolean` | Whether scrolling backward is possible. |
| `canScrollNext` | `boolean` | Whether scrolling forward is possible. |
| `scrollPrev` | `() => void` | Scroll to the previous slide. |
| `scrollNext` | `() => void` | Scroll to the next slide. |
| `scrollTo` | `(index: number) => void` | Scroll to a specific slide by index. |
# Data Grid
**Category**: react
**URL**: https://heroui.pro/docs/react/components/data-grid
> A full-featured data grid with sorting, selection, column resizing, pinned columns, drag-and-drop row reorder, virtualization, and async loading — built on the HeroUI Table.
## Usage
The `DataGrid` component takes a flat `data` array, a `columns` definition, and a `getRowId` function. It renders a fully accessible table with built-in support for sorting, selection, column resizing, and more.
## Anatomy
Import the `DataGrid` component — it is not a compound component and has no `DataGrid.*` sub-parts.
```tsx
import {DataGrid} from "@heroui-pro/react";
```
## Column Definitions
Columns are defined as an array of `DataGridColumn` objects. Each column has an `id`, a `header`, and either an `accessorKey` (to read a value from the row object) or a custom `cell` renderer.
```tsx
import type {DataGridColumn} from "@heroui-pro/react";
interface Payment {
id: string;
customer: string;
amount: number;
status: "succeeded" | "failed";
}
const columns: DataGridColumn[] = [
{
id: "customer",
header: "Customer",
accessorKey: "customer",
isRowHeader: true,
allowsSorting: true,
},
{
id: "amount",
header: "Amount",
accessorKey: "amount",
align: "end",
cell: (item) => `$${item.amount.toFixed(2)}`,
},
{
id: "status",
header: "Status",
accessorKey: "status",
},
];
```
### Custom Cell Rendering
The `cell` function receives the full row item and column definition. Return any `ReactNode`:
```tsx
{
id: "status",
header: "Status",
cell: (item) => (
{item.status}
),
}
```
### Custom Header Rendering
The `header` property accepts a string, a `ReactNode`, or a render function that receives `{ sortDirection }` for sortable columns:
```tsx
{
id: "amount",
header: ({ sortDirection }) => (
Amount {sortDirection === "ascending" ? "↑" : sortDirection === "descending" ? "↓" : ""}
),
allowsSorting: true,
}
```
## Row Selection
Enable row selection with `selectionMode` and `showSelectionCheckboxes`. Supports both `"single"` and `"multiple"` modes with controlled or uncontrolled state.
```tsx
const [selectedKeys, setSelectedKeys] = useState(new Set());
item.id}
selectionMode="multiple"
showSelectionCheckboxes
selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys}
/>
```
## Sorting
Mark columns as sortable with `allowsSorting: true`. In uncontrolled mode, the DataGrid sorts data client-side using locale-aware string comparison (or a custom `sortFn`). For server-side sorting, pass a controlled `sortDescriptor` and handle `onSortChange`.
### Uncontrolled (client-side)
```tsx
item.id}
defaultSortDescriptor={{ column: "customer", direction: "ascending" }}
/>
```
### Controlled (server-side)
```tsx
const [sortDescriptor, setSortDescriptor] = useState({
column: "date",
direction: "descending",
});
item.id}
sortDescriptor={sortDescriptor}
onSortChange={setSortDescriptor}
/>
```
### Custom Sort Function
Provide a `sortFn` on a column for custom comparison logic:
```tsx
{
id: "priority",
header: "Priority",
allowsSorting: true,
sortFn: (a, b) => priorityOrder[a.priority] - priorityOrder[b.priority],
}
```
## Column Resizing
Enable column resizing with `allowsColumnResize` on the DataGrid and `allowsResizing` on individual columns. Columns must have `minWidth` set.
```tsx
item.id}
allowsColumnResize
onColumnResize={(widths) => console.log("Resizing:", widths)}
onColumnResizeEnd={(widths) => console.log("Final widths:", widths)}
/>
```
## Pinned Columns
Pin columns to the start or end edge so they stay visible during horizontal scroll. Pinned columns must have a numeric `width` or `minWidth`.
```tsx
const columns: DataGridColumn[] = [
{
id: "name",
header: "Company",
pinned: "start",
minWidth: 160,
// ...
},
// ... scrollable columns ...
{
id: "actions",
header: "",
pinned: "end",
width: 50,
cell: (item) => ,
},
];
```
## Drag and Drop
Enable row reorder with the `onReorder` callback. The DataGrid provides built-in drag handles, keyboard support (Enter to grab, arrows to move, Enter to drop), and fires the callback with the reordered data array.
```tsx
const [tasks, setTasks] = useState(initialTasks);
item.id}
onReorder={(event) => setTasks(event.reorderedData)}
/>
```
For advanced drag-and-drop scenarios (cross-list, custom drag items), pass `dragAndDropHooks` directly from RAC's `useDragAndDrop`.
## Expandable Rows
Render hierarchical data by providing a `getChildren` function. The DataGrid recursively renders child rows, auto-generates a chevron toggle in the `treeColumn`, and indents each nested level by `treeIndent` pixels.
```tsx
interface FileRow {
id: string;
name: string;
type: "Folder" | "File";
children?: FileRow[];
}
const [expandedKeys, setExpandedKeys] = useState>(new Set(["1"]));
item.id}
getChildren={(item) => item.children}
treeColumn="name"
expandedKeys={expandedKeys}
onExpandedChange={setExpandedKeys}
/>
```
The `treeColumn` prop specifies which column displays the chevron. If omitted, it defaults to the first `isRowHeader` column (or the first column). Use `defaultExpandedKeys` for uncontrolled expansion, or pair `expandedKeys` with `onExpandedChange` for controlled behavior. Set `treeIndent={0}` to disable automatic per-level indentation.
Expandable rows compose with selection, drag-and-drop, sorting, pinned columns, and column resizing.
## Editable Cells
Use the `cell` render function to embed any interactive component — text fields, selects, switches, number steppers, etc.
## Empty State
Provide a `renderEmptyState` function to display a custom empty state when `data` is empty.
```tsx
item.id}
renderEmptyState={() => (
No Projects Yet
Get started by creating your first project.
)}
/>
```
## Async Loading
Use `onLoadMore`, `isLoadingMore`, and `loadMoreContent` to implement infinite scroll loading. The DataGrid renders a sentinel row that triggers `onLoadMore` when it scrolls into view.
```tsx
item.id}
scrollContainerClassName="max-h-[400px] overflow-y-auto"
onLoadMore={hasMore ? handleLoadMore : undefined}
isLoadingMore={isLoading}
loadMoreContent={}
/>
```
## Virtualization
Enable row virtualization for large datasets (1,000+ rows) with the `virtualized` prop. Only visible rows are rendered to the DOM. You must set `rowHeight` and `headingHeight`.
```tsx
item.id}
contentClassName="h-[400px] min-w-[900px] overflow-auto"
rowHeight={58}
headingHeight={37}
/>
```
## Bulk Actions
Combine row selection with an `ActionBar` to provide bulk operations like export, archive, or delete.
## Users
A minimal user directory using the `"secondary"` variant with `accessorKey`-only columns and a row action link — no selection, no sorting.
## Team Members
A full-featured HR table with controlled sorting, multi-selection, pinned columns, column resizing, column visibility toggling, search, filters, and client-side pagination.
## Servers
A server monitoring dashboard with controlled sorting, selection, column visibility toggling, search, status filtering, and rich cell renderers including sparkline charts and circular progress indicators.
## CSS Classes
### Base Classes
* `.data-grid` — Root wrapper. Sets `position: relative` and `width: 100%`. Defines `--data-grid-selection-column-width` and `--data-grid-drag-handle-column-width` custom properties.
### Element Classes
* `.data-grid__selection-column` — Narrow `