## Dashboard App
URL: /ui/docs/apps/dashboard-app/index
# Dashboard App
This [Dashboard App](https://ui-example-nativewind.vercel.app/) is built using `gluestack-ui v2`.
GitHub link for this [Dashboard App](https://github.com/gluestack/ui-example-nativewind).
---
## Kitchensink App
URL: /ui/docs/apps/kitchensink-app/index
# Kitchensink App
This [Kitchensink App](https://kitchen-sink-gluestack.vercel.app/) is built using `gluestack-ui v5`.
GitHub link for this app.
---
## Starter Kit
URL: /ui/docs/apps/starter-kit/index
# Starter Kit
These Starter Kits are built using `gluestack-ui v5`.
## Expo
---
## Next.js
---
GitHub link for and
---
## Todo App
URL: /ui/docs/apps/todo-app/index
# Todo App
This [Todo App](https://gluestack-ui-todo-example-app-git-main-gluestack.vercel.app/) is built using `gluestack-ui v2`.
### TodoApp.tsx
```tsx
const TodoApp = () => {
const [item, setItem] = useState('');
const [todos, setTodos] = useState(defaultTodos);
const addTodo = (task: string) => {
const lastTodo = todos[todos?.length - 1];
if (lastTodo?.task !== '' && task !== '') {
setTodos([
...todos,
{
id: nanoid(),
task: task,
completed: false,
},
]);
setItem('');
}
};
const toggleTodo = (id: string) => {
const updatedTodos = todos?.map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed;
}
return todo;
});
setTodos(updatedTodos);
};
const deleteTodo = (id: string) => {
const updatedTodos = todos.filter((todo) => todo.id !== id);
setTodos(updatedTodos);
};
return (
setItem(value)}
onSubmitEditing={() => addTodo(item)}
/>
addTodo(item)}>
{todos?.map((todo: Todo, index: number) => (
))}
);
};
export default TodoApp;
```
### TodoContainer.tsx
```tsx
export interface Todo {
id: string;
task: string;
completed: boolean;
}
const TodoContainer = ({
todo,
toggleTodo,
deleteTodo,
...props
}: {
todo: Todo;
toggleTodo: (id: string) => void;
deleteTodo: (id: string) => void;
}) => {
return (
toggleTodo(todo.id)}
size="sm"
aria-label={todo.task}
value={todo.task}
isChecked={todo.completed}
className="pl-6 py-2 flex-1"
>
{todo.task}
deleteTodo(todo.id)}>
{({ hovered }: { hovered: boolean }) => {
return (
);
}}
);
};
export default TodoContainer;
```
---
## Accordion
URL: /ui/docs/components/accordion/index
# Accordion
Explore gluestack's Accordion component for Expo, next.js, React & React Native. Build sleek, interactive accordions with ease.
This is an illustration of **Accordion** component.
```jsx
function Example() {
return (
{({ isExpanded }) => {
return (
<>
How do I place an order?
>
)
}}
To place an order, simply select the products you want, proceed to
checkout, provide shipping and payment information, and finalize
your purchase.
{({ isExpanded }) => {
return (
<>
What payment methods do you accept?
>
)
}}
We accept all major credit cards, including Visa, Mastercard, and
American Express. We also support payments through PayPal.
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add accordion
```
**Manual:**
### Step 1: Install the following dependencies:
```bash
npm i @expo/html-elements react-native-reanimated
```
### Step 2: Copy and paste the following code into your project.
```jsx
'use client';
/** Styles */
const accordionStyle = tva({
base: 'w-full',
});
const accordionItemStyle = tva({
base: '',
});
const accordionTitleTextStyle = tva({
base: 'text-foreground font-medium flex-1 text-left text-sm',
});
const accordionIconStyle = tva({
base: 'text-muted-foreground fill-none h-4 w-4',
});
const accordionContentTextStyle = tva({
base: 'text-foreground text-sm font-normal',
});
const accordionHeaderStyle = tva({
base: 'py-2.5 m-0',
});
const accordionContentStyle = tva({
base: 'pb-4',
});
const accordionTriggerStyle = tva({
base: 'w-full flex-row justify-between items-center web:outline-none focus:outline-none data-[disabled=true]:opacity-40 data-[disabled=true]:cursor-not-allowed data-[focus-visible=true]:bg-background/10 gap-3',
});
const Header = (
Platform.OS === 'web' ? H3 : View
) as React.ComponentType;
const StyledUIIcon = styled(UIIcon, {
className: "style",
});
/** Creator */
const UIAccordion = createAccordion({
Root: View,
Item: View,
Header: Header,
Trigger: Pressable,
Icon: StyledUIIcon,
TitleText: Text,
ContentText: Text,
Content: View,
});
type IAccordionProps = React.ComponentPropsWithoutRef;
type IAccordionItemProps = React.ComponentPropsWithoutRef<
typeof UIAccordion.Item
>;
type IAccordionContentProps = React.ComponentPropsWithoutRef<
typeof UIAccordion.Content
>;
type IAccordionContentTextProps = React.ComponentPropsWithoutRef<
typeof UIAccordion.ContentText
>;
type IAccordionIconProps = React.ComponentPropsWithoutRef<
typeof UIAccordion.Icon
> & {
as?: React.ElementType;
height?: number;
width?: number;
};
type IAccordionHeaderProps = React.ComponentPropsWithoutRef<
typeof UIAccordion.Header
>;
type IAccordionTriggerProps = React.ComponentPropsWithoutRef<
typeof UIAccordion.Trigger
>;
type IAccordionTitleTextProps = React.ComponentPropsWithoutRef<
typeof UIAccordion.TitleText
>;
/** Components */
const Accordion = React.forwardRef<
React.ComponentRef,
IAccordionProps
>(({ className, ...props }, ref) => {
return (
);
});
const AccordionItem = React.forwardRef<
React.ComponentRef,
IAccordionItemProps
>(({ className, ...props }, ref) => {
return (
);
});
const AccordionContent = React.forwardRef<
React.ComponentRef,
IAccordionContentProps
>(function AccordionContent({ className, ...props }, ref) {
const { isExpanded } = React.useContext(AccordionItemContext);
return (
);
});
const AccordionContentText = React.forwardRef<
React.ComponentRef,
IAccordionContentTextProps
>(function AccordionContentText({ className, ...props }, ref) {
return (
);
});
const AccordionIcon = React.forwardRef<
React.ComponentRef,
IAccordionIconProps
>(function AccordionIcon({ className, ...props }, ref) {
const { isExpanded } = React.useContext(AccordionItemContext);
return (
);
});
const AccordionHeader = React.forwardRef<
React.ComponentRef,
IAccordionHeaderProps
>(function AccordionHeader({ className, ...props }, ref) {
return (
);
});
const AccordionTrigger = React.forwardRef<
React.ComponentRef,
IAccordionTriggerProps
>(function AccordionTrigger({ className, ...props }, ref) {
return (
);
});
const AccordionTitleText = React.forwardRef<
React.ComponentRef,
IAccordionTitleTextProps
>(function AccordionTitleText({ className, ...props }, ref) {
return (
);
});
Accordion.displayName = 'Accordion';
AccordionItem.displayName = 'AccordionItem';
AccordionHeader.displayName = 'AccordionHeader';
AccordionTrigger.displayName = 'AccordionTrigger';
AccordionTitleText.displayName = 'AccordionTitleText';
AccordionContentText.displayName = 'AccordionContentText';
AccordionIcon.displayName = 'AccordionIcon';
AccordionContent.displayName = 'AccordionContent';
export {
Accordion, AccordionContent, AccordionContentText, AccordionHeader, AccordionIcon, AccordionItem, AccordionTitleText, AccordionTrigger
};
```
### Step 3: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```ts
```
```ts
export default () => (
);
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Accordion
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
<>
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | "single" | "multiple" | "single" | Determines whether one or multiple items can be opened at the same time. |
| `isCollapsible` | boolean | true | When type is "single" or "multiple", allows closing content when clicking trigger for an open item. |
| `defaultValue` | string[] | [] | The value of the item to expand when initially rendered when type is "single" or "multiple". |
| `value` | string[] | [] | The controlled value of the item to expand when type is "single" or "multiple". |
| `onValueChange` | function | - | Event handler called when the expanded state of an item changes and type is "single" or "multiple". |
| `isDisabled` | boolean | false | When true, prevents the user from interacting with the accordion and all its items. |
>
#### AccordionItem
Contains all the parts of a collapsible section.
<>
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | - | The controlled value of the item to expand when type is "single" or "multiple". Must be used in conjunction with onValueChange. This is a mandatory prop. |
| `isDisabled` | boolean | false | When true, prevents the user from interacting with the accordion and all its items. |
>
#### AccordionHeader
Wraps an `Accordion.Trigger`. It inherits all the properties of @expo/html-elements's [H3](https://www.npmjs.com/package/@expo/html-elements#h3) on web and It inherits all the properties of react native's [View](https://reactnative.dev/docs/view) on native. Use the as prop to update it to the appropriate heading level for your page.
#### AccordionTrigger
Toggles the collapsed state of its associated item. It inherits all the properties of react native's [Pressable](https://reactnative.dev/docs/pressable). It should be nested inside of an `Accordion.Header`.
#### AccordionTitleText
It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.
#### AccordionIcon
Contains all Icon related layout style props and actions.It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### AccordionContent
Contains all the collapsible content for an item. It inherits all the properties of React Native [View](https://reactnative.dev/docs/view) component.
#### AccordionContentText
It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.
### Accessibility
Adheres to the Accordion [WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/examples/accordion/).
We have outlined the various features that ensure the Accordion component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards.
- Header is h3 tag on web.
- aria-expanded is "true" when the Accordion Content is visible, otherwise false.
- role is set to "region" for the currently expanded accordion panel.
- aria-controls points to the id of the Accordion Content.
- aria-labelledby references the accordion header button that expands and collapses the region.
### Keyboard Interactions
- `Space` - When focus is on an Accordion.Trigger of a collapsed section, expands the section.
- `Enter` - When focus is on an Accordion.Trigger of a collapsed section, expands the section.
- `Tab` - Moves focus to the next focusable element.
- `Shift + Tab` - Moves focus to the previous focusable element.
### Screen Reader
- VoiceOver: When the Accordion Item is focused, the screen reader will announce the Accordion's title text and the state of the Accordion trigger (expanded or collapsed).
### Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs. Simply copy the code and integrate it into your project.
#### Customized Component
The following example demonstrates how easily you can customize the Accordion component to suit your needs.
```jsx
function App(){
return (
{({ isExpanded }) => {
return (
<>
What does the "type" prop of the Accordion component do?
>
);
}}
The type prop determines whether one or multiple items can be
opened at the same time. The default value is "single" which means
only one item can be opened at a time.
{({ isExpanded }) => {
return (
<>
Can I disable the whole accordion?
>
);
}}
Yes, you can disable the whole accordion by setting the isDisabled
prop to true on the Accordion component.
{({ isExpanded }) => {
return (
<>
What is a controlled accordion? How can I make it controlled?
>
);
}}
Controlled components refer to the components where the state and behaviors are controlled by the Parent component. You can make the accordion a controlled component by passing the value prop to the Accordion component and setting the onValueChange prop to update the value prop. Refer to the controlled accordion example in the docs.
);
}`
```
#### Rounded corners
The borderRadius prop can be used to create rounded corners for both the Accordion and AccordionItem components.
```jsx
function App(){
return (
{({ isExpanded }) => {
return (
<>
{isExpanded ? (
) : (
)}
How do I place an order?
>
);
}}
To place an order, simply select the products you want, proceed to
checkout, provide shipping and payment information, and finalize
your purchase.
{({ isExpanded }) => {
return (
<>
{isExpanded ? (
) : (
)}
What payment methods do you accept?
>
);
}}
We accept all major credit cards, including Visa, Mastercard, and
American Express. We also support payments through PayPal.
);
}`
```
#### Disabled item
You can disable an item by setting the isDisabled prop to true. This will prevent the user from interacting with the item and its content. You can also disable the whole accordion by setting the isDisabled prop to true on the Accordion component.
```jsx
function App(){
return (
{({ isExpanded }) => {
return (
<>
Disabled Item
{isExpanded ? (
) : (
)}
>
);
}}
This is a Disabled Item.
{({ isExpanded }) => {
return (
<>
Is this accordion accessible?
{isExpanded ? (
) : (
)}
>
);
}}
Yes, the accordion is accessible. It adheres to the WAI-ARIA design
pattern. You can read more about it in the accessibility section of
the docs.
);
}`
```
#### Default value
Use the defaultValue prop to define the open item by default.
```jsx
function App(){
return (
{({ isExpanded }) => {
return (
<>
What is the defaultValue prop of the Accordion component?
{isExpanded ? (
) : (
)}
>
);
}}
The defaultValue prop of the Accordion component is used to define
the open item by default. It is used when the Accordion component is
uncontrolled.
{({ isExpanded }) => {
return (
<>
How many size variants does the Accordion component have?
{isExpanded ? (
) : (
)}
>
);
}}
The Accordion component has three size variants - sm, md and lg.
{({ isExpanded }) => {
return (
<>
Can I nest my accordions?
{isExpanded ? (
) : (
)}
>
);
}}
Yes, you can nest your accordions. Refer to the nested accordion example in the docs.
);
}`
```
#### Nested Components
You can nest Accordion components to create a nested accordion. This is useful when you want to group related content together. In the following example, we have created a nested accordion to group the states of USA by region.
```jsx
function App(){
return (
{({ isExpanded }) => (
<>
{isExpanded ? (
) : (
)}
USA
>
)}
{({ isExpanded }) => (
<>
{isExpanded ? (
) : (
)}
California
>
)}
Capital city of California is Sacramento. California has a GDP
of 2.89 trillion dollars and follows Pacific Standard Time
zone.
{({ isExpanded }) => (
<>
{isExpanded ? (
) : (
)}
Nevada
>
)}
Nevada is located in a mountainous region that includes vast
semiarid grasslands and sandy alkali deserts. It is the most
arid state of the country.
);
}`
```
#### Controlled Accordion
A component is controlled when it's managed by its parent using props. You can make the Accordion component controlled by passing the value prop to the Accordion and setting the onValueChange to update the value prop. In the following example, we have created a controlled accordion to display the expanded state of the accordion.
```jsx
function App(){
const [selectedValues, setSelectedValues] = React.useState(['item-1', 'item-2']);
return (
setSelectedValues(item)} className="m-5 w-[95%]">
{({ isExpanded }) => {
return (
<>
Exploring Nature's Wonders
{isExpanded ? (
) : (
)}
>
);
}}
Embark on a journey through the breathtaking landscapes and diverse ecosystems of our planet. From majestic mountains to serene oceans, discover the beauty that nature has to offer.
{({ isExpanded }) => {
return (
<>
The Art of Culinary Delights
{isExpanded ? (
) : (
)}
>
);
}}
Indulge your senses in a culinary adventure. Uncover the secrets behind delectable dishes, learn about unique flavor profiles, and ignite your passion for cooking.
{({ isExpanded }) => {
return (
<>
Mastering the Creative Process
{isExpanded ? (
) : (
)}
>
);
}}
Immerse yourself in the world of creativity. Unleash your artistic potential, whether it's through writing, painting, or any other form of expression.
);
}`
```
---
## ActionSheet
URL: /ui/docs/components/actionsheet/index
# ActionSheet
Discover the ActionSheet component for Expo, React & React Native. Easily create intuitive action sheets in your app with gluestack-ui. Learn more in our detailed documentation!
This is an illustration of **ActionSheet** component.
```jsx
function App() {
const [showActionsheet, setShowActionsheet] = React.useState(false)
const handleClose = () => setShowActionsheet(false)
return (
<>
Edit MessageMark UnreadRemind MeAdd to Saved ItemsDelete
>
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add actionsheet
```
**Manual:**
### Step 1: Install the following dependencies:
```bash
npm i @expo/html-elements @legendapp/motion
```
### Step 2: Copy and paste the following code into your project.
```jsx
'use client';
const ItemWrapper = React.forwardRef<
React.ComponentRef,
PressableProps
>(function ItemWrapper({ ...props }, ref) {
return ;
});
type IMotionViewProps = React.ComponentProps &
MotionComponentProps;
const MotionView = Motion.View as React.ComponentType;
type IAnimatedPressableProps = React.ComponentProps &
MotionComponentProps;
const AnimatedPressable = createMotionAnimatedComponent(
Pressable
) as React.ComponentType;
const StyledUIIcon = styled(UIIcon, { className: "style" });
export const UIActionsheet = createActionsheet({
Root: View,
Content: MotionView,
Item: ItemWrapper,
ItemText: Text,
DragIndicator: View,
IndicatorWrapper: View,
Backdrop: AnimatedPressable,
ScrollView: ScrollView,
VirtualizedList: VirtualizedList,
FlatList: FlatList,
SectionList: SectionList,
SectionHeaderText: H4,
Icon: StyledUIIcon,
AnimatePresence: AnimatePresence,
});
const actionsheetStyle = tva({ base: 'w-full h-full web:pointer-events-none' });
const actionsheetContentStyle = tva({
base: 'items-center rounded-t-lg p-4 bg-background web:pointer-events-auto web:select-none border-t border-border dark:border-border/10 max-h-[80vh] pb-safe',
});
const actionsheetItemStyle = tva({
base: 'w-full flex-row items-center p-3 rounded-sm data-[disabled=true]:opacity-40 data-[disabled=true]:web:pointer-events-auto data-[disabled=true]:web:cursor-not-allowed data-[hover=true]:bg-accent data-[active=true]:bg-accent data-[focus=true]:bg-accent web:data-[focus-visible=true]:bg-accent gap-2',
});
const actionsheetItemTextStyle = tva({
base: 'text-foreground font-normal text-sm',
variants: {
isTruncated: {
true: '',
},
bold: {
true: 'font-bold',
},
underline: {
true: 'underline',
},
strikeThrough: {
true: 'line-through',
},
},
});
const actionsheetDragIndicatorStyle = tva({
base: 'w-[100px] h-2 bg-muted rounded-full',
});
const actionsheetDragIndicatorWrapperStyle = tva({
base: 'w-full py-1 items-center',
});
const actionsheetBackdropStyle = tva({
base: 'absolute left-0 top-0 right-0 bottom-0 bg-[#000]/50 web:cursor-default web:pointer-events-auto',
});
const actionsheetScrollViewStyle = tva({
base: 'w-full h-auto',
});
const actionsheetVirtualizedListStyle = tva({
base: 'w-full h-auto',
});
const actionsheetFlatListStyle = tva({
base: 'w-full h-auto',
});
const actionsheetSectionListStyle = tva({
base: 'w-full h-auto',
});
const actionsheetSectionHeaderTextStyle = tva({
base: 'leading-5 font-semibold my-0 text-muted-foreground p-3 uppercase text-xs',
variants: {
isTruncated: {
true: '',
},
bold: {
true: 'font-bold',
},
underline: {
true: 'underline',
},
strikeThrough: {
true: 'line-through',
},
sub: {
true: 'text-xs',
},
italic: {
true: 'italic',
},
highlight: {
true: 'bg-yellow-500',
},
},
});
const actionsheetIconStyle = tva({
base: 'text-foreground fill-none h-4 w-4',
});
type IActionsheetProps = VariantProps &
React.ComponentPropsWithoutRef;
type IActionsheetContentProps = VariantProps &
React.ComponentPropsWithoutRef & {
className?: string;
};
type IActionsheetItemProps = VariantProps &
React.ComponentPropsWithoutRef;
type IActionsheetItemTextProps = VariantProps &
React.ComponentPropsWithoutRef;
type IActionsheetDragIndicatorProps = VariantProps<
typeof actionsheetDragIndicatorStyle
> &
React.ComponentPropsWithoutRef;
type IActionsheetDragIndicatorWrapperProps = VariantProps<
typeof actionsheetDragIndicatorWrapperStyle
> &
React.ComponentPropsWithoutRef;
type IActionsheetBackdropProps = VariantProps &
React.ComponentPropsWithoutRef & {
className?: string;
};
type IActionsheetScrollViewProps = VariantProps<
typeof actionsheetScrollViewStyle
> &
React.ComponentPropsWithoutRef;
type IActionsheetVirtualizedListProps = VariantProps<
typeof actionsheetVirtualizedListStyle
> &
React.ComponentPropsWithoutRef;
type IActionsheetFlatListProps = VariantProps &
React.ComponentPropsWithoutRef;
type IActionsheetSectionListProps = VariantProps<
typeof actionsheetSectionListStyle
> &
React.ComponentPropsWithoutRef;
type IActionsheetSectionHeaderTextProps = VariantProps<
typeof actionsheetSectionHeaderTextStyle
> &
React.ComponentPropsWithoutRef;
type IActionsheetIconProps = VariantProps &
React.ComponentPropsWithoutRef & {
className?: string;
as?: React.ElementType;
height?: number;
width?: number;
};
const Actionsheet = React.forwardRef<
React.ComponentRef,
IActionsheetProps
>(function Actionsheet({ className, ...props }, ref) {
return (
);
});
const ActionsheetContent = React.forwardRef<
React.ComponentRef,
IActionsheetContentProps
>(function ActionsheetContent({ className, ...props }, ref) {
return (
);
});
const ActionsheetItem = React.forwardRef<
React.ComponentRef,
IActionsheetItemProps
>(function ActionsheetItem({ className, ...props }, ref) {
return (
);
});
const ActionsheetItemText = React.forwardRef<
React.ComponentRef,
IActionsheetItemTextProps
>(function ActionsheetItemText(
{ isTruncated, bold, underline, strikeThrough, className, ...props },
ref
) {
return (
);
});
const ActionsheetDragIndicator = React.forwardRef<
React.ComponentRef,
IActionsheetDragIndicatorProps
>(function ActionsheetDragIndicator({ className, ...props }, ref) {
return (
);
});
const ActionsheetDragIndicatorWrapper = React.forwardRef<
React.ComponentRef,
IActionsheetDragIndicatorWrapperProps
>(function ActionsheetDragIndicatorWrapper({ className, ...props }, ref) {
return (
);
});
const ActionsheetBackdrop = React.forwardRef<
React.ComponentRef,
IActionsheetBackdropProps
>(function ActionsheetBackdrop({ className, ...props }, ref) {
return (
);
});
const ActionsheetScrollView = React.forwardRef<
React.ComponentRef,
IActionsheetScrollViewProps
>(function ActionsheetScrollView({ className, ...props }, ref) {
return (
);
});
const ActionsheetVirtualizedList = React.forwardRef<
React.ComponentRef,
IActionsheetVirtualizedListProps
>(function ActionsheetVirtualizedList({ className, ...props }, ref) {
return (
);
});
const ActionsheetFlatList = React.forwardRef<
React.ComponentRef,
IActionsheetFlatListProps
>(function ActionsheetFlatList({ className, ...props }, ref) {
return (
);
});
const ActionsheetSectionList = React.forwardRef<
React.ComponentRef,
IActionsheetSectionListProps
>(function ActionsheetSectionList({ className, ...props }, ref) {
return (
);
});
const ActionsheetSectionHeaderText = React.forwardRef<
React.ComponentRef,
IActionsheetSectionHeaderTextProps
>(function ActionsheetSectionHeaderText(
{
className,
isTruncated,
bold,
underline,
strikeThrough,
sub,
italic,
highlight,
...props
},
ref
) {
return (
);
});
const ActionsheetIcon = React.forwardRef<
React.ComponentRef,
IActionsheetIconProps
>(function ActionsheetIcon({ className, height, width, ...props }, ref) {
if (typeof height === 'number' || typeof width === 'number') {
return (
);
}
return (
);
});
export {
Actionsheet, ActionsheetBackdrop, ActionsheetContent, ActionsheetDragIndicator,
ActionsheetDragIndicatorWrapper, ActionsheetFlatList, ActionsheetIcon, ActionsheetItem,
ActionsheetItemText, ActionsheetScrollView, ActionsheetSectionHeaderText, ActionsheetSectionList, ActionsheetVirtualizedList
};
```
### Step 3: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```ts
export default () => (
);
```
```ts
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Actionsheet
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `isOpen` | boolean | false | Whether the actionsheet is open. |
| `onClose` | () => void | - | Callback function when the actionsheet is closed. |
| `snapPoints` | number[] | - | Array of numbers representing the snap points in percentage. |
| `initialFocusRef` | React.RefObject<any> | - | Ref for the element that should be focused when the actionsheet opens. |
| `finalFocusRef` | React.RefObject<any> | - | Ref for the element that should be focused when the actionsheet closes. |
> Note: If snapPoints are not provided to Actionsheet, then it's essential to set maxHeight to ActionsheetContent.
#### ActionsheetBackdrop
It is React Native's [Pressable](https://reactnative.dev/docs/pressable) component, created using [@legendapp/motion's](https://legendapp.com/open-source/motion/) `createMotionAnimatedComponent` function to add animation to the component. You can use any declarative animation library you prefer.
#### ActionsheetContent
It inherits all the properties of [@legendapp/motion's](https://legendapp.com/open-source/motion/) [Motion.View](https://legendapp.com/open-source/motion/overview/) component. With this Actionsheet component, you have the flexibility to use any declarative animation library that suits your needs.
#### ActionsheetDragIndicatorWrapper
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### ActionsheetDragIndicator
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### ActionsheetItem
It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) component.
#### ActionsheetItemText
It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.
#### ActionsheetIcon
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### ActionsheetScrollView
It inherits all the properties of React Native's [ScrollView](https://reactnative.dev/docs/scrollview) component.
#### ActionsheetVirtualizedList
It inherits all the properties of React Native's [VirtualizedList](https://reactnative.dev/docs/virtualizedlist) component.
#### ActionsheetFlatList
It inherits all the properties of React Native's [FlatList](https://reactnative.dev/docs/flatlist) component.
#### ActionsheetSectionList
It inherits all the properties of React Native's [SectionList](https://reactnative.dev/docs/sectionlist) component.
#### ActionsheetSectionHeaderText
It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.
> Note: While our Actionsheet component supports both ActionsheetScrollView and ActionsheetVirtualizedList, we recommend using VirtualizedList for better performance on large lists of items. The ScrollView option may cause performance issues on lists with many items.
### Features
- Actionsheet has aria-modal set to true.
- Actionsheet has role set to dialog.
- When the Actionsheet opens, focus is trapped within it.
- Pressing Esc closes the Actionsheet
- Clicking on the overlay closes the Actionsheet
- Scrolling is blocked on the elements behind the Actionsheet
### Accessibility
We have outlined the various features that ensure the Actionsheet component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards.
#### Keyboard
- `Tab + Enter`: Triggers the actionsheet action.
### Screen Reader
- VoiceOver: When the ActionSheet is focused, the screen reader will announce the button name. After entering the ActionSheet, it will read the content of the ActionSheet items.
#### Keyboard
- `Space`: Opens the actionsheet.
- `Enter`: Opens/closes the actionsheet.
- `Tab`: Moves focus to the next focusable element.
- `Shift + Tab`: Moves focus to the previous focusable element.
- `Esc`: Closes the actionsheet.
### Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs. Simply copy the code and integrate it into your project.
#### Keyboard handling
Demonstrates a common UI pattern known as keyboard handling or keyboard scrolling, in which an input field is automatically scrolled into view when the user taps on it and the keyboard appears. This improves usability and ensures that the input field is always visible and accessible, even when the keyboard is blocking part of the screen.
#### Without SnapPoints
```jsx
function App() {
const [showActionsheet, setShowActionsheet] = React.useState(false)
const handleClose = () => setShowActionsheet(false)
return (
<>
Edit MessageMark UnreadRemind MeAdd to Saved ItemsDelete
>
)
}`
```
#### With SnapPoints
```jsx
function App() {
const [showActionsheet, setShowActionsheet] = React.useState(false);
const handleClose = () => setShowActionsheet(false);
return (
<>
MastercardCard ending in 2345
Confirm security code
>
);
}`
```
### Selection with State Persistence
This example demonstrates proper state handling when closing the actionsheet via drag gesture. Users can select a notification preference, and the selection is correctly saved whether they close by dragging or clicking the backdrop.
#### Selection with State Persistence
```jsx
function App() {
const [showActionsheet, setShowActionsheet] = React.useState(false);
const [preference, setPreference] = React.useState('all');
const [tempPreference, setTempPreference] = React.useState('all');
const handleClose = () => {
setPreference(tempPreference);
setShowActionsheet(false);
};
const options = [
{ value: 'all', label: 'All Notifications' },
{ value: 'mentions', label: 'Mentions Only' },
{ value: 'off', label: 'Off' },
];
return (
<>
Current: {options.find(o => o.value === preference)?.label}Preferences
{options.map((option) => (
{option.label}
))}
>
);
}`
```
#### Icons
The Actionsheet with Icons is a variation of the Actionsheet component that displays icons next to each option. It's commonly used in apps for a more visual representation of options and easier navigation.
```jsx
function App() {
const [showActionsheet, setShowActionsheet] = React.useState(false);
const handleClose = () => setShowActionsheet(false);
return (
<>
Edit MessageMark UnreadRemind MeAdd to Saved ItemsDelete
>
);
}`
```
#### VirtualizedList
The Actionsheet with Virtualized List includes a virtualized list for better performance when displaying a large number of options. It's commonly used in apps with long lists of options for an improved user experience.
```jsx
function App(){
const [showActionsheet, setShowActionsheet] = React.useState(false);
const handleClose = () => setShowActionsheet(false);
const data = React.useMemo(() => Array(50).fill(0).map((_, index) => 'Item' + index),[]);
const getItem = (_data, index) => ({
id: index,
title: _data[index],
});
const getItemCount = (_data) => _data.length;
const Item = React.useCallback(
({ title }) => (
{title}
),
[handleClose]
);
return (
<>
}
keyExtractor={(item) => item.id}
getItemCount={getItemCount}
getItem={getItem}
/>
>
);
}`
```
#### FlatList
The Actionsheet with Flat List is a variation of the Actionsheet component that displays a flat list of options. It's commonly used in apps for a simple and straightforward display of options to the user.
```jsx
function App(){
const [showActionsheet, setShowActionsheet] = React.useState(false);
const handleClose = () => setShowActionsheet(false);
const DATA = [
{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item'
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item'
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item'
},
];
const Item = React.useCallback(
({ title }) => (
{title}
),
[handleClose]
);
return (
<>
}
keyExtractor={(item) => item.id}
/>
>
);
}`
```
#### SectionList
The Actionsheet with SectionList is a variation of the Actionsheet component that displays a sectioned list of options. It's commonly used in apps to organize options into different categories or sections for better readability and navigation.
```jsx
function App(){
const [showActionsheet, setShowActionsheet] = React.useState(false);
const handleClose = () => setShowActionsheet(false);
const DATA = [
{
title: 'Gender',
data: ['Men', 'Women', 'Boy', 'Girl'],
},
];
return (
<>
item + index}
renderItem={({ item }) => (
{item}
)}
renderSectionHeader={({ section: { title, data } }) => (
{title} ({data.length})
)}
/>
>
);
}`
```
#### File Upload with Actionsheet
```jsx
function App(){
const [showActionsheet, setShowActionsheet] = React.useState(false);
const handleClose = () => setShowActionsheet(false);
return (
<>
Upload your latest resume
JPG, PDF, PNG supportedNo files uploaded yet
>
);
}`
```
---
## AlertDialog
URL: /ui/docs/components/alert-dialog/index
# AlertDialog
Build seamless React Native dialogs with the AlertDialog component. Enhance user engagement with smooth and responsive modal prompts.
This is an illustration of **AlertDialog** component.
```jsx
function Example() {
const [showAlertDialog, setShowAlertDialog] = React.useState(false)
const handleClose = () => setShowAlertDialog(false)
return (
<>
Are you sure you want to delete this post?
Deleting the post will remove it permanently and cannot be undone.
Please confirm if you want to proceed.
>
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add alert-dialog
```
**Manual:**
### Step 1: Install the following dependencies:
```bash
npm i react-native-reanimated
```
### Step 2: Copy and paste the following code into your project.
```tsx
'use client';
const SCOPE = 'ALERT_DIALOG';
const RootComponent = withStyleContext(View, SCOPE);
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
const AnimatedView = Animated.createAnimatedComponent(View);
const UIAccessibleAlertDialog = createAlertDialog({
Root: RootComponent,
Body: ScrollView,
Content: AnimatedView,
CloseButton: Pressable,
Header: View,
Footer: View,
Backdrop: AnimatedPressable,
});
const alertDialogStyle = tva({
base: 'group/modal w-full h-full justify-center items-center web:pointer-events-none',
parentVariants: {
size: {
xs: '',
sm: '',
md: '',
lg: '',
full: '',
},
},
});
const alertDialogContentStyle = tva({
base: 'bg-background rounded-lg overflow-hidden border border-border p-6',
parentVariants: {
size: {
xs: 'w-[60%] max-w-[360px]',
sm: 'w-[70%] max-w-[420px]',
md: 'w-[80%] max-w-[510px]',
lg: 'w-[90%] max-w-[640px]',
full: 'w-full',
},
},
});
const alertDialogCloseButtonStyle = tva({
base: 'group/alert-dialog-close-button z-10 rounded-sm p-2 data-[focus-visible=true]:bg-background/10 web:cursor-pointer outline-0',
});
const alertDialogHeaderStyle = tva({
base: 'justify-between items-center flex-row',
});
const alertDialogFooterStyle = tva({
base: 'flex-row justify-end items-center gap-3',
});
const alertDialogBodyStyle = tva({ base: '' });
const alertDialogBackdropStyle = tva({
base: 'absolute left-0 top-0 right-0 bottom-0 bg-black/50 web:cursor-default',
});
type IAlertDialogProps = React.ComponentPropsWithoutRef<
typeof UIAccessibleAlertDialog
> &
VariantProps;
type IAlertDialogContentProps = React.ComponentPropsWithoutRef<
typeof UIAccessibleAlertDialog.Content
> &
VariantProps & { className?: string };
type IAlertDialogCloseButtonProps = React.ComponentPropsWithoutRef<
typeof UIAccessibleAlertDialog.CloseButton
> &
VariantProps;
type IAlertDialogHeaderProps = React.ComponentPropsWithoutRef<
typeof UIAccessibleAlertDialog.Header
> &
VariantProps;
type IAlertDialogFooterProps = React.ComponentPropsWithoutRef<
typeof UIAccessibleAlertDialog.Footer
> &
VariantProps;
type IAlertDialogBodyProps = React.ComponentPropsWithoutRef<
typeof UIAccessibleAlertDialog.Body
> &
VariantProps;
type IAlertDialogBackdropProps = React.ComponentPropsWithoutRef<
typeof UIAccessibleAlertDialog.Backdrop
> &
VariantProps & { className?: string };
const AlertDialog = React.forwardRef<
React.ComponentRef,
IAlertDialogProps
>(function AlertDialog({ className, size = 'md', ...props }, ref) {
return (
);
});
const AlertDialogContent = React.forwardRef<
React.ComponentRef,
IAlertDialogContentProps
>(function AlertDialogContent({ className, size, ...props }, ref) {
const { size: parentSize } = useStyleContext(SCOPE);
return (
);
});
const AlertDialogCloseButton = React.forwardRef<
React.ComponentRef,
IAlertDialogCloseButtonProps
>(function AlertDialogCloseButton({ className, ...props }, ref) {
return (
);
});
const AlertDialogHeader = React.forwardRef<
React.ComponentRef,
IAlertDialogHeaderProps
>(function AlertDialogHeader({ className, ...props }, ref) {
return (
);
});
const AlertDialogFooter = React.forwardRef<
React.ComponentRef,
IAlertDialogFooterProps
>(function AlertDialogFooter({ className, ...props }, ref) {
return (
);
});
const AlertDialogBody = React.forwardRef<
React.ComponentRef,
IAlertDialogBodyProps
>(function AlertDialogBody({ className, ...props }, ref) {
return (
);
});
const AlertDialogBackdrop = React.forwardRef<
React.ComponentRef,
IAlertDialogBackdropProps
>(function AlertDialogBackdrop({ className, ...props }, ref) {
return (
);
});
AlertDialog.displayName = 'AlertDialog';
AlertDialogContent.displayName = 'AlertDialogContent';
AlertDialogCloseButton.displayName = 'AlertDialogCloseButton';
AlertDialogHeader.displayName = 'AlertDialogHeader';
AlertDialogFooter.displayName = 'AlertDialogFooter';
AlertDialogBody.displayName = 'AlertDialogBody';
AlertDialogBackdrop.displayName = 'AlertDialogBackdrop';
export {
AlertDialog,
AlertDialogBackdrop,
AlertDialogBody,
AlertDialogCloseButton,
AlertDialogContent,
AlertDialogFooter,
AlertDialogHeader
};
```
### Step 3: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```ts
```
```ts
export default () => (
export default () => (
);
);
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### AlertDialog
Contains all alert dialog related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
<>
| `isOpen` | boolean | false | If true, the alert-dialog will open. Useful for controllable state behavior. |
| --- | --- | --- | --- |
| `onClose` | () => any | - | Callback invoked when the alert-dialog is closed. |
| `useRNModal` | boolean | false | If true, renders react-native native modal. (Only works in react-native) |
| `defaultIsOpen` | boolean | false | Specifies the default open state of the AlertDialog |
| `initialFocusRef` | {`React.RefObject`} | - | The ref of element to receive focus when the alert-dialog opens. |
| `finalFocusRef` | {`React.RefObject`} | - | The ref of element to receive focus when the alert-dialog closes. |
| `avoidKeyboard` | boolean | - | If true, the AlertDialog will avoid the keyboard. |
| `closeOnOverlayClick` | boolean | true | If true, the AlertDialog will close when the overlay is clicked. |
| `isKeyboardDismissable` | boolean | true | If true, the keyboard can dismiss the AlertDialog |
>
#### AlertDialogBackdrop
It is React Native's [Pressable](https://reactnative.dev/docs/pressable#props) component, created using [react-native-reanimated's](https://docs.swmansion.com/react-native-reanimated/) `Animated.createAnimatedComponent` function to add animation to the component. You can use any declarative animation library you prefer.
#### AlertDialogContent
It is [react-native-reanimated's](https://docs.swmansion.com/react-native-reanimated/) [Animated.View](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/glossary#animatedview) component. You can use any declarative animation library you prefer.
#### AlertDialogCloseButton
It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) component.
#### AlertDialogHeader
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### AlertDialogBody
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### AlertDialogFooter
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component
### Features
- Keyboard support for actions.
### Accessibility
We have outlined the various features that ensure the AlertDialog component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards.It uses React Native ARIA [@react-native-aria/focus](https://react-native-aria.geekyants.com/) which follows the [WAI-ARIA Alert and Message Dialogs Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/).
#### Keyboard
- `Tab`:
- Moves focus to the next tabbable element inside the dialog.
- If focus is on the last tabbable element inside the dialog, moves focus to the first tabbable element inside the dialog.
- `Shift + Tab`:
- Moves focus to the previous tabbable element inside the dialog.
- If focus is on the first tabbable element inside the dialog, moves focus to the last tabbable element inside the dialog.
- `Escape`: Closes the dialog.
#### Screen Reader
- VoiceOver: When the button is focused, the screen reader will announce the button's label and provide instructions on how to click it. When the alert dialog is triggered, the screen reader will read its content.
#### States
- When the alert dialog is triggered, `aria-modal="true"` is added to the alert dialog content. This attribute informs assistive technologies that the windows underneath the current alert dialog are not available for interaction(inert).
### Props
AlertDialog component is created using View component from react-native. It extends all the props supported by [React Native View](https://reactnative.dev/docs/view#props), and the props mentioned below.
#### AlertDialog
<>
| Name | Value | Default |
| --- | --- | --- |
| `size` | xs | sm | md | lg | full | md |
>
### Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs. Simply copy the code and integrate it into your project.
#### AlertDialog with Image
```jsx
function Example() {
const [showAlertDialog, setShowAlertDialog] = React.useState(false);
const handleClose = () => setShowAlertDialog(false);
return (
<>
Get Additional Discount
Upgrade your plan before your trial ends yo get 5% discount. Use
code{' '}
#PRO005
>
);
}`
```
#### AlertDialog with icon + cta
```jsx
function Example() {
const [showAlertDialog, setShowAlertDialog] = React.useState(false);
const handleClose = () => setShowAlertDialog(false);
return (
<>
Cloud storage full!
You have used up all the storage you have.
>
);
}`
```
#### AlertDialog with Delete Option
```jsx
function Example() {
const [showAlertDialog, setShowAlertDialog] = React.useState(false);
const handleClose = () => setShowAlertDialog(false);
return (
<>
Delete account?
The invoice will be deleted from the invoices section and in
the documents folder. This cannot be undone.
>
);
}`
```
---
## Alert
URL: /ui/docs/components/alert/index
# Alert
gluestack-ui offers a responsive React Native Alert component with multiple styles. Easily integrate alerts into your UI with customizable React Native alert styles.
This is an illustration of **Alert** component.
```jsx
function Example() {
return (
Description of alert!
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add alert
```
**Manual:**
### Step 1: Copy and paste the following code into your project.
```tsx
'use client';
const SCOPE = 'ALERT';
const alertStyle = tva({
base: 'rounded-lg border px-2.5 py-2 flex-row gap-2 items-start ',
variants: {
variant: {
default: 'bg-card border-border',
destructive: 'bg-card border-destructive',
},
},
});
const alertTextStyle = tva({
base: 'font-medium tracking-tight text-sm flex-1',
parentVariants: {
variant: {
default: 'text-card-foreground',
destructive: 'text-destructive',
},
},
});
const alertIconStyle = tva({
base: 'fill-none w-4 h-4 mt-0.5',
parentVariants: {
variant: {
default: 'text-card-foreground',
destructive: 'text-destructive',
},
},
});
const StyledUIIcon = styled(UIIcon, {
className: "style",
});
export const UIAlert = createAlert({
Root: withStyleContext(View, SCOPE),
Text: Text,
Icon: StyledUIIcon,
});
type IAlertProps = Omit<
React.ComponentPropsWithoutRef,
'context'
> &
VariantProps;
const Alert = React.forwardRef, IAlertProps>(
function Alert({ className, variant = 'default', ...props }, ref) {
return (
);
}
);
type IAlertTextProps = React.ComponentPropsWithoutRef &
VariantProps;
const AlertText = React.forwardRef<
React.ComponentRef,
IAlertTextProps
>(function AlertText({ className, ...props }, ref) {
const { variant: parentVariant } = useStyleContext(SCOPE);
return (
);
});
type IAlertIconProps = React.ComponentPropsWithoutRef &
VariantProps & {
height?: number;
width?: number;
};
const AlertIcon = React.forwardRef<
React.ComponentRef,
IAlertIconProps
>(function AlertIcon({ className, ...props }, ref) {
const { variant: parentVariant } = useStyleContext(SCOPE);
return (
);
});
Alert.displayName = 'Alert';
AlertText.displayName = 'AlertText';
AlertIcon.displayName = 'AlertIcon';
export { Alert, AlertIcon, AlertText };
```
### Step 2: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```ts
```
```ts
export default () => (
);
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Alert
Contains all alert related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
<>
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | error | warning | success | info | muted | info | Determines the color scheme of the alert. |
| `variant` | solid | outline | solid | Determines the visual style of the alert. |
>
#### AlertIcon
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### AlertText
Contains all text related layout style props and actions. It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.
### Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs. Simply copy the code and integrate it into your project.
#### Alert with CTA
```jsx
function Example() {
return (
Verify your phone number to create an API key
);
}`
```
#### Alert on cloud sync
```jsx
function Example() {
return (
Your data has been synced to the cloud
);
}`
```
#### Warning alert
```jsx
function Example() {
return (
Heads up:
{" "} Once done, this action cannot be undone
);
}`
```
#### Alert on confirm password modal
```jsx
function Example() {
const [showPassword, setShowPassword] = React.useState(false);
const handleState = () => {
setShowPassword((showState) => {
return !showState;
});
};
return (
Confirm our password?johnsmith@gmail.com
Minimum 8 characters, with at least 1 uppercase, 1 lowercase, and 1
number required.
);
}`
```
---
## All Components
URL: /ui/docs/components/all-components/index
# All Components
30+ responsive components for every screen and style
---
## Avatar
URL: /ui/docs/components/avatar/index
# Avatar
Enhance your UI with our React Native Avatar component. Explore gluestack's-ui Avatar for seamless design and customization. Check out the docs to add an Avatar component to your app!
This is an illustration of **Avatar** component.
```jsx
function Example() {
return (
Jane Doe is test
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add avatar
```
**Manual:**
### Step 1: Copy and paste the following code into your project.
```tsx
'use client';
const SCOPE = 'AVATAR';
const UIAvatar = createAvatar({
Root: withStyleContext(View, SCOPE),
Badge: View,
Group: View,
Image: Image,
FallbackText: Text,
});
const avatarStyle = tva({
base: 'relative flex h-12 w-12 shrink-0 rounded-full bg-muted items-center justify-center group-[.avatar-group]/avatar-group:-ml-2.5',
});
const avatarFallbackTextStyle = tva({
base: 'text-foreground text-xs font-medium text-transform:uppercase',
});
const avatarGroupStyle = tva({
base: 'group/avatar-group flex-row-reverse relative avatar-group',
});
const avatarBadgeStyle = tva({
base: 'absolute h-3 w-3 rounded-full border-2 border-background right-0 bottom-0 bg-green-500',
});
const avatarImageStyle = tva({
base: 'h-full w-full rounded-full absolute',
});
type IAvatarProps = Omit<
React.ComponentPropsWithoutRef,
'context'
> &
VariantProps;
const Avatar = React.forwardRef<
React.ComponentRef,
IAvatarProps
>(function Avatar({ className, ...props }, ref) {
return (
);
});
type IAvatarBadgeProps = React.ComponentPropsWithoutRef &
VariantProps;
const AvatarBadge = React.forwardRef<
React.ComponentRef,
IAvatarBadgeProps
>(function AvatarBadge({ className, ...props }, ref) {
return (
);
});
type IAvatarFallbackTextProps = React.ComponentPropsWithoutRef<
typeof UIAvatar.FallbackText
> &
VariantProps;
const AvatarFallbackText = React.forwardRef<
React.ComponentRef,
IAvatarFallbackTextProps
>(function AvatarFallbackText({ className, ...props }, ref) {
return (
);
});
type IAvatarImageProps = React.ComponentPropsWithoutRef &
VariantProps;
const AvatarImage = React.forwardRef<
React.ComponentRef,
IAvatarImageProps
>(function AvatarImage({ className, ...props }, ref) {
return (
);
});
type IAvatarGroupProps = React.ComponentPropsWithoutRef &
VariantProps;
const AvatarGroup = React.forwardRef<
React.ComponentRef,
IAvatarGroupProps
>(function AvatarGroup({ className, ...props }, ref) {
return (
);
});
// Alias for shadcn compatibility
const AvatarFallback = AvatarFallbackText;
export {
Avatar,
AvatarBadge,
AvatarFallback,
AvatarFallbackText,
AvatarGroup,
AvatarImage
};
```
### Step 2: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```ts
```
> IOS: It is highly recommended to use `` before `` to avoid indexing issues in IOS.
```ts
export default () => (
);
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Avatar
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### AvatarGroup
It also inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### AvatarImage
It inherits all the properties of React Native's [Image](https://reactnative.dev/docs/image) component.
#### AvatarFallbackText
It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.
#### AvatarBadge
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
### Features
- Returns a fallback to FallbackText if given an invalid image source.
### Props
Avatar component is created using View component from react-native. It extends all the props supported by [React Native View](https://reactnative.dev/docs/view#props), [utility props](/ui/docs/styling/utility-and-sx-props) and the props mentioned below.
#### Avatar
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | xs | sm | md | lg | xl | 2xl | md | Determines the size of the avatar. |
### Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs. Simply copy the code and integrate it into your project.
#### Avatar with letters
An Avatar component with text displays a stylized representation of a user or entity alongside accompanying text, providing a visual and textual identification within a user interface.
```jsx
function Example() {
return (
Ronald RichardsRonald RichardsNursing AssistantArlene McCoyArlene McCoyMarketing Coordinator
);
}`
```
#### Avatar with Icon
An Avatar component with an icon combines a graphical symbol or icon with a user or entity representation, providing a visually appealing and easily recognizable identification within a user interface.
```jsx
function Example() {
return (
Ronald RichardsNursing AssistantKevin JamesWeb Designer
);
}`
```
#### Avatar with Image
An Avatar component with an image incorporates a user or entity representation using a profile picture or image, adding a personalized touch and visual identification within a user interface.
```jsx
function Example() {
return (
SSRonald RichardsNursing AssistantSSArlene McCoyMarketing Coordinator
);
}`
```
#### Avatar Group
The avatar group allows users to group avatars and display them in a horizontal or vertical row for better visual representation and functionality.
```jsx
function Example() {
const avatars = [
{ src: 'https://example.com.jpg', alt: 'Sandeep Srivastva', color:'bg-emerald-600' },
{ src: 'https://example.com.jpg', alt: 'Arjun Kapoor', color:'bg-cyan-600' },
{ src: 'https://example.com.jpg', alt: 'Ritik Sharma ', color:'bg-indigo-600' },
{ src: 'https://example.com.jpg', alt: 'Akhil Sharma', color:'bg-gray-600' },
{ src: 'https://example.com.jpg', alt: 'Rahul Sharma ', color:'bg-red-400' },
];
const extraAvatars = avatars.slice(3);
const remainingCount = extraAvatars.length;
return (
{avatars.slice(0, 3).map((avatar, index) => {
return (
{avatar.alt}
);
})}
{"+ " + remainingCount + ""}
);
}`
```
> To reverse the order of avatars in case of multiple avatars use `flexDirection="row"` in ``
#### Avatar Group (Without Badge)
An Avatar Group component without badges organizes multiple avatars in a visually cohesive manner, offering a streamlined display of user or entity representations without additional visual indicators or badges within a user interface.
```jsx
function Example() {
return (
John DoeJohn DoeJohn DoeJohn Doe
);
}`
```
#### Avatar Group (with Badge)
An Avatar Group component with badges presents a collection of avatars with accompanying badges, providing visual indicators or additional information associated with each user or entity representation within a user interface.
```jsx
function Example() {
return (
John DoeJohn DoeJohn DoeJohn Doe
);
}`
```
#### Custom
A custom Avatar component with text allows for personalized user or entity representations by combining customized visuals, such as an image or icon, with accompanying text, providing a unique and identifiable presentation within a user interface.
```jsx
function Example() {
return (
Ronald RichardsNursing AssistantKevin JamesWeb Designer
);
}`
```
#### Fallback
Fallback text is shown when the image fails to load, the image is not available or the provided image url is not correct.
```jsx
function Example() {
return (
John Doe
);
}`
```
---
## Badge
URL: /ui/docs/components/badge/index
# Badge
Display status indicators with the Badge component. Perfect for notifications, labels, and status tags in your React Native app.
This is an illustration of **Badge** component.
```jsx
function Example() {
return (
New
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add badge
```
**Manual:**
### Step 1: Copy and paste the following code into your project.
```tsx
'use client';
const SCOPE = 'BADGE';
const badgeStyle = tva({
base: 'flex-row items-center justify-center rounded-sm px-2 py-0.5',
variants: {
variant: {
default: 'bg-primary',
secondary: 'bg-secondary',
destructive:
'bg-destructive dark:bg-destructive/60',
outline: 'border border-border dark:border-border/90 bg-transparent',
},
},
});
const badgeTextStyle = tva({
base: 'text-xs font-medium tracking-normal uppercase',
parentVariants: {
variant: {
default: 'text-primary-foreground',
secondary: 'text-secondary-foreground',
destructive: 'text-white',
outline: 'text-foreground',
},
},
});
const badgeIconStyle = tva({
base: 'fill-none h-3 w-3 pointer-events-none',
parentVariants: {
variant: {
default: 'text-primary-foreground',
secondary: 'text-secondary-foreground',
destructive: 'text-white',
outline: 'text-foreground',
},
},
});
const ContextView = withStyleContext(View, SCOPE);
type IBadgeProps = React.ComponentPropsWithoutRef &
VariantProps;
function Badge({
children,
variant = 'default',
className,
...props
}: { className?: string } & IBadgeProps) {
return (
{children}
);
}
type IBadgeTextProps = React.ComponentPropsWithoutRef &
VariantProps;
const BadgeText = React.forwardRef<
React.ComponentRef,
IBadgeTextProps
>(function BadgeText({ children, className, ...props }, ref) {
const { variant: parentVariant } = useStyleContext(SCOPE);
return (
{children}
);
});
type IBadgeIconProps = React.ComponentPropsWithoutRef &
VariantProps & {
size?: number;
};
const StyledUIIcon = styled(UIIcon, {
className: {
target: 'style',
nativeStyleToProp: {
height: true,
width: true,
fill: true,
color: 'classNameColor',
stroke: true,
},
},
});
const BadgeIcon = React.forwardRef<
React.ComponentRef,
IBadgeIconProps
>(function BadgeIcon({ className, size, ...props }, ref) {
const { variant: parentVariant } = useStyleContext(SCOPE);
if (typeof size === 'number') {
return (
);
} else if (
(props?.height !== undefined || props?.width !== undefined) &&
size === undefined
) {
return (
);
}
return (
);
});
Badge.displayName = 'Badge';
BadgeText.displayName = 'BadgeText';
BadgeIcon.displayName = 'BadgeIcon';
export { Badge, BadgeIcon, BadgeText };
```
### Step 2: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```ts
```
```ts
export default () => (
);
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Badge
It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### BadgeText
It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.
#### BadgeIcon
Contains all Icon related layout style props and actions. It inherits all the properties of gluestack Style's [AsForwarder](/style/docs/api/as-forwarder) component.
### Props
Badge component is created using View component from react-native. It extends all the props supported by [React Native View](https://reactnative.dev/docs/view#props).
#### Badge
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `action` | error | warning | success | info | muted | success | Determines the color scheme of the badge. |
| `variant` | solid | outline | solid | Determines the visual style of the badge. |
| `size` | sm | md | lg | md | Determines the size of the badge. |
> Note: These props are exclusively applicable when utilizing the default configuration of gluestack-ui/config. If you are using a custom theme, these props may not be available.
### Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs. Simply copy the code and integrate it into your project.
#### Badge with Avatar
An example of the Badge component being used with the Avatar component to display badges alongside user avatars for enhanced visual representation or identification.
```jsx
function Example() {
return (
SSRonald RichardsVerifiedNursing Assistant
);
}`
```
#### Badge Composition
An example of the Badge component being used with the Composition component, allowing for the display of badges within a composition of other UI elements.
```jsx
function Example() {
return (
2
);
}`
```
---
## BottomSheet
URL: /ui/docs/components/bottomsheet/index
# BottomSheet
A bottom sheet component for React Native built on top of [@gorhom/bottom-sheet](https://ui.gorhom.dev/components/bottom-sheet) with gluestack-ui styling and NativeWind className support.
> **Note:** BottomSheet is **not supported in Next.js**. It is intended for React Native and Expo (mobile/web via React Native) environments only. `@gorhom/bottom-sheet` relies on React Native Reanimated and Gesture Handler which are not compatible with Next.js.
This is an illustration of the **BottomSheet** component.
```jsx
function Example() {
return (
Open BottomSheetItem 1Item 2Item 3
)
}`
```
#### With Search Input
A bottom sheet with a keyboard-aware search input using BottomSheetTextInput. Typing filters the list in real time without dismissing the keyboard.
```jsx
function App() {
const [query, setQuery] = React.useState('');
const items = [
'Design', 'Development', 'Marketing', 'Product',
'Research', 'Engineering', 'Sales', 'Support',
];
const filtered = items.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
);
return (
Open Search SheetSearch Categories
{filtered.length} result{filtered.length !== 1 ? 's' : ''}
{filtered.length === 0 ? (
No results for "{query}"
) : (
filtered.map(item => (
{item}
))
)}
);
}`
```
#### With Footer
A bottom sheet with a sticky BottomSheetFooter containing action buttons. Items can be multi-selected without closing the sheet, and the Confirm button shows the selected count.
```jsx
function App() {
const ref = React.useRef(null);
const [selected, setSelected] = React.useState([]);
const items = [
'Copy Link',
'Save to Library',
'Set as Wallpaper',
'Share via...',
'Delete',
];
const toggle = (item) => {
setSelected(prev =>
prev.includes(item) ? prev.filter(i => i !== item) : [...prev, item]
);
};
const footer = React.useCallback((props) => (
), [selected.length]);
return (
Open with FooterChoose ActionsSelect one or more options
{items.map(item => {
const isSelected = selected.includes(item);
return (
toggle(item)}
className={isSelected ? 'bg-primary/5' : ''}
>
{item}
{isSelected && (
✓
)}
);
})}
);
}`
```
#### With Snap Points
A bottom sheet with multiple snap points controlled programmatically via a ref. Demonstrates open, close, expand, collapse, and snapToIndex using the imperative BottomSheetRef handle.
```jsx
function App() {
const ref = React.useRef(null);
const [currentSnap, setCurrentSnap] = React.useState(-1);
return (
setCurrentSnap(-1)}>
{currentSnap >= 0
? 'Sheet open at snap index: ' + currentSnap
: 'Sheet is closed'}
Open
Snap Points
Current snap index: {currentSnap}
);
}`
```
## Installation
### Run the following command:
```bash
npx gluestack-ui add bottomsheet
```
## API Reference
To use this component in your project, include the following import statement in your file.
```jsx
```
```jsx
export default () => (
Open BottomSheetItem 1Item 2Item 3
);
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### BottomSheet
The root context provider for the entire BottomSheet. Accepts a `ref` to control the sheet imperatively.
### Props
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultSnapIndex` | number | 0 | {`The index of snapPoints at which the bottom sheet initially opens when triggered.`} |
| `onOpen` | () => void | - | {`Callback fired when the bottom sheet opens.`} |
| `onClose` | () => void | - | {`Callback fired when the bottom sheet closes.`} |
| `onChange` | (index: number) => void | - | {`Callback fired whenever the sheet snaps to a new index. Receives the current snap index (-1 when closed).`} |
### Ref Methods
`BottomSheet` forwards a ref exposing the following imperative methods:
| Method | Signature | Description |
| --- | --- | --- |
| `open` | (index?: number) => void | {`Opens the bottom sheet. Optionally pass a snap index; defaults to defaultSnapIndex.`} |
| `close` | () => void | {`Closes the bottom sheet.`} |
| `snapToIndex` | (index: number) => void | {`Snaps the sheet to the given snap point index.`} |
| `expand` | () => void | {`Expands the sheet to the highest snap point.`} |
| `collapse` | () => void | {`Collapses the sheet to the lowest snap point.`} |
#### BottomSheetTrigger
Wraps React Native's [Pressable](https://reactnative.dev/docs/pressable). Pressing it opens the bottom sheet.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `index` | number | - | {`Snap index to open the sheet at. Overrides defaultSnapIndex when provided.`} |
Also inherits all props of React Native's [Pressable](https://reactnative.dev/docs/pressable).
#### BottomSheetPortal
The actual gorhom BottomSheet container. Inherits all props from [gorhom BottomSheet](https://ui.gorhom.dev/components/bottom-sheet/props).
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `snapPoints` | {`Array`} | - | {`Required. Points for the bottom sheet to snap to. Accepts numbers or percentage strings (e.g. '50%').`} |
| `enablePanDownToClose` | boolean | true | {`Enables closing the sheet by panning downward.`} |
| `enableDynamicSizing` | boolean | false | {`Enables the sheet height to be determined dynamically by its content.`} |
| `closeOnBackdropPress` | boolean | true | {`Closes the sheet when the backdrop is pressed.`} |
| `backgroundClassName` | string | - | {`NativeWind className applied to the sheet background surface.`} |
| `handleIndicatorClassName` | string | - | {`NativeWind className applied to the drag handle indicator.`} |
#### BottomSheetBackdrop
Renders a semi-transparent backdrop behind the sheet. Inherits all props from [gorhom BottomSheetBackdrop](https://ui.gorhom.dev/components/bottom-sheet/components/bottomsheetbackdrop).
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disappearsOnIndex` | number | -1 | {`Snap index at which the backdrop disappears.`} |
| `appearsOnIndex` | number | 0 | {`Snap index at which the backdrop appears.`} |
| `opacity` | number | 0.5 | {`Opacity of the backdrop overlay.`} |
| `pressBehavior` | 'close' | 'collapse' | 'none' | 'close' | {`Defines what happens when the backdrop is pressed.`} |
#### BottomSheetDragIndicator
The drag handle shown at the top of the sheet. Inherits all props from [gorhom BottomSheetHandle](https://ui.gorhom.dev/components/bottom-sheet/components/bottomsheethandle).
#### BottomSheetContent
The scrollable content area inside the sheet. Inherits all props from [gorhom BottomSheetView](https://ui.gorhom.dev/components/bottom-sheet/components/bottomsheetview).
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `focusScope` | boolean | true | {`When true, wraps content in a FocusScope on web for keyboard accessibility (traps focus and restores it on close).`} |
#### BottomSheetFooter
A sticky footer pinned to the bottom of the sheet. Inherits all props from [gorhom BottomSheetFooter](https://ui.gorhom.dev/components/bottom-sheet/components/bottomsheetfooter).
#### BottomSheetItem
A pressable row item inside the sheet content. Inherits all props from React Native's [Pressable](https://reactnative.dev/docs/pressable).
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `closeOnSelect` | boolean | true | {`Automatically closes the sheet when the item is pressed.`} |
#### BottomSheetItemText
The text label for a `BottomSheetItem`. Inherits all props from React Native's [Text](https://reactnative.dev/docs/text).
#### BottomSheetScrollView
Direct re-export of [gorhom BottomSheetScrollView](https://ui.gorhom.dev/components/bottom-sheet/components/bottomsheetscrollview). Use inside `BottomSheetPortal` for scrollable content.
#### BottomSheetFlatList
Direct re-export of [gorhom BottomSheetFlatList](https://ui.gorhom.dev/components/bottom-sheet/components/bottomsheetflatlist). Use inside `BottomSheetPortal` for list content.
#### BottomSheetSectionList
Direct re-export of [gorhom BottomSheetSectionList](https://ui.gorhom.dev/components/bottom-sheet/components/bottomsheetsectionlist). Use inside `BottomSheetPortal` for sectioned list content.
#### BottomSheetTextInput
Direct re-export of [gorhom BottomSheetTextInput](https://ui.gorhom.dev/components/bottom-sheet/components/bottomsheettextinput). Use this instead of the standard `TextInput` inside the sheet to avoid keyboard layout issues.
---
## Box
URL: /ui/docs/components/box/index
# Box
Use gluestack-ui Box, a powerful box component for flexible layouts. Customize styles, props, and structure easily for web and native platforms.
This is an illustration of **Box** component.
```jsx
function Example() {
return (
This is the Box
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add box
```
**Manual:**
### Step 1: Copy and paste the following code into index.tsx in your project.
```tsx
type IBoxProps = ViewProps &
VariantProps & { className?: string };
const Box = React.forwardRef, IBoxProps>(
function Box({ className, ...props }, ref) {
return (
);
}
);
Box.displayName = 'Box';
export { Box };
```
> Note: **Step 2 is optional and only required if you want to add support for [React Server Components](https://vercel.com/blog/understanding-react-server-components), You can skip this and jump to Step 3 directly if you don't have this requirement.**
### Step 2: Step 2(optional): Copy and paste the following code into index.web.tsx in your project.
```tsx
type IBoxProps = React.ComponentPropsWithoutRef<'div'> &
VariantProps & { className?: string };
const Box = React.forwardRef(function Box(
{ className, ...props },
ref
) {
return (
);
});
Box.displayName = 'Box';
export { Box };
```
### Step 3: Copy and paste the following code into styles.tsx in your project.
```tsx
const baseStyle = isWeb
? 'flex flex-col relative z-0 box-border border-0 list-none min-w-0 min-h-0 bg-transparent items-stretch m-0 p-0 text-decoration-none'
: '';
export const boxStyle = tva({
base: baseStyle,
});
```
## Step 4: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```ts
```
```ts
export default () => ;
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Box
Renders a `` on web and a `View` on native.
| Platform | Output |
| --- | --- |
| `Web` | {``} |
| `Native` | {``} |
---
## Button
URL: /ui/docs/components/button/index
# Button
Discover a powerful button component for React & React Native with customizable size, shape, color, and behavior. Perfect for UI design & seamless user interactions.
This is an illustration of **Button** component.
```jsx
function Example() {
return (
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add button
```
**Manual:**
### Step 1: Copy and paste the following code into your project.
```jsx
'use client';
const SCOPE = 'BUTTON';
const Root = withStyleContext(Pressable, SCOPE);
const StyledUIIcon = styled(UIIcon, {
className: "style",
});
const UIButton = createButton({
Root: Root,
Text,
Group: View,
Spinner: ActivityIndicator,
Icon: StyledUIIcon,
});
const buttonStyle = tva({
base: 'rounded-md flex-row items-center justify-center data-[focus-visible=true]:web:outline-none data-[focus-visible=true]:web:ring-2 data-[disabled=true]:opacity-40 gap-2 h-fit',
variants: {
variant: {
default:
'bg-primary data-[hover=true]:bg-primary/90 data-[active=true]:bg-primary/90',
destructive:
'bg-destructive data-[hover=true]:bg-destructive/90 data-[active=true]:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
outline:
'border border-border bg-background shadow-xs data-[hover=true]:bg-accent data-[active=true]:bg-accent dark:bg-input/[0.045] dark:border-border/90 dark:data-[hover=true]:bg-input/[0.075] dark:data-[active=true]:bg-input/[0.075]',
secondary:
'bg-secondary text-secondary-foreground data-[hover=true]:bg-secondary/80 data-[active=true]:bg-secondary/80',
ghost: 'data-[hover=true]:bg-accent data-[active=true]:bg-accent dark:data-[hover=true]:bg-accent/50 dark:data-[active=true]:bg-accent/50',
link: 'text-primary underline-offset-4 data-[hover=true]:underline data-[active=true]:underline',
},
size: {
default: 'px-4 py-2',
sm: 'min-h-8 rounded-md px-3 text-xs',
lg: 'min-h-10 rounded-md px-8',
icon: 'min-h-9 min-w-9',
},
},
});
const buttonTextStyle = tva({
base: 'web:select-none font-sans',
parentVariants: {
variant: {
default: 'text-primary-foreground',
destructive: 'text-white',
outline: 'text-foreground data-[hover=true]:text-accent-foreground data-[active=true]:text-accent-foreground',
secondary: 'text-secondary-foreground',
ghost: 'text-foreground ',
link: 'text-primary data-[hover=true]:underline data-[active=true]:underline',
},
size: {
default: 'text-sm',
sm: 'text-xs',
lg: 'text-sm',
icon: 'text-sm',
},
},
});
const buttonSpinnerStyle = tva({
base: '',
parentVariants: {
size: {
default: 'h-4 w-4',
sm: 'h-3 w-3',
lg: 'h-5 w-5',
icon: 'h-4 w-4',
},
},
});
const buttonIconStyle = tva({
base: 'fill-none pointer-events-none shrink-0',
parentVariants: {
variant: {
default: 'text-primary-foreground',
destructive: 'text-white',
outline:
'text-foreground data-[hover=true]:text-accent-foreground data-[active=true]:text-accent-foreground',
secondary: 'text-secondary-foreground',
ghost:
'text-foreground data-[hover=true]:text-accent-foreground data-[active=true]:text-accent-foreground',
link: 'text-primary',
},
size: {
default: 'h-4 w-4',
sm: 'h-3 w-3',
lg: 'h-5 w-5',
icon: 'h-4 w-4',
},
},
});
const buttonGroupStyle = tva({
base: '',
variants: {
space: {
'xs': 'gap-1',
'sm': 'gap-2',
'md': 'gap-3',
'lg': 'gap-4',
'xl': 'gap-5',
'2xl': 'gap-6',
'3xl': 'gap-7',
'4xl': 'gap-8',
},
isAttached: {
true: 'gap-0',
},
flexDirection: {
'row': 'flex-row',
'column': 'flex-col',
'row-reverse': 'flex-row-reverse',
'column-reverse': 'flex-col-reverse',
},
},
});
type IButtonProps = Omit<
React.ComponentPropsWithoutRef,
'context'
> &
VariantProps & { className?: string };
const Button = React.forwardRef<
React.ElementRef,
IButtonProps
>(({ className, variant = 'default', size = 'default', ...props }, ref) => {
return (
);
});
type IButtonTextProps = React.ComponentPropsWithoutRef &
VariantProps & { className?: string };
const ButtonText = React.forwardRef<
React.ElementRef,
IButtonTextProps
>(({ className, size, ...props }, ref) => {
const { size: parentSize, variant: parentVariant } = useStyleContext(SCOPE);
return (
);
});
const ButtonSpinner = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => {
const { size: parentSize } = useStyleContext(SCOPE);
return ;
});
type IButtonIcon = React.ComponentPropsWithoutRef &
VariantProps & {
className?: string | undefined;
as?: React.ElementType;
height?: number;
width?: number;
};
const ButtonIcon = React.forwardRef<
React.ElementRef,
IButtonIcon
>(({ className, size, ...props }, ref) => {
const { size: parentSize, variant: parentVariant } = useStyleContext(SCOPE);
if (typeof size === 'number') {
return (
);
} else if (
(props.height !== undefined || props.width !== undefined) &&
size === undefined
) {
return (
);
}
return (
);
});
type IButtonGroupProps = React.ComponentPropsWithoutRef &
VariantProps;
const ButtonGroup = React.forwardRef<
React.ElementRef,
IButtonGroupProps
>(
(
{
className,
space = 'md',
isAttached = false,
flexDirection = 'column',
...props
},
ref
) => {
return (
);
}
);
Button.displayName = 'Button';
ButtonText.displayName = 'ButtonText';
ButtonSpinner.displayName = 'ButtonSpinner';
ButtonIcon.displayName = 'ButtonIcon';
ButtonGroup.displayName = 'ButtonGroup';
export { Button, ButtonGroup, ButtonIcon, ButtonSpinner, ButtonText };
```
### Step 2: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```jsx
```
```jsx
export default () => (
);
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Button
Contains all button related layout style props and actions. It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) component.
<>
| `isHovered` | bool | false | To manually set hover to the button. |
| --- | --- | --- | --- |
| `isPressed` | bool | false | To manually set pressable state to the button. |
| `isFocused` | bool | false | To manually set focused state to the button. |
| `isDisabled` | bool | false | To manually set disable to the button. |
>
#### ButtonText
Contains all text related layout style props and actions. It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.
#### ButtonGroup
Contains all group related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
<>
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `flexDirection` | 'row' | 'column' | 'row-reverse' | 'column-reverse' | 'row' | Set the direction of Button group to vertical or horizontal |
| `isDisabled` | bool | false | When true, this will disable all the buttons in a ButtonGroup. |
| `isAttached` | bool | false | When attached, all buttons will be attached to each other. |
| `reversed` | bool | false | To reverse the order of components. |
| `space` | string | md | It sets the space between different buttons. |
>
#### ButtonSpinner
Contains all spinner related layout style props and actions.
It inherits all the properties of React Native's [ActivityIndicator](https://reactnative.dev/docs/activityindicator) component.
#### ButtonIcon
Contains all Icon related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
### Features
- Keyboard support for actions.
- Support for hover, focus and active states.
- Option to add your styles or use the default styles.
### Accessibility
We have outlined the various features that ensure the Button component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards.Adheres to the [WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/button/).
#### Keyboard
- `Tab`: Moves focus to the next focusable element.
- `Shift + Tab`: Moves focus to the previous focusable element.
- `Enter`: Triggers the button's action.
### Screen Reader
- VoiceOver: When the button is focused, the screen reader will announce the button's label and its current state.
### Focus Management
- The `onFocus` and `onBlur` props to manage focus states and provide visual cues to users. This is especially important for users who rely on keyboard navigation.
### Props
Button component is created using Pressable component from react-native. It extends all the props supported by [React Native Pressable](https://reactnative.dev/docs/pressable#props), [utility props](/ui/docs/styling/utility-and-sx-props) and the props mentioned below.
#### Button
<>
| Name | Value | Default |
| --- | --- | --- |
| `action` | primary | secondary | positive | negative | default | primary |
| `variant` | link | outline | solid | solid |
| `size` | xs | sm | md | lg | xl | md |
>
> Note: These props are exclusively applicable when utilizing the default configuration of gluestack-ui/config. If you are using a custom theme, these props may not be available.
### Data Attributes Table
Component receives states as props as boolean values, which are applied as `data-*` attributes. These attributes are then used to style the component via classNames, enabling state-based styling.
<>
| State | Data Attribute | Values |
| --- | --- | --- |
| `hover` | data-hover | `true | false` |
| `active` | data-active | `true | false` |
| `disabled` | data-disabled | `true | false` |
| `focusVisible` | data-focus-visible | `true | false` |
>
### Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs. Simply copy the code and integrate it into your project.
#### Button with Loading State
A loading button is a type of button component that provides visual feedback to the user during an action that takes some time to complete. It typically displays an animated loading icon or spinner indicating the action is in progress.
```jsx
function Example() {
return (
);
}`
```
#### Icon Button
A button with an icon integrates a visual symbol within the button component, enhancing its appearance and providing a recognizable and intuitive representation of its associated action or functionality.
```jsx
function Example() {
return (
);
}`
```
#### Link Button
A button with a link combines the interactive behavior of a button component with the ability to navigate to a specified URL, providing a clickable element for users to access external resources or internal pages.
```jsx
function Example() {
return (
);
}`
```
#### Button With Icon
The icon component incorporates a button component, combining visual representation with interactive functionality for seamless user interaction.
```jsx
function Example() {
return (
);
}`
```
#### Button with Full Width
The button with full width component utilizes a button component, expanding its width to occupy the entire available space, creating a visually prominent and easily accessible interface element.
```jsx
function Example() {
return (
Set new password
Almost done. Enter your new password and you are all set.
);
}`
```
---
## Calendar
URL: /ui/docs/components/calendar/index
# Calendar
A versatile calendar component for React & React Native with support for single, multiple, and range date selection. Features include event markers, disabled dates, week numbers, and customizable styling.
This is an illustration of **Calendar** component.
#### Basic Calendar
A simple calendar component with single date selection
```jsx
function Example() {
const [selected, setSelected] = React.useState(new Date());
return (
{/* Calendar will auto-render the grid */}
);
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add calendar
```
**Manual:**
### Step 1: Copy and paste the following code into your project.
```jsx
'use client';
// Styled Root Component
const CalendarRoot = React.forwardRef<
React.ElementRef,
ICalendarProps & React.ComponentProps & { className?: string }
>(({ className, ...props }, ref) => {
return (
);
});
// Styled Header
const CalendarHeaderRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string }
>(({ className, ...props }, ref) => {
return (
);
});
const CalendarHeaderPrevButtonRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & {
className?: string;
disabled?: boolean;
}
>(({ className, disabled, ...props }, ref) => {
return (
);
});
const CalendarHeaderNextButtonRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & {
className?: string;
disabled?: boolean;
}
>(({ className, disabled, ...props }, ref) => {
return (
);
});
const CalendarHeaderTitleRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string }
>(({ className, ...props }, ref) => {
return (
);
});
const MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
];
type SelectRootProps = React.ComponentProps & {
className?: string;
items?: Array<{ label: string; value: number }>;
selectedValue?: number;
onValueChange?: (value: number) => void;
};
const CalendarHeaderMonthSelectRoot = React.forwardRef<
React.ElementRef,
SelectRootProps
>(({ className, items = [], selectedValue, onValueChange, ...props }, ref) => {
const label = selectedValue !== undefined ? MONTH_NAMES[selectedValue] : 'Month';
return (
);
});
const CalendarHeaderYearSelectRoot = React.forwardRef<
React.ElementRef,
SelectRootProps
>(({ className, items = [], selectedValue, onValueChange, ...props }, ref) => {
const label = selectedValue !== undefined ? String(selectedValue) : 'Year';
return (
);
});
// Styled Week Days Header
const CalendarWeekDaysHeaderRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string }
>(({ className, ...props }, ref) => {
return (
);
});
const CalendarWeekDayRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string }
>(({ className, children, ...props }, ref) => {
return (
{typeof children === 'string' ? (
{children}
) : (
children
)}
);
});
// Styled Body & Grid
const CalendarBodyRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string }
>(({ className, ...props }, ref) => {
return (
);
});
const CalendarGridRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string }
>(({ className, ...props }, ref) => {
return (
);
});
const CalendarWeekRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string }
>(({ className, ...props }, ref) => {
return (
);
});
// Styled Day
const CalendarDayRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & {
'className'?: string;
'data-state'?: string;
}
>(({ className, 'data-state': dataState, ...props }, ref) => {
return (
);
});
const CalendarDayTextRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string; state?: any }
>(({ className, state, ...props }, ref) => {
return (
);
});
const CalendarDayIndicatorRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & {
'className'?: string;
'data-type'?: string;
}
>(({ className, 'data-type': dataType, ...props }, ref) => {
return (
);
});
// Styled Week Number
const CalendarWeekNumberRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string }
>(({ className, children, ...props }, ref) => {
return (
{typeof children === 'string' || typeof children === 'number' ? (
{children}
) : (
children
)}
);
});
// Styled Footer
const CalendarFooterRoot = React.forwardRef<
React.ElementRef,
React.ComponentProps & { className?: string }
>(({ className, ...props }, ref) => {
return (
);
});
// Create Calendar using the factory
const UICalendar = createCalendar({
Root: CalendarRoot,
Header: CalendarHeaderRoot,
HeaderPrevButton: CalendarHeaderPrevButtonRoot,
HeaderNextButton: CalendarHeaderNextButtonRoot,
HeaderTitle: CalendarHeaderTitleRoot,
HeaderMonthSelect: CalendarHeaderMonthSelectRoot,
HeaderYearSelect: CalendarHeaderYearSelectRoot,
WeekDaysHeader: CalendarWeekDaysHeaderRoot,
WeekDay: CalendarWeekDayRoot,
Body: CalendarBodyRoot,
Grid: CalendarGridRoot,
Week: CalendarWeekRoot,
Day: CalendarDayRoot,
DayText: CalendarDayTextRoot,
DayIndicator: CalendarDayIndicatorRoot,
WeekNumber: CalendarWeekNumberRoot,
Footer: CalendarFooterRoot,
});
// Mode-specific discriminated union props so onValueChange is correctly
// narrowed per mode (prevents TypeScript errors when passing setState).
type OmittedCalendarKeys = 'mode' | 'value' | 'defaultValue' | 'onValueChange';
type SingleModeProps = {
mode?: 'single';
value?: Date;
defaultValue?: Date;
onValueChange?: (value: Date) => void;
};
type MultipleModeProps = {
mode: 'multiple';
value?: Date[];
defaultValue?: Date[];
onValueChange?: (value: Date[]) => void;
};
type RangeModeProps = {
mode: 'range';
value?: { from: Date; to?: Date };
defaultValue?: { from: Date; to?: Date };
onValueChange?: (value: { from: Date; to?: Date }) => void;
};
type CalendarProps = (SingleModeProps | MultipleModeProps | RangeModeProps) &
Omit &
Omit, OmittedCalendarKeys> & {
className?: string;
};
const CalendarComponent = React.forwardRef<
React.ElementRef,
CalendarProps
>((props, ref) => {
return ;
});
CalendarComponent.displayName = 'Calendar';
// Export components
export const Calendar = CalendarComponent;
export const CalendarHeader = UICalendar.Header;
export const CalendarHeaderPrevButton = UICalendar.HeaderPrevButton;
export const CalendarHeaderNextButton = UICalendar.HeaderNextButton;
export const CalendarHeaderTitle = UICalendar.HeaderTitle;
export const CalendarHeaderMonthSelect = UICalendar.HeaderMonthSelect;
export const CalendarHeaderYearSelect = UICalendar.HeaderYearSelect;
export const CalendarWeekDaysHeader = UICalendar.WeekDaysHeader;
export const CalendarWeekDay = UICalendar.WeekDay;
export const CalendarBody = UICalendar.Body;
export const CalendarGrid = UICalendar.Grid;
export const CalendarWeek = UICalendar.Week;
export const CalendarDay = UICalendar.Day;
export const CalendarDayText = UICalendar.DayText;
export const CalendarDayIndicator = UICalendar.DayIndicator;
export const CalendarWeekNumber = UICalendar.WeekNumber;
export const CalendarFooter = UICalendar.Footer;
// Re-export types
export type {
CalendarMarker,
CalendarMarkers, CalendarMode, DayState, ICalendarProps
} from '@gluestack-ui/core/calendar/creator';
export type { CalendarProps };
```
### Step 2: Copy and paste the following styles into your project.
```jsx
// Main Calendar container
// Full width on native, max-width on web
export const calendarStyle = tva({
base: 'w-full web:w-fit bg-background border border-border rounded-lg p-4 gap-2',
variants: {
size: {
sm: 'p-2 gap-2',
md: 'p-2 gap-1',
lg: 'p-6 gap-6',
},
},
defaultVariants: {
size: 'md',
},
});
// Header
export const calendarHeaderStyle = tva({
base: 'flex-row items-center justify-between mb-2',
variants: {},
});
export const calendarHeaderButtonStyle = tva({
base: 'h-9 w-9 flex items-center justify-center rounded-md hover:bg-accent active:bg-accent/80 data-[disabled=true]:opacity-40 data-[disabled=true]:cursor-not-allowed',
variants: {},
});
export const calendarHeaderTitleStyle = tva({
base: 'text-foreground text-base font-semibold',
variants: {},
});
// Week Days Header
export const calendarWeekDaysHeaderStyle = tva({
base: 'flex-row gap-0 mb-2',
variants: {},
});
export const calendarWeekDayStyle = tva({
base: 'flex-1 items-center justify-center min-w-[2.0rem] web:max-w-[2.5rem]',
variants: {},
});
export const calendarWeekDayTextStyle = tva({
base: 'text-muted-foreground text-xs font-medium uppercase',
variants: {},
parentVariants: {},
});
// Body & Grid
export const calendarBodyStyle = tva({
base: 'gap-0',
variants: {},
});
export const calendarGridStyle = tva({
base: 'gap-0',
variants: {},
});
export const calendarWeekStyle = tva({
base: 'flex-row gap-0',
variants: {},
});
// Day
export const calendarDayStyle = tva({
base: 'flex-1 aspect-square items-center justify-center rounded-md web:max-w-[2.25rem] relative transition-colors',
variants: {
state: {
'default': 'hover:bg-accent active:bg-accent/80',
'selected': 'bg-primary hover:bg-primary/90 active:bg-primary/80',
'today': 'bg-accent',
'disabled': 'opacity-40 cursor-not-allowed',
'outside-month': 'opacity-30',
'range-start': 'bg-primary rounded-r-md',
'range-end': 'bg-primary rounded-l-md',
'range-middle': 'bg-primary/20 rounded-none',
},
},
});
export const calendarDayTextStyle = tva({
base: 'text-foreground text-sm font-normal z-10',
variants: {
state: {
'default': 'text-foreground',
'selected': 'text-primary-foreground ',
'today': 'text-accent-foreground',
'disabled': 'text-muted-foreground',
'outside-month': 'text-muted-foreground',
'range-start': 'text-primary-foreground font-semibold',
'range-end': 'text-primary-foreground font-semibold',
'range-middle': 'text-foreground',
},
},
});
// Day Indicator (markers/dots)
export const calendarDayIndicatorStyle = tva({
base: 'absolute bottom-1 flex-row gap-0.5 z-0',
variants: {
type: {
'dot': 'flex-row gap-0.5',
'multi-dot': 'flex-row gap-0.5',
'period': 'absolute inset-0 rounded-md opacity-20',
},
},
});
export const calendarDotStyle = tva({
base: 'w-1 h-1 rounded-full',
variants: {},
});
// Week Number
export const calendarWeekNumberStyle = tva({
base: 'w-8 items-center justify-center mr-1',
variants: {},
});
export const calendarWeekNumberTextStyle = tva({
base: 'text-muted-foreground text-xs font-normal',
variants: {},
parentVariants: {},
});
// Footer
export const calendarFooterStyle = tva({
base: 'mt-4 pt-4 border-t border-border',
variants: {},
});
// Month/Year Select
export const calendarHeaderSelectStyle = tva({
base: 'bg-transparent border border-border rounded-md px-2 py-1 text-sm text-foreground',
variants: {},
});
```
### Step 3: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```jsx
```
### Component Anatomy
```jsx
export default () => (
);
```
### Calendar Props
Contains all the calendar related layout style props and actions.
<>
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | 'single' | 'multiple' | 'range' | 'single' | Selection mode for the calendar. |
| `value` | Date | Date[] | {'{ from: Date; to?: Date }'} | - | Controlled value of the calendar. |
| `defaultValue` | Date | Date[] | {'{ from: Date; to?: Date }'} | - | Default value for uncontrolled calendar. |
| `onValueChange` | {'(value) => void'} | - | Callback when selection changes. |
| `minDate` | Date | - | Minimum selectable date. |
| `maxDate` | Date | - | Maximum selectable date. |
| `disabledDates` | Date[] | {'(date: Date) => boolean'} | - | Disabled dates or function to check disabled dates. |
| `initialMonth` | Date | - | Initial month to display. |
| `numberOfMonths` | number | 1 | Number of months to display. |
| `showWeekNumbers` | boolean | false | Show ISO week numbers. |
| `showOutsideDays` | boolean | true | Show days from adjacent months. |
| `firstDayOfWeek` | 0-6 | 0 | First day of week (0=Sunday). |
| `fixedWeeks` | boolean | false | Always show 6 weeks. |
| `markers` | CalendarMarkers | - | Event markers for specific dates. |
| `enableMonthYearPicker` | boolean | false | Enable month/year dropdown selectors. |
| `minYear` | number | - | Minimum year in year picker. |
| `maxYear` | number | - | Maximum year in year picker. |
| `locale` | string | 'en-US' | Locale for date formatting. |
| `isDisabled` | boolean | false | Disable entire calendar. |
| `isReadOnly` | boolean | false | Read-only mode. |
| `onMonthChange` | {'(month: Date) => void'} | - | Called when month changes. |
| `onDayPress` | {'(date: Date) => void'} | - | Called when day is pressed. |
| `onDayLongPress` | {'(date: Date) => void'} | - | Called when day is long-pressed. |
>
### Marker Types
Markers allow you to add visual indicators to dates.
<>
| Type | Description |
| --- | --- |
| `dot` | Single colored dot below the date. |
| `multi-dot` | Multiple colored dots below the date. |
| `period` | Highlighted period/background for the date. |
>
### Marker Colors
Marker colors support shadcn-style design tokens using CSS variables:
```jsx
const markers = {
'2024-01-15': {
type: 'dot',
color: 'hsl(var(--primary))', // Primary color
},
'2024-01-20': {
type: 'multi-dot',
dots: [
{ color: 'hsl(var(--primary))', key: '1' },
{ color: 'hsl(var(--destructive))', key: '2' },
],
},
'2024-01-25': {
type: 'period',
color: 'hsl(var(--secondary))', // Secondary color
},
};
```
**Available color tokens:**
- `hsl(var(--primary))` - Primary brand color
- `hsl(var(--secondary))` - Secondary color
- `hsl(var(--accent))` - Accent color
- `hsl(var(--destructive))` - Destructive/error color
- `hsl(var(--muted))` - Muted/neutral color
### Sub-components
#### CalendarHeader
Container for the calendar header. Contains navigation buttons and title.
#### CalendarHeaderPrevButton / CalendarHeaderNextButton
Navigation buttons for moving between months. Use with Icon component.
#### CalendarHeaderTitle
Displays the current month and year.
#### CalendarHeaderMonthSelect / CalendarHeaderYearSelect
Dropdown selectors for quick month/year navigation. Requires `enableMonthYearPicker={true}`.
#### CalendarWeekDaysHeader
Container for the weekday labels row (Sun, Mon, Tue, etc.).
#### CalendarWeekDay
Individual weekday label cell.
#### CalendarBody
Container for the calendar body.
#### CalendarGrid
Grid container for the calendar days.
#### CalendarWeek
Individual week row container.
#### CalendarDay
Individual day cell. Supports `data-state` attribute for styling based on state.
#### CalendarDayText
Text component for displaying the day number. Automatically styles based on day state.
#### CalendarDayIndicator
Visual indicator for event markers. Use with `data-type` attribute.
#### CalendarWeekNumber
Displays ISO week numbers when `showWeekNumbers={true}`.
#### CalendarFooter
Footer container for additional content.
## Features
- **Three Selection Modes**: Single date, multiple dates, and date range selection
- **Event Markers**: Dot, multi-dot, and period indicators for dates
- **Date Constraints**: Min/max dates and custom disabled dates
- **Week Numbers**: Optional ISO week number display
- **Month/Year Picker**: Quick navigation with dropdown selectors
- **Customizable**: Full Tailwind CSS styling support with shadcn-style tokens
- **Accessible**: Built-in disabled and read-only states
- **Internationalization**: Locale support via date-fns
## Styling
The Calendar component uses **shadcn-style design tokens** for consistent theming. These semantic color tokens automatically adapt to light and dark modes.
### Available Color Tokens
| Token | Light Mode | Dark Mode | Usage |
| --------------------------- | ----------- | ---------- | ------------------------------- |
| `bg-primary` | Near black | Off-white | Selected dates, buttons |
| `text-primary-foreground` | White | Black | Text on primary backgrounds |
| `bg-secondary` | Light gray | Dark gray | Secondary backgrounds |
| `text-secondary-foreground` | Black | White | Text on secondary backgrounds |
| `bg-accent` | Off-white | Dark gray | Hover states, today marker |
| `text-accent-foreground` | Dark gray | White | Text on accent backgrounds |
| `bg-destructive` | Red | Light red | Error states, important markers |
| `bg-background` | White | Near black | Component backgrounds |
| `text-foreground` | Black | White | Primary text |
| `text-muted-foreground` | Medium gray | Light gray | Secondary text, placeholders |
| `border-border` | Light gray | Dark gray | Borders |
### Usage Example
```jsx
```
### State-based Styling
Day cells automatically receive state attributes for styling:
- `data-state="selected"` - Selected date
- `data-state="today"` - Today's date
- `data-state="disabled"` - Disabled date
- `data-state="outside-month"` - Date from adjacent month
- `data-state="range-start"` / `data-state="range-end"` / `data-state="range-middle"` - Range selection states
## Accessibility
### Keyboard Navigation
- **Tab**: Move focus between interactive elements
- **Enter/Space**: Select a date or activate buttons
- **Arrow Keys**: Navigate between dates (when focus is within calendar)
### Screen Reader
The calendar announces the current month, selected date(s), and day names appropriately.
### ARIA Attributes
- Dates have appropriate role and aria-label attributes
- Selected dates are marked with aria-selected
- Disabled dates have aria-disabled
## Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs.
#### Selection Modes
Calendar supports three selection modes: single date, multiple dates, and date range selection
```jsx
function Example() {
const [singleDate, setSingleDate] = React.useState(new Date());
const [multipleDates, setMultipleDates] = React.useState([new Date()]);
const [dateRange, setDateRange] = React.useState({ from: new Date(), to: null });
const renderCalendar = (mode, value, onChange, title) => (
{title}
);
return (
{renderCalendar("single", singleDate, setSingleDate, "Single Date Selection")}
{renderCalendar("multiple", multipleDates, setMultipleDates, "Multiple Dates Selection")}
{renderCalendar("range", dateRange, setDateRange, "Date Range Selection")}
);
}`
```
#### Month & Year Picker
Enable quick navigation with month and year dropdown selectors
```jsx
function Example() {
const [selected, setSelected] = React.useState(new Date());
return (
);
}`
```
#### With Event Markers
Add visual indicators and markers to highlight specific dates with events or important information
```jsx
function Example() {
const [selected, setSelected] = React.useState(new Date());
const today = new Date();
const year = today.getFullYear();
const monthNum = today.getMonth() + 1;
const month = monthNum < 10 ? '0' + monthNum : monthNum;
// Create markers for the current month with shadcn-style colors
const markers = {};
markers[year + '-' + month + '-05'] = {
type: 'dot',
color: 'hsl(var(--primary))'
};
markers[year + '-' + month + '-12'] = {
type: 'multi-dot',
dots: [
{ color: 'hsl(var(--primary))', key: '1' },
{ color: 'hsl(var(--destructive))', key: '2' }
]
};
markers[year + '-' + month + '-20'] = {
type: 'period',
color: 'hsl(var(--secondary))'
};
markers[year + '-' + month + '-25'] = {
type: 'dot',
color: 'hsl(var(--accent))'
};
return (
MeetingReminderEventDeadline
);
}`
```
#### With Disabled Dates
Disable specific dates or date ranges using minDate, maxDate, or custom disabled dates function
```jsx
function Example() {
const [selected, setSelected] = React.useState(new Date());
const today = new Date();
const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
const twoWeeksLater = new Date(today.getTime() + 14 * 24 * 60 * 60 * 1000);
// Disable weekends
const isWeekend = (date) => {
const day = date.getDay();
return day === 0 || day === 6; // Sunday or Saturday
};
return (
Min/Max Date Constraints
Only dates between today and two weeks later are selectable
Disabled Weekends
Saturdays and Sundays are disabled
);
}`
```
#### With Week Numbers
Display ISO week numbers alongside the calendar for business and project planning use cases
```jsx
function Example() {
const [selected, setSelected] = React.useState(new Date());
return (
);
}`
```
#### Customized Styling
Customize the calendar appearance with different sizes, colors, and styles using Tailwind classes
```jsx
function Example() {
const [selected, setSelected] = React.useState(new Date());
return (
Primary ThemeSecondary Theme
);
}`
```
---
## Card
URL: /ui/docs/components/card/index
# Card
Build beautiful interfaces with the gluestack-ui Card component. This React Native card offers a clean, modern design for any project. Perfect for seamless card design UI integration.
This is an illustration of **Card** component.
```jsx
function Example() {
return (
Quick Start
Start building your next project in minutes
)
}`
```
## Installation
**CLI:**
```bash
npx gluestack-ui add card
```
**Manual:**
### Step 1: Copy and paste the following code into index.tsx in your project.
```jsx
'use client';
type ICardProps = React.ComponentPropsWithoutRef &
VariantProps & {
className?: string;
size?: 'default' | 'sm';
};
const Card = React.forwardRef, ICardProps>(
function Card({ className, size = 'default', ...props }, ref) {
return (
);
}
);
Card.displayName = 'Card';
export { Card };
```
> Note: **Step 2** is optional and only required if you want to add support for [React Server Components](https://vercel.com/blog/understanding-react-server-components), You can skip this and jump to **Step 3** directly if you don't have this requirement.
### Step 2(optional): Copy and paste the following code into index.web.tsx in your project.
```jsx
type ICardProps = React.ComponentPropsWithoutRef<'div'> &
VariantProps & {
className?: string;
size?: 'default' | 'sm';
};
const Card = React.forwardRef(function Card(
{ className, size = 'default', ...props },
ref
) {
return (
);
});
Card.displayName = 'Card';
export { Card };
```
### Step 3: Copy and paste the following code into styles.tsx in your project.
```jsx
const baseStyle = isWeb ? 'flex flex-col relative z-0' : '';
export const cardStyle = tva({
base: `${baseStyle} flex-col bg-card border border-border rounded-xl shadow-sm`,
variants: {
size: {
default: 'p-4 gap-6',
sm: 'p-3 gap-3',
},
},
defaultVariants: {
size: 'default',
},
});
```
### Step 4: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```ts
```
```ts
export default () => ;
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
### Card
Renders a `` on web and a `View` on native.
<>
| Platform | Output |
| --- | --- |
| `Web` | {``} |
| `Native` | {``} |
>
<>
| Prop | Type | Default |
| --- | --- | --- |
| `size` | sm | md | lg | md |
| `variant` | elevated | outline | ghost | filled | elevated |
>
### Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs. Simply copy the code and integrate it into your project.
#### Card with Image
This is an example of a Card component with an image.
```jsx
function App(){
return (
May 15, 2023
The Power of Positive Thinking
Read Blog
);
}`
```
#### Advanced Composition
This is an example of a Card component with other components like Avatar, Image and Button.
```jsx
function App(){
return (
JD
Jane Doe
janedoe@sample.com
81
posts
5,281
followers
281
following
);
}`
```
#### Product Card
This example illustrates a card showcasing a product
```jsx
function App(){
return (
Fashion Clothing
Cotton Kurta
Floral embroidered notch neck thread work cotton kurta in white and
black.
);
}`
```
#### Blog Card
This is an example of a Card component with a blog post.
```jsx
function App(){
return (
May 15, 2023
The Power of Positive Thinking
Discover how the power of positive thinking can transform your life,
boost your confidence, and help you overcome challenges. Explore
practical tips and techniques to cultivate a positive mindset for
greater happiness and success.
RR
John Smith
Motivational Speaker
);
}`
```
---
## Center
URL: /ui/docs/components/center/index
# Center
gluestack-ui Center component helps center-align text and content in React Native. Perfect for creating responsive layouts with React Native text center support.
This is an illustration of **Center** component.
```jsx
function Example() {
return (
This is the center.
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add center
```
**Manual:**
### Step 1: Copy and paste the following code into index.tsx in your project.
```jsx
type ICenterProps = ViewProps & VariantProps;
const Center = React.forwardRef, ICenterProps>(
function Center({ className, ...props }, ref) {
return (
);
}
);
Center.displayName = 'Center';
export { Center };
```
> Note: **Step 2 is optional and only required if you want to add support for [React Server Components](https://vercel.com/blog/understanding-react-server-components), You can skip this and jump to Step 3 directly if you don't have this requirement.**
### Step 2(optional): Copy and paste the following code into index.web.tsx in your project.
```jsx
type ICenterProps = React.ComponentPropsWithoutRef<'div'> &
VariantProps;
const Center = React.forwardRef(function Center(
{ className, ...props },
ref
) {
return (
);
});
Center.displayName = 'Center';
export { Center };
```
### Step 3: Copy and paste the following code into styles.tsx in your project.
```jsx
const baseStyle = isWeb ? 'flex flex-col relative z-0' : '';
export const centerStyle = tva({
base: `justify-center items-center ${baseStyle}`,
});
```
### Step 4: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```ts
```
```ts
export default () =>
;
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Center
{`Renders a
on web and a View on native.`}
<>
| `Web` | {``} |
| --- | --- |
| `Native` | {``} |
>
---
## Chat AI
URL: /ui/docs/components/chat-ai/index
# Chat AI
Build AI chat interfaces with a comprehensive set of components including conversations, messages, attachments, and prompt inputs. Perfect for creating AI-powered chat applications in React & React Native.
This is an illustration of **Chat AI** components.
A basic chat interface example. This is just a mock example—features like streaming and image upload do not work here. However, they function properly when using the useChat hook from Vercel with an OpenRouter API key.
```jsx
function Example() {
const [messages, setMessages] = React.useState([
{
id: '1',
role: 'assistant',
content: 'Hi! I can help you with various tasks. What would you like to work on today?',
},
]);
const AttachmentItem = memo(({ attachment, onRemove }: any) => {
const handleRemove = React.useCallback(
() => onRemove(attachment.id),
[onRemove, attachment.id]
);
return (
);
});
const PromptInputAttachmentsDisplay = () => {
const attachments = usePromptInputAttachments();
if (attachments.files.length === 0) return null;
return (
{attachments.files.map((attachment: any) => (
))}
);
};
const renderMessage: ListRenderItem = ({ item: message, index }) => (
{message.role === 'assistant' && (
)}
);
const handleSubmit = ({ text }: { text: string }) => {
if (!text.trim()) return;
const userMessage = {
id: Date.now().toString(),
role: 'user' as const,
content: text,
};
setMessages((prev) => [...prev, userMessage]);
setTimeout(() => {
const aiReply = {
id: (Date.now() + 1).toString(),
role: 'assistant' as const,
content: "Hello! . This is a fake generated response. In a real app, this would come from Grok, GPT, or any AI model using Vercel AI SDK.",
}
setMessages((prev) => [...prev, aiReply]);
}, 800)
};
return (
(
+
)}
/>
Model
);
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add chat-ai
```
**Manual:**
### Step 1: Copy and paste the following code into your project.
```jsx
// components/ai-elements/index.ts
export * from './message';
export * from './conversation';
export * from './prompt-input';
export * from './file-tree';
export * from './model-selector';
export * from './attatchments';
export const Conversation = ({ children, className }: ConversationProps) => (
{children}
);
export type ConversationEmptyStateProps = {
title?: string;
description?: string;
icon?: ReactElement;
className?: string;
};
export const ConversationEmptyState = ({
title = 'Start a conversation',
description = 'Type a message below to begin chatting',
icon,
className,
}: ConversationEmptyStateProps) => (
{title}
);
export type ConversationContentProps = {
messages: UIMessage[];
renderItem?: ListRenderItem;
estimatedItemSize?: number;
} & Omit, 'data' | 'renderItem'>;
export const ConversationContent = ({
messages,
renderItem,
estimatedItemSize = 140,
...flatListProps
}: ConversationContentProps) => {
const flatListRef = useRef | LegendListRef>(null);
const defaultRenderItem: ListRenderItem = useCallback(
({ item: message, index }) => (
{message.parts
?.filter((part) => part.type === 'text')
.map((part, i) => (
))}
),
[]
);
const { scrollHandler, panGesture } = useKeyboardAwareChat();
const { blankSize } = useBlankContext();
const prevLengthRef = useRef(messages.length);
useEffect(() => {
const shouldScroll =
messages.length > prevLengthRef.current &&
messages[messages.length - 1].role === 'user';
if (shouldScroll && Platform.OS !== 'web') {
flatListRef.current?.scrollToEnd?.();
}
prevLengthRef.current = messages.length;
}, [messages]);
const { messagesContainerHeight } = useBlankContext();
return (
{
const height = e.nativeEvent.layout.height;
messagesContainerHeight.value = height;
}}
>
{messages.length === 0 ? (
) : (
item.id}
scrollEventThrottle={16}
estimatedItemSize={estimatedItemSize}
removeClippedSubviews={Platform.OS !== 'web'}
initialNumToRender={15}
windowSize={10}
maxToRenderPerBatch={10}
contentContainerStyle={{
paddingBottom: blankSize.value,
}}
{...flatListProps}
/>
)}
);
};
export const ConversationScrollButton = () => (
{}}
className="absolute bottom-6 left-1/2 -translate-x-1/2 bg-primary h-11 w-11 items-center justify-center rounded-full shadow-lg"
>
);
export type ConversationDownloadProps = { messages: UIMessage[] };
export const ConversationDownload = ({
messages,
}: ConversationDownloadProps) => {
const handleDownload = useCallback(() => {
const markdown = messages
.map((msg) => {
const role = msg.role === 'user' ? 'User' : 'Assistant';
const text = msg.parts
?.filter((p) => p.type === 'text')
.map((p) => p.text)
.join('\n');
return `**${role}:**\n${text}`;
})
.join('\n\n');
Alert.alert('Download', `Markdown ready (${messages.length} messages)`);
}, [messages]);
return (
);
};
type MessageContextType = {
role: UIMessage['role'];
message?: UIMessage;
};
const MessageContext = createContext(null);
const useMessageContext = () => {
const context = useContext(MessageContext);
if (!context) {
throw new Error(
'MessageToolbar and other children must be used inside '
);
}
return context;
};
const mergeRefs = (
...refs: Array | null | undefined>
): React.RefCallback => {
return (node: T | null) => {
refs.forEach((ref) => {
if (typeof ref === 'function') {
ref(node);
} else if (ref != null) {
(ref as React.MutableRefObject).current = node;
}
});
};
};
export type MessageProps = {
role: UIMessage['role'];
children: React.ReactNode;
className?: string;
index: number;
message: UIMessage;
};
export type MessageContentProps = {
children: React.ReactNode;
className?: string;
};
export const Message = memo(
({ role, children, className, index, message }: MessageProps) => {
const isUserFirstMessage = index === 0;
const {
style: animationStyle,
ref: animRef,
onLayout: animOnLayout,
} = useUserMessageAnimation({ disabled: !isUserFirstMessage });
const { ref: blankRef, onLayout: blankOnLayout } = useBlankSize({
role: 'user',
disabled: !isUserFirstMessage,
});
const combinedRef = useMemo(
() => mergeRefs(animRef, blankRef),
[animRef, blankRef]
);
const contextValue = useMemo(() => ({ role, message }), [role, message]);
if (role === 'user') {
return (
{
animOnLayout?.(event);
blankOnLayout?.(event);
}}
style={animationStyle as ViewStyle}
className={`group mt-4 flex w-full max-w-[95%] flex-col gap-2 ${className || ''}`}
>
{children}
);
}
return (
{children}
);
}
);
export const MessageContent = memo(
({ children, className }: MessageContentProps) => {
const { role } = useMessageContext();
const roleStyles =
role === 'user' ? 'self-end bg-muted max-w-[90%] px-4' : 'self-start ';
return (
{children}
);
}
);
export const MessageResponse = memo(({ message }: { message: UIMessage }) => {
const markdownRules = {
text: (node, children, parent) => {
return (
{node.content}
);
},
ordered_list: (node, children) => {
return (
{children}
);
},
list_item: (node, children, parent) => {
const isOrdered = parent?.type === 'ordered_list';
const index = node.index ?? 0;
return (
{isOrdered ? `${index + 1}.` : '•'}
{children}
);
},
paragraph: (node, children) => {
return (
{children}
);
},
strong: (node, children) => (
{children}
),
em: (node, children) => (
{children}
),
fence: (node) => (
{node.content}
),
code_block: (node) => (
{node.content}
),
code_inline: (node) => (
{node.content}
),
};
if (!message?.parts) {
return {message?.content || ''};
}
const hasText = message.parts.some((p) => p.type === 'text');
const hasFile = message.parts.some((p) => p.type === 'file');
if (!hasText && !hasFile) {
return Thinking...;
}
return (
{message.parts.map((part, index) => {
if (part.type === 'text') {
return (
{part.text || ''}
);
}
if (part.type === 'file') {
let uri = '';
if (part.url) uri = part.url;
else if (part.data && part.mimeType) {
uri = `data:${part.mimeType};base64,${part.data}`;
}
if (!uri) return null;
return (
);
}
return null;
})}
);
});
export type MessageToolbarProps = {
children: React.ReactNode;
className?: string;
message?: UIMessage;
};
export const MessageToolbar = memo(
({ children, className }: MessageToolbarProps) => {
const { role, message } = useMessageContext();
const roleStyles = role === 'user' ? 'self-end' : 'self-start';
const hasText = message?.parts?.some(
(p) => p.type === 'text' && p.text?.length > 0
);
if (!hasText) return null;
if (role === 'user') return null;
return (
{children}
);
}
);
export const MessageAction = ({
onPress,
tooltip,
children,
}: {
onPress?: () => void;
tooltip?: string;
children: React.ReactNode;
}) => {
const handlePress = () => {
if (tooltip) Alert.alert(tooltip);
onPress?.();
};
return (
{children}
);
};
interface MessageBranchContextType {
currentBranch: number;
totalBranches: number;
goToPrevious: () => void;
goToNext: () => void;
branches: React.ReactElement[];
setBranches: (branches: React.ReactElement[]) => void;
}
const MessageBranchContext = createContext(
null
);
const useMessageBranch = () => {
const context = useContext(MessageBranchContext);
if (!context) {
throw new Error(
'MessageBranch components must be used within '
);
}
return context;
};
export const MessageBranch = ({
defaultBranch = 0,
onBranchChange,
children,
className,
}: {
defaultBranch?: number;
onBranchChange?: (index: number) => void;
children: React.ReactNode;
className?: string;
}) => {
const [currentBranch, setCurrentBranch] = useState(defaultBranch);
const [branches, setBranches] = useState([]);
const handleBranchChange = (newBranch: number) => {
setCurrentBranch(newBranch);
onBranchChange?.(newBranch);
};
const goToPrevious = () => {
const newBranch =
currentBranch > 0 ? currentBranch - 1 : branches.length - 1;
handleBranchChange(newBranch);
};
const goToNext = () => {
const newBranch =
currentBranch < branches.length - 1 ? currentBranch + 1 : 0;
handleBranchChange(newBranch);
};
const contextValue: MessageBranchContextType = {
branches,
currentBranch,
goToNext,
goToPrevious,
setBranches,
totalBranches: branches.length,
};
return (
{children}
);
};
export const MessageBranchContent = ({
children,
}: {
children: React.ReactNode;
}) => {
const { currentBranch, setBranches } = useMessageBranch();
const childrenArray = React.Children.toArray(
children
) as React.ReactElement[];
React.useEffect(() => {
if (branches.length !== childrenArray.length) {
setBranches(childrenArray);
}
}, [childrenArray, branches.length]);
return childrenArray.map((branch, index) => (
{branch}
));
};
export const MessageBranchSelector = ({
children,
}: {
children: React.ReactNode;
}) => {
const { totalBranches } = useMessageBranch();
if (totalBranches <= 1) return null;
return {children};
};
export const MessageBranchPrevious = () => (
{}}
className="h-8 w-8 items-center justify-center"
>
);
export const MessageBranchNext = () => (
{}}
className="h-8 w-8 items-center justify-center"
>
);
export const MessageBranchPage = () => {
const { currentBranch, totalBranches } = useMessageBranch();
return (
{currentBranch + 1} of {totalBranches}
);
};
'use client';
// ====================== CONTEXT ======================
type PromptContextType = {
text: string;
setText: (t: string) => void;
handleSubmit: () => void;
isDisabled: boolean;
};
const PromptContext = createContext(null);
const usePrompt = () => {
const context = useContext(PromptContext);
if (!context) throw new Error('Must be inside PromptInput');
return context;
};
// ====================== ATTACHMENTS ======================
const generateId = (): string => {
return (
Math.random().toString(36).substring(2) +
Math.random().toString(36).substring(2)
);
};
export type FileUIPart = {
type: 'file';
filename?: string;
mediaType?: string;
url: string;
};
const AttachmentsContext = createContext(null);
export const usePromptInputAttachments = () => {
const context = useContext(AttachmentsContext);
if (!context) {
throw new Error('usePromptInputAttachments must be used within provider');
}
return context;
};
export const PromptInputProvider = ({ children }: { children: ReactNode }) => {
const [textInput, setTextInput] = useState('');
const [attachmentFiles, setAttachmentFiles] = useState<
(FileUIPart & { id: string })[]
>([]);
const add = useCallback((newFiles: any[] | any) => {
// Accept single object OR array (more flexible)
const filesToAdd = Array.isArray(newFiles) ? newFiles : [newFiles];
setAttachmentFiles((prev) => [...prev, ...filesToAdd]);
}, []);
const remove = useCallback((id: string) => {
setAttachmentFiles((prev) => prev.filter((f) => f.id !== id));
}, []);
const clear = useCallback(() => {
setAttachmentFiles([]);
setTextInput('');
}, []);
// ====================== FIXED IMAGE PICKER ======================
const openImagePicker = useCallback(async () => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ['images'],
allowsMultipleSelection: true,
quality: 0.1,
base64: Platform.OS !== 'web', // base64 only on native
});
if (!result.canceled && result.assets && result.assets.length > 0) {
const newFiles = result.assets.map((asset) => {
let url = '';
if (Platform.OS === 'web') {
url = asset.uri; // blob URL on web
} else {
url = `data:${asset.mimeType || 'image/jpeg'};base64,${asset.base64}`;
}
return {
id: generateId(),
filename: asset.fileName || asset.name || `image-${Date.now()}.jpg`,
mediaType: asset.mimeType || 'image/jpeg',
type: 'file' as const,
url,
};
});
add(newFiles); // ← now correctly passes array
}
}, [add]);
const attachmentsValue = useMemo(
() => ({
files: attachmentFiles,
add,
remove,
clear,
openImagePicker,
}),
[attachmentFiles, add, remove, clear, openImagePicker]
);
return (
{children}
);
};
// ====================== REST OF YOUR COMPONENTS (unchanged) ======================
export const PromptInput = ({
children,
onSubmit,
}: {
children: ReactNode;
onSubmit?: any;
}) => {
const [text, setText] = useState('');
const attachments = usePromptInputAttachments();
const handleSubmit = () => {
onSubmit?.({ text, files: attachments.files });
setText('');
attachments.clear();
};
const isDisabled = !text.trim() && attachments.files.length === 0;
const { height } = useReanimatedKeyboardAnimation();
const inputAnimatedStyle = useAnimatedStyle(
() => ({
transform: [{ translateY: height.value }],
}),
[height.value]
);
return (
{children}
);
};
export const PromptInputBody = ({ children }: { children: ReactNode }) => (
{children}
);
export const PromptInputTextarea = () => {
const { text, setText } = usePrompt();
return (
);
};
export const PromptInputFooter = ({ children }: { children: ReactNode }) => (
{children}
);
export const PromptInputTools = ({ children }: { children: ReactNode }) => (
{children}
);
export const PromptInputButton = ({ children }: { children: ReactNode }) => (
{children}
);
export const PromptInputSubmit = () => {
const { handleSubmit, isDisabled } = usePrompt();
return (
↑
);
};
export const PromptInputActionMenu = ({ children }: any) => <>{children}>;
export const PromptInputActionMenuTrigger = ({ children, ...props }: any) => (
{children ?? +}
);
export const PromptInputActionMenuContent = ({
trigger,
}: {
trigger: (props: any) => React.ReactNode;
}) => {
const attachments = usePromptInputAttachments();
const openDocumentPicker = useCallback(async () => {
const result = await DocumentPicker.getDocumentAsync({ multiple: true });
if (result.assets) {
const newFiles = result.assets.map((asset) => ({
id: generateId(),
filename: asset.name,
mediaType: asset.mimeType,
type: 'file' as const,
url: asset.uri,
}));
attachments.add(newFiles);
}
}, [attachments]);
return (
);
};
export type AttachmentData =
| (FileUIPart & { id: string })
| (SourceDocumentUIPart & { id: string });
export type AttachmentMediaCategory =
| 'image'
| 'video'
| 'audio'
| 'document'
| 'source'
| 'unknown';
export type AttachmentVariant = 'grid' | 'inline' | 'list';
export const getMediaCategory = (
data: AttachmentData
): AttachmentMediaCategory => {
if (data.type === 'source-document') return 'source';
const mediaType = data.mediaType ?? '';
if (mediaType.startsWith('image/')) return 'image';
if (mediaType.startsWith('video/')) return 'video';
if (mediaType.startsWith('audio/')) return 'audio';
if (mediaType.startsWith('application/') || mediaType.startsWith('text/'))
return 'document';
return 'unknown';
};
export const getAttachmentLabel = (data: AttachmentData): string => {
if (data.type === 'source-document') {
return data.title || data.filename || 'Source';
}
const category = getMediaCategory(data);
return data.filename || (category === 'image' ? 'Image' : 'Attachment');
};
interface AttachmentsContextValue {
variant: AttachmentVariant;
}
const AttachmentsContext = createContext(null);
interface AttachmentContextValue {
data: AttachmentData;
mediaCategory: AttachmentMediaCategory;
onRemove?: () => void;
variant: AttachmentVariant;
}
const AttachmentContext = createContext(null);
export const useAttachmentsContext = () =>
useContext(AttachmentsContext) ?? { variant: 'grid' as const };
export const useAttachmentContext = () => {
const ctx = useContext(AttachmentContext);
if (!ctx)
throw new Error('Attachment components must be used within ');
return ctx;
};
export type AttachmentsProps = ComponentProps & {
variant?: AttachmentVariant;
};
export const Attachments = ({
variant = 'grid',
className = '',
children,
...props
}: AttachmentsProps) => {
const contextValue = useMemo(() => ({ variant }), [variant]);
return (
{children}
);
};
export type AttachmentProps = ComponentProps & {
data: AttachmentData;
onRemove?: () => void;
};
export const Attachment = ({
data,
onRemove,
className = '',
children,
...props
}: AttachmentProps) => {
const { variant } = useAttachmentsContext();
const mediaCategory = getMediaCategory(data);
const contextValue = useMemo(
() => ({ data, mediaCategory, onRemove, variant }),
[data, mediaCategory, onRemove, variant]
);
return (
{children}
{onRemove && (
)}
);
};
export type AttachmentPreviewProps = ComponentProps & {
fallbackIcon?: ReactNode;
};
export const AttachmentPreview = ({
fallbackIcon,
className = '',
...props
}: AttachmentPreviewProps) => {
const { data, mediaCategory, variant } = useAttachmentContext();
const renderContent = () => {
if (mediaCategory === 'image' && data.type === 'file' && data.url) {
return (
);
}
const iconMap: Record> = {
audio: Music2,
document: FileText,
image: ImageIcon,
source: Globe,
unknown: Paperclip,
video: Video,
};
const Icon = iconMap[mediaCategory];
const size = variant === 'inline' ? 12 : variant === 'list' ? 24 : 20;
return (
fallbackIcon ??
);
};
return (
{renderContent()}
);
};
export type AttachmentRemoveProps = ComponentProps & {
label?: string;
};
export const AttachmentRemove = ({
label = 'Remove',
className = '',
children,
...props
}: AttachmentRemoveProps) => {
const { onRemove, variant } = useAttachmentContext();
const isWeb = Platform.OS === 'web';
const handlePress = useCallback(() => onRemove?.(), [onRemove]);
if (!onRemove) return null;
return (
);
};
export type AttachmentHoverCardProps = Omit<
ComponentProps,
'trigger' | 'children'
> & {
children: ReactNode;
};
export const AttachmentHoverCard = ({
children,
placement = 'top',
openDelay = 0,
closeDelay = 100,
...props
}: AttachmentHoverCardProps) => {
const childrenArray = React.Children.toArray(children);
const triggerElement = childrenArray[0];
const contentElements = childrenArray.slice(1);
if (!triggerElement) {
return <>{children}>;
}
return (
{
if (React.isValidElement(triggerElement)) {
return React.cloneElement(triggerElement, {
...triggerProps,
...triggerElement.props,
});
}
return triggerElement;
}}
{...props}
>
{contentElements}
);
};
export const AttachmentHoverCardContent = ({
className = '',
children,
...props
}: ComponentProps) => (
{children}
);
export const AttachmentHoverCardText = TooltipText;
export const AttachmentEmpty = ({
className = '',
children,
...props
}: ComponentProps) => (
{children ?? 'No attachments'}
);
type BlankContextType = {
blankSize: SharedValue;
userMessageHeight: SharedValue;
assistantMessageHeight: SharedValue;
messagesContainerHeight: SharedValue;
};
export const BlankContext = createContext(null);
export const BlankProvider = ({ children }: { children: ReactNode }) => {
const blankSize = useSharedValue(0);
const userMessageHeight = useSharedValue(0);
const assistantMessageHeight = useSharedValue(0);
const messagesContainerHeight = useSharedValue(0);
return (
{children}
);
};
export const useBlankContext = () => {
const context = useContext(BlankContext);
if (!context)
throw new Error('useBlankContext must be used inside BlankProvider');
return context;
};
// Context
const ModelSelectorContext = React.createContext<{
open: boolean;
onOpenChange: (open: boolean) => void;
} | null>(null);
const useModelSelector = () => {
const ctx = React.useContext(ModelSelectorContext);
if (!ctx)
throw new Error(
'ModelSelector sub-components must be used inside '
);
return ctx;
};
// ─────────────────────────────────────────────────────────────
// Root
// ─────────────────────────────────────────────────────────────
export type ModelSelectorProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
size?: 'xs' | 'sm' | 'md' | 'lg' | 'full';
children: ReactNode;
};
export const ModelSelector = ({
open,
onOpenChange,
size = 'md',
children,
}: ModelSelectorProps) => {
const childrenArray = Children.toArray(children);
const triggerChildren: ReactNode[] = [];
let contentElement: React.ReactElement | null = null;
childrenArray.forEach((child) => {
if (isValidElement(child) && child.type === ModelSelectorContent) {
contentElement = child;
} else {
triggerChildren.push(child);
}
});
return (
{triggerChildren}
onOpenChange(false)} size={size}>
{contentElement}
);
};
// ─────────────────────────────────────────────────────────────
// Trigger (asChild support)
// ─────────────────────────────────────────────────────────────
export type ModelSelectorTriggerProps = {
asChild?: boolean;
} & ComponentProps;
export const ModelSelectorTrigger = ({
asChild = false,
className,
children,
onPress: userOnPress,
...props
}: ModelSelectorTriggerProps) => {
const { onOpenChange } = useModelSelector();
const handlePress = () => {
onOpenChange(true);
userOnPress?.();
};
if (asChild && isValidElement(children)) {
return cloneElement(children, {
...props,
onPress: handlePress,
className: `${children.props.className || ''} ${className || ''}`,
} as any);
}
return (
{children}
);
};
// ─────────────────────────────────────────────────────────────
// Content — NOW passes size to ModalContent (this fixes the crash)
// ─────────────────────────────────────────────────────────────
export type ModelSelectorContentProps = ComponentProps & {
title?: ReactNode;
size?: 'xs' | 'sm' | 'md' | 'lg' | 'full'; // ← added
};
export const ModelSelectorContent = ({
title = 'Model Selector',
children,
className,
size, // ← receive from root
...props
}: ModelSelectorContentProps) => (
{title}{children}
);
// Rest of the components (unchanged)
export const ModelSelectorInput = ({
className,
...props
}: ComponentProps) => (
);
export const ModelSelectorList = ({
className,
...props
}: ComponentProps) => (
);
export const ModelSelectorEmpty = ({
className,
...props
}: ComponentProps) => (
No models found.
);
export const ModelSelectorGroup = ({
heading,
children,
className,
...props
}: ComponentProps & { heading?: string }) => (
{heading && (
{heading}
)}
{children}
);
export const ModelSelectorItem = ({
isSelected = false,
children,
className,
...props
}: ComponentProps & { isSelected?: boolean }) => {
console.log(children);
return (
{children}
);
};
export const ModelSelectorShortcut = ({
className,
...props
}: ComponentProps) => (
);
export const ModelSelectorSeparator = ({
className,
}: {
className?: string;
}) => ;
export const ModelSelectorLogo = ({ provider }: { provider: string }) => (
{provider.slice(0, 2).toUpperCase()}
);
export const ModelSelectorLogoGroup = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => (
{children}
);
export const ModelSelectorName = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => (
{children}
);
// ====================== Custom Collapsible ======================
type CollapsibleProps = {
open: boolean;
children: ReactNode;
};
const Collapsible = ({ open, children }: CollapsibleProps) => {
const progress = useSharedValue(open ? 1 : 0);
const animatedStyle = useAnimatedStyle(() => ({
opacity: progress.value,
maxHeight: progress.value * 9999,
}),[progress.value]);
React.useEffect(() => {
progress.value = withTiming(open ? 1 : 0, { duration: 180 });
}, [open]);
return {children};
};
// ====================== Contexts ======================
interface FileTreeContextType {
expandedPaths: Set;
togglePath: (path: string) => void;
selectedPath?: string;
onSelect?: (path: string) => void;
}
const FileTreeContext = createContext({
expandedPaths: new Set(),
togglePath: () => {},
});
interface FileTreeFolderContextType {
path: string;
name: string;
isExpanded: boolean;
}
const FileTreeFolderContext = createContext({
path: '',
name: '',
isExpanded: false,
});
// ====================== Main FileTree ======================
export type FileTreeProps = {
expanded?: Set;
defaultExpanded?: Set;
selectedPath?: string;
onSelect?: (path: string) => void;
onExpandedChange?: (expanded: Set) => void;
className?: string;
children: ReactNode;
};
export const FileTree = ({
expanded: controlledExpanded,
defaultExpanded = new Set(),
selectedPath,
onSelect,
onExpandedChange,
className,
children,
}: FileTreeProps) => {
const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);
const expandedPaths = controlledExpanded ?? internalExpanded;
const togglePath = useCallback(
(path: string) => {
const newExpanded = new Set(expandedPaths);
if (newExpanded.has(path)) {
newExpanded.delete(path);
} else {
newExpanded.add(path);
}
setInternalExpanded(newExpanded);
onExpandedChange?.(newExpanded);
},
[expandedPaths, onExpandedChange]
);
const contextValue = useMemo(
() => ({ expandedPaths, togglePath, selectedPath, onSelect }),
[expandedPaths, togglePath, selectedPath, onSelect]
);
return (
{children}
);
};
// ====================== Folder ======================
export type FileTreeFolderProps = {
path: string;
name: string;
children?: ReactNode;
className?: string;
};
export const FileTreeFolder = ({
path,
name,
children,
className,
}: FileTreeFolderProps) => {
const { expandedPaths, togglePath, selectedPath, onSelect } =
useContext(FileTreeContext);
const isExpanded = expandedPaths.has(path);
const isSelected = selectedPath === path;
const handleToggle = useCallback(() => togglePath(path), [togglePath, path]);
const handleSelect = useCallback(() => onSelect?.(path), [onSelect, path]);
const folderContextValue = useMemo(
() => ({ isExpanded, name, path }),
[isExpanded, name, path]
);
return (
{isExpanded ? (
) : (
)}
{name}
{children}
);
};
// ====================== File ======================
export type FileTreeFileProps = {
path: string;
name: string;
icon?: ReactNode;
className?: string;
};
export const FileTreeFile = ({
path,
name,
icon,
className,
}: FileTreeFileProps) => {
const { selectedPath, onSelect } = useContext(FileTreeContext);
const isSelected = selectedPath === path;
const handlePress = useCallback(() => onSelect?.(path), [onSelect, path]);
return (
{icon ?? }
{name}
);
};
// ====================== Actions ======================
export const FileTreeActions = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => {
return (
{children}
);
};
interface UseMessageBlankSizeOptions {
disabled?: boolean;
role: 'user' | 'assistant';
}
export function useBlankSize({
disabled = false,
role,
}: UseMessageBlankSizeOptions) {
const context = useBlankContext();
if (!context) {
throw new Error('useMessageBlankSize must be used inside Chat');
}
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
const { ref, onLayout } = useMessageHeight();
useAnimatedReaction(
() => ({
user: context.userMessageHeight.value,
assistant: context.assistantMessageHeight.value,
keyboard: keyboardHeight.value,
disabled,
}),
({ user, assistant, disabled: isDisabled }) => {
'worklet';
console.log(
user,
assistant,
disabled,
context.messagesContainerHeight.value
);
const pairedHeight = user;
const nextBlank = Math.max(
0,
context.messagesContainerHeight.value - pairedHeight - 46
);
console.log('blank', nextBlank);
context.blankSize.value = nextBlank;
}
);
return { ref, onLayout: onLayout };
}
interface UseMessageRenderedHeightProps {
targetHeight?: SharedValue;
}
export function useMessageHeight(targetHeight?: SharedValue) {
const internalHeight = useSharedValue(0);
const heightToUse = targetHeight || internalHeight;
const ref = useAnimatedRef();
const onLayout = useCallback(
(event: LayoutChangeEvent) => {
'worklet';
heightToUse.value = event.nativeEvent.layout.height;
},
[heightToUse]
);
return {
ref,
onLayout,
targetHeight: heightToUse,
};
}
export const useKeyboardAwareChat = () => {
const keyboardHeight = useSharedValue(0);
const keyboardProgress = useSharedValue(0);
const scrollY = useSharedValue(0);
useKeyboardHandler({
onInteractive: (e) => {
'worklet';
keyboardHeight.value = e.height;
keyboardProgress.value = e.progress;
},
});
const scrollHandler = useAnimatedScrollHandler({
onScroll: (e) => {
scrollY.value = e.contentOffset.y;
},
});
const keyboardOffset = useDerivedValue(() => {
return keyboardHeight.value * keyboardProgress.value;
});
const inputStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateY: -keyboardOffset.value,
},
],
};
},[keyboardHeight.value, keyboardProgress.value]);
const listContentStyle = useAnimatedStyle(() => {
return {
paddingBottom: keyboardOffset.value,
};
},[keyboardOffset.value]);
const panGesture = useMemo(() => {
return Gesture.Pan();
}, []);
return {
scrollHandler,
inputStyle,
listContentStyle,
panGesture,
};
};
export const useUserMessageAnimation = ({
disabled,
}: {
disabled?: boolean;
}) => {
const { ref, onLayout, targetHeight } = useMessageHeight();
const translateY = useSharedValue(0);
const progress = useSharedValue(-1);
const windowHeight = useWindowDimensions().height;
const { userMessageHeight } = useBlankContext();
useAnimatedReaction(
() => {
return targetHeight.value;
},
(messageHeight) => {
userMessageHeight.value = messageHeight;
}
);
useAnimatedReaction(
() => {
const didAnimate = progress.get() !== -1;
if (disabled || didAnimate) return -1;
return targetHeight.value;
},
(messageHeight) => {
if (messageHeight <= 0) return;
userMessageHeight.value = messageHeight;
const startY = Math.max(20, windowHeight - messageHeight);
translateY.value = withTiming(startY, { duration: 0 }, () => {
translateY.value = withSpring(0, {
damping: 22,
stiffness: 160,
mass: 1,
overshootClamping: true,
});
});
progress.value = withTiming(1, {
duration: 380,
});
}
);
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [{ translateY: translateY.value }],
opacity: disabled ? 1 : progress.value,
};
}, [disabled, progress.value, translateY.value]);
const didUserMessageAnimate = useDerivedValue(() =>
disabled ? 1 : progress.get() === 1
);
return {
ref: ref,
onLayout,
didUserMessageAnimate,
style: animatedStyle,
};
};
```
### Step 2: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```jsx
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Conversation
The root container for the chat interface. It provides context for the entire conversation.
<>
| `children` | ReactNode | - | The content of the conversation. |
| --- | --- | --- | --- |
| `className` | string | - | Additional CSS classes to apply. |
>
#### ConversationContent
Displays the list of messages in the conversation.
<>
| `messages` | Array<UIMessage> | - | Array of messages to display. |
| --- | --- | --- | --- |
| `renderItem` | ListRenderItem<UIMessage> | - | Custom render function for messages. |
| `estimatedItemSize` | number | 140 | Estimated height of each message item. |
>
#### Message
Represents an individual message in the conversation.
<>
| `role` | 'user' | 'assistant' | - | The role of the message sender. |
| --- | --- | --- | --- |
| `index` | number | - | The index of the message in the list. |
| `message` | UIMessage | - | The message data object. |
| `children` | ReactNode | - | The content of the message. |
| `className` | string | - | Additional CSS classes to apply. |
>
#### MessageContent
Wrapper for the message content with role-based styling.
<>
| `children` | ReactNode | - | The content to display. |
| --- | --- | --- | --- |
| `className` | string | - | Additional CSS classes to apply. |
>
#### MessageResponse
Renders the message content with markdown support.
<>
| `message` | UIMessage | - | The message object containing content and parts. |
| --- | --- | --- | --- |
>
#### MessageToolbar
Container for message action buttons.
<>
| `children` | ReactNode | - | Action buttons to display. |
| --- | --- | --- | --- |
| `className` | string | - | Additional CSS classes to apply. |
>
#### MessageAction
Individual action button for messages.
<>
| `onPress` | () => void | - | Callback when action is pressed. |
| --- | --- | --- | --- |
| `tooltip` | string | - | Tooltip text for the action. |
| `children` | ReactNode | - | The icon or content of the action. |
>
#### PromptInput
Input area for typing and sending messages.
<>
| `children` | ReactNode | - | Additional content (attachments, etc.). |
| --- | --- | --- | --- |
| `onSubmit` | (message: PromptInputMessage) => void | - | Callback when message is submitted. |
>
#### PromptInputProvider
Provider component for managing prompt input state.
<>
| `children` | ReactNode | - | Child components. |
| --- | --- | --- | --- |
>
#### Attachment Components
Components for handling file attachments.
**Attachments** - Container for multiple attachments.
**Attachment** - Individual attachment item.
**AttachmentPreview** - Preview of the attachment (image or icon).
**AttachmentRemove** - Button to remove an attachment.
## Usage
### Basic Usage
```jsx
function Chat() {
const [messages, setMessages] = useState([]);
const handleSubmit = ({ text, files }) => {
// Handle message submission
setMessages([...messages, { role: 'user', content: text }]);
};
return (
);
}
```
### With AI SDK
```jsx
function AIChat() {
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
fetch: expoFetch,
api: 'http://localhost:8082/api/chat',
}),
});
const handleSubmit = (message) => {
sendMessage({
text: message.text,
files: message.files,
});
};
return (
);
}
```
## Accessibility
### Keyboard Navigation
- **Tab**: Move focus between interactive elements
- **Enter**: Submit message or activate buttons
- **Shift + Enter**: New line in text input
- **Escape**: Close menus or modals
### Screen Reader
- Messages are announced with proper role and labeling
- Attachment previews have descriptive alt text
- Action buttons have accessible labels
### Focus Management
- Focus is managed when opening menus
- Input field maintains focus for continuous typing
- Keyboard-aware scrolling for chat list
## Best Practices
### Do's
- ✅ Use semantic HTML for better accessibility
- ✅ Provide clear feedback for user actions
- ✅ Handle loading states gracefully
- ✅ Validate file types and sizes before upload
- ✅ Implement proper error handling
### Don'ts
- ❌ Don't store sensitive data in local state
- ❌ Don't ignore accessibility requirements
- ❌ Don't forget to handle network errors
- ❌ Don't allow unlimited file uploads
## Related Components
- [Textarea](/ui/textarea) - Multi-line text input
- [Button](/ui/button) - Action buttons
- [Menu](/ui/menu) - Dropdown menus
- [Tooltip](/ui/tooltip) - Helpful hints
---
## Checkbox
URL: /ui/docs/components/checkbox/index
# Checkbox
Build interactive forms with a checkbox component for React & React Native. Features include hover, focus, disabled states, and multiple checkbox selection.
This is an illustration of **Checkbox** component.
```jsx
function Example() {
return (
Label
)
}`
```
## Installation
**CLI:**
### Run the following command:
```bash
npx gluestack-ui add checkbox
```
**Manual:**
### Step 1: Install the following dependencies:
```bash
npm i lucide-react-native
```
### Step 2: Copy and paste the following code into your project.
```jsx
'use client';
const IndicatorWrapper = React.forwardRef<
React.ComponentRef,
ViewProps
>(function IndicatorWrapper({ ...props }, ref) {
return ;
});
const LabelWrapper = React.forwardRef<
React.ComponentRef,
TextProps
>(function LabelWrapper({ ...props }, ref) {
return ;
});
const StyledUIIcon = styled(UIIcon, {
className: "style",
});
const IconWrapper = React.forwardRef<
React.ComponentRef,
React.ComponentPropsWithoutRef
>(function IconWrapper({ ...props }, ref) {
return ;
});
const SCOPE = 'CHECKBOX';
const UICheckbox = createCheckbox({
// @ts-expect-error : internal implementation for r-19/react-native-web
Root:
Platform.OS === 'web'
? withStyleContext(View, SCOPE)
: withStyleContext(Pressable, SCOPE),
Group: View,
Icon: IconWrapper,
Label: LabelWrapper,
Indicator: IndicatorWrapper,
});
const checkboxStyle = tva({
base: 'group/checkbox flex-row items-center justify-start gap-2 web:cursor-pointer data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
});
const checkboxIndicatorStyle = tva({
base: 'justify-center items-center w-4 h-4 shrink-0 rounded border border-input dark:bg-input/30 shadow-xs web:outline-none web:data-[focus-visible=true]:ring-[3px] web:data-[focus-visible=true]:ring-ring/50 web:data-[focus-visible=true]:border-ring data-[checked=true]:bg-primary data-[checked=true]:border-primary dark:data-[checked=true]:bg-primary dark:data-[checked=true]:border-primary data-[invalid=true]:ring-destructive/20 data-[invalid=true]:border-destructive data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
});
const checkboxLabelStyle = tva({
base: 'text-foreground text-sm font-medium font-body web:select-none web:cursor-pointer data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
});
const checkboxIconStyle = tva({
base: 'text-primary-foreground fill-none h-3.5 w-3.5',
});
const CheckboxGroup = UICheckbox.Group;
type ICheckboxProps = React.ComponentPropsWithoutRef &
VariantProps;
const Checkbox = React.forwardRef<
React.ComponentRef,
ICheckboxProps
>(function Checkbox({ className, ...props }, ref) {
return (
);
});
type ICheckboxIndicatorProps = React.ComponentPropsWithoutRef<
typeof UICheckbox.Indicator
> &
VariantProps;
const CheckboxIndicator = React.forwardRef<
React.ComponentRef,
ICheckboxIndicatorProps
>(function CheckboxIndicator({ className, ...props }, ref) {
return (
);
});
type ICheckboxLabelProps = React.ComponentPropsWithoutRef<
typeof UICheckbox.Label
> &
VariantProps;
const CheckboxLabel = React.forwardRef<
React.ComponentRef,
ICheckboxLabelProps
>(function CheckboxLabel({ className, ...props }, ref) {
return (
);
});
type ICheckboxIconProps = React.ComponentPropsWithoutRef<
typeof UICheckbox.Icon
> &
VariantProps;
const CheckboxIcon = React.forwardRef<
React.ComponentRef,
ICheckboxIconProps
>(function CheckboxIcon({ className, size, ...props }, ref) {
if (typeof size === 'number') {
return (
);
} else if (
(props.height !== undefined || props.width !== undefined) &&
size === undefined
) {
return (
);
}
return (
);
});
Checkbox.displayName = 'Checkbox';
CheckboxIndicator.displayName = 'CheckboxIndicator';
CheckboxLabel.displayName = 'CheckboxLabel';
CheckboxIcon.displayName = 'CheckboxIcon';
export {
Checkbox, CheckboxGroup, CheckboxIcon, CheckboxIndicator,
CheckboxLabel
};
```
### Step 3: Update the import paths to match your project setup.
## API Reference
To use this component in your project, include the following import statement in your file.
```jsx
```
```jsx
export default () => (
);
```
### Component Props
This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.
#### Checkbox
Contains all Checkbox related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
<>
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string | - | The value to be used in the checkbox input. This is the value that will be returned on form submission. |
| `onChange` | (value: boolean) => void | - | Function called when the state of the checkbox changes. |
| `defaultIsChecked` | bool | false | If true, the checkbox will be initially checked. |
| `isChecked` | bool | false | When true, the checkbox will be checked. You'll need to pass onChange to update it's value (since it's now controlled). |
| `isDisabled` | bool | false | To manually set disable to the checkbox. |
| `isInvalid` | bool | false | To manually set invalid to the checkbox. |
| `isReadOnly` | bool | false | To manually set read-only to the checkbox. |
| `isHovered` | bool | false | To manually set hover to the checkbox. |
| `isFocusVisible` | bool | false | To manually set focus visible state to the checkbox. |
| `isIndeterminate` | bool | false | To manually set indeterminate to the checkbox. |
>
#### CheckboxIndicator
Contains all indicators related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
#### CheckboxIcon
Contains all Icon related layout style props and actions. It inherits all the properties of gluestack Style's [AsForwarder](/style/docs/api/as-forwarder) component.
<>
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `forceMount` | boolean | false | Forces mounting when more control is needed, useful for animations with React libraries. |
>
#### CheckboxLabel
Contains all Label related layout style props and actions. It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.
#### CheckboxGroup
Contains all Group related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.
<>
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | string[] | - | The value of the checkbox group. |
| `onChange` | {`(values: Array) => void`} | - | The callback fired when any children Checkbox is checked or unchecked. |
| `isDisabled` | bool | false | To manually set disable to the checkbox. |
| `isInvalid` | bool | false | To manually set invalid to the checkbox. |
| `isReadOnly` | bool | false | To manually set read-only to the checkbox. |
>
### Features
- Keyboard support for actions.
- Support for hover, focus and active states.
### Accessibility
We have outlined the various features that ensure the Checkbox component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards.Adheres to the [WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/).
#### Keyboard
- `Tab`: Moves focus to the next focusable element.
- `Shift + Tab`: Moves focus to the previous focusable element.
- `Space`: To check or uncheck focused checkbox.
#### Screen Reader
- VoiceOver: When the checkbox is focused, the screen reader will announce it's a checkbox and it's current state (check or uncheck) and it's label.
#### Focus Management
- The `onFocus` and `onBlur` props to manage focus states and provide visual cues to users. This is especially important for users who rely on keyboard navigation.
#### States
- In error state, `aria-invalid` will be passed to indicate that the radio input has an error, and providing support for an `aria-errormessage` to describe the error in more detail.
- In disabled state, `aria-hidden` will be passed to make radio input not focusable.
- In required state, `aria-required` will be passed to indicate that the radio input is required.
### Props
Checkbox component is created using Pressable component from react-native. It extends all the props supported by [React Native Pressable](https://reactnative.dev/docs/pressable#props).
#### Checkbox
<>
| Name | Value | Default |
| --- | --- | --- |
| `size` | lg | md | sm | md |
>
> Note: These props are exclusively applicable when utilizing the default configuration of gluestack-ui/config. If you are using a custom theme, these props may not be available.
### Data Attributes Table
Component receives states as props as boolean values, which are applied as `data-*` attributes. These attributes are then used to style the component via classNames, enabling state-based styling.
<>
| State | Data Attribute | Values |
| --- | --- | --- |
| `hover` | data-hover | `true | false` |
| `active` | data-active | `true | false` |
| `disabled` | data-disabled | `true | false` |
| `focusVisible` | data-focus-visible | `true | false` |
| `invalid` | data-invalid | `true | false` |
| `checked` | data-checked | `true | false` |
>
### Examples
The Examples section provides visual representations of the different variants of the component, allowing you to quickly and easily determine which one best fits your needs. Simply copy the code and integrate it into your project.
#### Multiple Checkbox
Checkbox provide a mutually exclusive selection mechanism, allowing users to choose a multiple option from a set of related choices.
```jsx
function Example() {
const [values, setValues] = React.useState(["Eng"]);
return (
{
setValues(keys)
}}>
FramerInvision StudioAdobe XD
);
}`
```
#### Horizontal
A horizontal component incorporating a checkbox allows for intuitive and space-efficient selection of multiple options within a linear layout.
```jsx
function Example() {
const [values, setValues] = React.useState(["Illustration"]);
return (
{
setValues(keys)
}}>
IllustrationAnimationTypography
);
}`
```
#### With Help Text
A checkbox component with help text provides informative guidance alongside selectable options, ensuring clarity and ease of use.
```jsx
function Example() {
const [values, setValues] = React.useState(["Design"]);
return (
{
setValues(keys)
}}>
DesignSubscribe to updates from the Design FeedMarketingSubscribe to updates from the Marketing Feed
);
}`
```
#### Form Control
A checkbox component integrated with form control enhances the user experience by enabling easy selection and submission of options within a structured input context.
```jsx
function Example() {
return (
Sign up for newslettersDaily BitsEvent UpdatesSponsorshipSubscribe to newsletters for updates
);
}`
```
#### Label Left
A checkbox component with Label left configuration aligns the label to the left, facilitating clear association between the option and its corresponding checkbox.
```jsx
function Example() {
const [values, setValues] = React.useState(["Jane"]);
return (
{
setValues(keys)
}}>
Jane CooperWade WarrenRobert Fox
);
}`
```
#### Controlled
A controlled component architecture incorporates a checkbox component, allowing for precise management of its state and behavior through explicit control mechanisms.
```jsx
function Example() {
const [values, setValues] = React.useState(['UX Research']);
return (
{
setValues(keys);
}}>
UX ResearchSoftware Development
);
}`
```
#### Uncontrolled
An uncontrolled component utilizes a checkbox component, providing a simpler implementation where the checkbox state is managed internally, without explicit control from external sources.
```jsx
function Example() {
const checkboxRef = React.useRef(null);
const handleCheckboxChange = (e) => {
const checkboxValue = checkboxRef.current.checked;
};
return (
ApartmentsResidents
);
}`
```
#### Checkbox Group
The checkbox group component allows users to group checkbox and display them in a horizontal or vertical row for better visual representation and functionality.
```jsx
function Example() {
const [values, setValues] = React.useState([]);
return (