## 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 Message Mark Unread Remind Me Add to Saved Items Delete ) }` ``` ## 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 Message Mark Unread Remind Me Add to Saved Items Delete ) }` ``` #### With SnapPoints ```jsx function App() { const [showActionsheet, setShowActionsheet] = React.useState(false); const handleClose = () => setShowActionsheet(false); return ( <> Mastercard Card 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 Message Mark Unread Remind Me Add to Saved Items Delete ); }` ``` #### 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 supported No 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 ( <> image 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 Richards Ronald Richards Nursing Assistant Arlene McCoy Arlene McCoy Marketing 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 Richards Nursing Assistant Kevin James Web 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 ( SS Ronald Richards Nursing Assistant SS Arlene McCoy Marketing 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 Doe John Doe John Doe John 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 Doe John Doe John Doe John 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 Richards Nursing Assistant Kevin James Web 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 ( SS Ronald Richards Verified Nursing 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 BottomSheet Item 1 Item 2 Item 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 Sheet Search 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 Footer Choose Actions Select 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 BottomSheet Item 1 Item 2 Item 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 ( ( {label} )} > {items.map((item) => ( onValueChange?.(item.value)} > {item.label} ))} ); }); const CalendarHeaderYearSelectRoot = React.forwardRef< React.ElementRef, SelectRootProps >(({ className, items = [], selectedValue, onValueChange, ...props }, ref) => { const label = selectedValue !== undefined ? String(selectedValue) : 'Year'; return ( ( {label} )} > {items.map((item) => ( onValueChange?.(item.value)} > {item.label} ))} ); }); // 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 ( Meeting Reminder Event Deadline ); }` ``` #### 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 Theme Secondary 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 ( image 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 image image ); }` ``` #### Product Card This example illustrates a card showcasing a product ```jsx function App(){ return ( image 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 ( Select Image Select Document ); }; 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 ( {data.filename ); } 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) }}> Framer Invision Studio Adobe 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) }}> Illustration Animation Typography ); }` ``` #### 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) }}> Design Subscribe to updates from the Design Feed Marketing Subscribe 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 newsletters Daily Bits Event Updates Sponsorship Subscribe 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 Cooper Wade Warren Robert 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 Research Software 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 ( Apartments Residents ); }` ``` #### 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 (
Label 1 Label 2
); }` ``` --- ## DateTimePicker URL: /ui/docs/components/date-time-picker/index # DateTimePicker A comprehensive date and time picker component that provides a native experience on mobile platforms and a custom-built interface for web. Perfect for forms, scheduling, and any date/time input needs. This is an illustration of **DateTimePicker** component. A basic DateTimePicker with date and time selection ```jsx function Example() { const [date, setDate] = React.useState(new Date()); return ( ); }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add date-time-picker ``` **Manual:** ### Step 1: Install dependencies ```bash npm install @react-native-community/datetimepicker ``` ### Step 2: Copy and paste the following code into your project. ```jsx 'use client'; const SCOPE = 'DATE_TIME_PICKER'; export type DateTimePickerMode = 'date' | 'time' | 'datetime'; export interface DateTimePickerProps { value?: Date; onChange?: (date: Date | undefined) => void; mode?: DateTimePickerMode; minimumDate?: Date; maximumDate?: Date; locale?: string; timeZoneOffsetInMinutes?: number; is24Hour?: boolean; disabled?: boolean; placeholder?: string; format?: string; display?: 'modal' | 'inline'; // iOS only: 'modal' shows picker in modal with backdrop, 'inline' shows picker directly children?: React.ReactNode; } const DateTimePickerTriggerWrapper = React.forwardRef< React.ComponentRef, React.ComponentProps >(function DateTimePickerTriggerWrapper({ ...props }, ref) { return ; }); const StyledTextInput = styled(TextInput, { className: { target: 'style', nativeStyleToProp: { textAlign: true } }, }); const StyledUIIcon = styled(UIIcon, { className: 'style', }); const UIDateTimePicker = createDateTimePicker({ Root: withStyleContext(View, SCOPE), Trigger: withStyleContext(DateTimePickerTriggerWrapper, SCOPE), Input: StyledTextInput, Icon: StyledUIIcon, }); type IDateTimePickerProps = VariantProps & DateTimePickerProps & { className?: string }; const DateTimePicker = React.forwardRef< React.ComponentRef, IDateTimePickerProps >(function DateTimePicker( { className, value, onChange, mode = 'datetime', minimumDate, maximumDate, locale, timeZoneOffsetInMinutes, is24Hour, disabled, placeholder, format, display = 'modal', // Default to modal for iOS children, ...props }, ref ) { const handleNativeChange = useCallback( (event: any, selectedDate?: Date) => { // The native picker handles its own close if (selectedDate) { onChange?.(selectedDate); } }, [onChange] ); // On iOS, use custom trigger + spinner in modal or inline if (Platform.OS === 'ios') { return ( {children} {/* iOS spinner picker shown in modal or inline based on display prop */} ); } return ( {children} {/* Native picker is rendered directly on Android */} ); }); // Separate component to handle the native picker display (Android only) function DateTimePickerNativeWrapper({ value, mode, minimumDate, maximumDate, timeZoneOffsetInMinutes, is24Hour, onChange, }: { value?: Date; mode: DateTimePickerMode; minimumDate?: Date; maximumDate?: Date; timeZoneOffsetInMinutes?: number; is24Hour?: boolean; onChange: (event: any, date?: Date) => void; }) { const { isOpen, setIsOpen } = useDateTimePicker(); const handleChange = useCallback( (event: any, selectedDate?: Date) => { setIsOpen(false); onChange(event, selectedDate); }, [onChange, setIsOpen] ); // Android doesn't support 'datetime' mode - use two-step picker if (mode === 'datetime') { return ( ); } // Android: Use display="default" which opens system dialogs for date/time if (!isOpen) return null; return ( ); } // Android-specific datetime picker (uses two separate pickers) function AndroidDateTimePicker({ value, minimumDate, maximumDate, is24Hour, isOpen, onChange, setIsOpen, }: { value?: Date; minimumDate?: Date; maximumDate?: Date; is24Hour?: boolean; isOpen: boolean; onChange: (event: any, date?: Date) => void; setIsOpen: (open: boolean) => void; }) { const [step, setStep] = React.useState<'date' | 'time' | null>(null); const [tempDate, setTempDate] = React.useState(value); React.useEffect(() => { if (isOpen) { setStep('date'); setTempDate(value || new Date()); } else { setStep(null); } }, [isOpen, value]); const handleDateChange = React.useCallback( (event: any, selectedDate?: Date) => { if (selectedDate) { setTempDate(selectedDate); setStep('time'); } else { setIsOpen(false); } }, [setIsOpen] ); const handleTimeChange = React.useCallback( (event: any, selectedTime?: Date) => { setIsOpen(false); setStep(null); if (selectedTime && tempDate) { // Combine date and time const combinedDate = new Date(tempDate); combinedDate.setHours(selectedTime.getHours()); combinedDate.setMinutes(selectedTime.getMinutes()); onChange(event, combinedDate); } else if (selectedTime) { onChange(event, selectedTime); } }, [tempDate, onChange, setIsOpen] ); if (!isOpen || !step) return null; if (step === 'date') { return ( ); } return ( ); } // iOS-specific picker with spinner in modal or inline function IOSDateTimePicker({ value, mode, minimumDate, maximumDate, timeZoneOffsetInMinutes, is24Hour, display, onChange, }: { value?: Date; mode: DateTimePickerMode; minimumDate?: Date; maximumDate?: Date; timeZoneOffsetInMinutes?: number; is24Hour?: boolean; display: 'modal' | 'inline'; onChange: (event: any, date?: Date) => void; }) { const { isOpen, setIsOpen } = useDateTimePicker(); const [tempValue, setTempValue] = React.useState(value || new Date()); // Update temp value when picker opens React.useEffect(() => { if (isOpen) { setTempValue(value || new Date()); } }, [isOpen, value]); const handleChange = React.useCallback( (event: any, selectedDate?: Date) => { if (selectedDate) { setTempValue(selectedDate); // Update the parent immediately for live feedback onChange(event, selectedDate); } }, [onChange] ); const handleDone = React.useCallback(() => { setIsOpen(false); }, [setIsOpen]); const handleCancel = React.useCallback(() => { setIsOpen(false); // Revert to original value if (value) { onChange({ type: 'dismissed' }, value); } }, [setIsOpen, onChange, value]); if (!isOpen) return null; // Inline mode: show picker directly without modal if (display === 'inline') { return ( ); } // Modal mode: show picker in modal with backdrop return ( {/* Backdrop - separate touchable area */} {/* Picker container */} Cancel {mode === 'date' ? 'Select Date' : mode === 'time' ? 'Select Time' : 'Select Date & Time'} Done ); } type IDateTimePickerTriggerProps = VariantProps< typeof dateTimePickerTriggerStyle > & React.ComponentProps & { className?: string; }; const DateTimePickerTrigger = React.forwardRef< React.ComponentRef, IDateTimePickerTriggerProps >(function DateTimePickerTrigger( { className, size = 'md', variant = 'outline', ...props }, ref ) { const { disabled, setIsOpen } = useDateTimePicker(); return ( !disabled && setIsOpen(true)} {...props} /> ); }); type IDateTimePickerInputProps = VariantProps & React.ComponentProps & { className?: string }; const DateTimePickerInput = React.forwardRef< React.ComponentRef, IDateTimePickerInputProps >(function DateTimePickerInput({ className, ...props }, ref) { const { size: parentSize, variant: parentVariant } = useStyleContext(SCOPE); const { value, placeholder, format } = useDateTimePicker(); const displayValue = useMemo(() => { if (!value) return ''; if (format) { return formatDate(value, format); } return value.toLocaleString(); }, [value, format]); return ( ); }); type IDateTimePickerIconProps = VariantProps & React.ComponentProps & { className?: string }; const DateTimePickerIcon = React.forwardRef< React.ComponentRef, IDateTimePickerIconProps >(function DateTimePickerIcon({ className, size, ...props }, ref) { const { size: parentSize } = useStyleContext(SCOPE); if (typeof size === 'number') { return ( ); } return ( ); }); function formatDate(date: Date, format: string): string { const pad = (n: number) => n.toString().padStart(2, '0'); return format .replace('YYYY', date.getFullYear().toString()) .replace('MM', pad(date.getMonth() + 1)) .replace('DD', pad(date.getDate())) .replace('HH', pad(date.getHours())) .replace('mm', pad(date.getMinutes())) .replace('ss', pad(date.getSeconds())); } export { DateTimePicker, DateTimePickerIcon, DateTimePickerInput, DateTimePickerTrigger }; ``` ### Step 3: Copy and paste the web implementation code. ```jsx 'use client'; type DateTimePickerMode = 'date' | 'time' | 'datetime'; interface DateTimePickerProps { value?: Date; onChange?: (date: Date | undefined) => void; mode?: DateTimePickerMode; minimumDate?: Date; maximumDate?: Date; disabled?: boolean; placeholder?: string; className?: string; style?: any; } const DateTimePicker = React.forwardRef< React.ComponentRef, DateTimePickerProps >(function DateTimePicker( { value, onChange, mode = 'datetime', minimumDate, maximumDate, disabled, placeholder = 'Select date/time', className, style, }, ref ) { const [isOpen, setIsOpen] = useState(false); const [tempDate, setTempDate] = useState(value); const [tempTime, setTempTime] = useState( value ? formatTimeForInput(value) : '' ); const handleDateSelect = useCallback( (date: Date | Date[] | { from: Date; to: Date }) => { if (date instanceof Date) { setTempDate(date); if (mode === 'date') { onChange?.(date); setIsOpen(false); } } }, [mode, onChange] ); const handleTimeChange = useCallback( (e: React.ChangeEvent) => { setTempTime(e.target.value); }, [] ); const handleConfirm = useCallback(() => { if (mode === 'time' && tempTime) { const [hours, minutes] = tempTime.split(':').map(Number); const newDate = new Date(); newDate.setHours(hours, minutes, 0, 0); onChange?.(newDate); } else if (tempDate) { if (mode === 'datetime' && tempTime) { const [hours, minutes] = tempTime.split(':').map(Number); const combinedDate = new Date(tempDate); combinedDate.setHours(hours, minutes, 0, 0); onChange?.(combinedDate); } else { onChange?.(tempDate); } } setIsOpen(false); }, [mode, tempDate, tempTime, onChange]); const handleCancel = useCallback(() => { setTempDate(value); setTempTime(value ? formatTimeForInput(value) : ''); setIsOpen(false); }, [value]); const displayValue = useMemo(() => { if (!value) return ''; if (mode === 'time') return formatTimeForInput(value); if (mode === 'date') return formatDate(value); return `${formatDate(value)} ${formatTimeForInput(value)}`; }, [value, mode]); return ( {/* Trigger Input */} !disabled && setIsOpen(true)} className="w-full" > {/* Modal/Dropdown */} {isOpen && ( {/* Date Picker */} {(mode === 'date' || mode === 'datetime') && ( )} {/* Time Picker */} {(mode === 'time' || mode === 'datetime') && ( )} {/* Actions */} Cancel Confirm )} ); }); // Helper functions function formatDate(date: Date): string { return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', }); } function formatTimeForInput(date: Date): string { const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; } // Export additional components (simplified versions) const DateTimePickerTrigger = React.forwardRef< React.ComponentRef, React.ComponentProps >(function DateTimePickerTrigger({ children, ...props }, ref) { return ( {children} ); }); const DateTimePickerInput = React.forwardRef< React.ComponentRef, React.ComponentProps >(function DateTimePickerInput(props, ref) { return ; }); const DateTimePickerIcon = React.forwardRef< React.ComponentRef, React.ComponentProps >(function DateTimePickerIcon(props, ref) { return ; }); export { DateTimePicker, DateTimePickerTrigger, DateTimePickerInput, DateTimePickerIcon, }; export type { DateTimePickerProps, DateTimePickerMode }; ``` ### Step 4: Copy and paste the styles. ```jsx export const dateTimePickerStyle = tva({ base: '', }); export const dateTimePickerTriggerStyle = tva({ base: 'border border-border rounded flex-row items-center overflow-hidden data-[hover=true]:border-primary/80 data-[focus=true]:border-primary/80 data-[disabled=true]:opacity-40 data-[disabled=true]:data-[hover=true]:border-border/80', variants: { size: { xl: 'min-h-12', lg: 'min-h-11', md: 'min-h-10', sm: 'min-h-9', }, variant: { underlined: 'border-0 border-b rounded-none data-[hover=true]:border-primary/80 data-[focus=true]:border-primary/80 data-[focus=true]:web:shadow-[inset_0_-1px_0_0] data-[focus=true]:web:shadow-primary/80 data-[invalid=true]:border-destructive data-[invalid=true]:web:shadow-destructive', outline: 'data-[focus=true]:border-primary/80 data-[focus=true]:web:shadow-[inset_0_0_0_1px] data-[focus=true]:data-[hover=true]:web:shadow-primary/80 data-[invalid=true]:web:shadow-[inset_0_0_0_1px] data-[invalid=true]:border-destructive data-[invalid=true]:web:shadow-destructive data-[invalid=true]:data-[hover=true]:border-destructive', rounded: 'rounded-full data-[focus=true]:border-primary/80 data-[focus=true]:web:shadow-[inset_0_0_0_1px] data-[focus=true]:web:shadow-primary/80 data-[invalid=true]:border-destructive data-[invalid=true]:web:shadow-destructive', }, }, }); export const dateTimePickerInputStyle = tva({ base: 'px-3 placeholder:text-muted-foreground web:w-full h-full text-foreground/90 pointer-events-none web:outline-none ios:leading-[0px] py-0', parentVariants: { size: { xl: 'text-xl', lg: 'text-lg', md: 'text-base', sm: 'text-sm', }, variant: { underlined: 'px-0', outline: '', rounded: 'px-4', }, }, }); export const dateTimePickerIconStyle = tva({ base: 'text-foreground/50 fill-none', parentVariants: { size: { '2xs': 'h-3 w-3', 'xs': 'h-3.5 w-3.5', 'sm': 'h-4 w-4', 'md': 'h-[18px] w-[18px]', 'lg': 'h-5 w-5', 'xl': 'h-6 w-6', }, }, }); ``` ### Step 5: 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 () => ( ); ``` ### DateTimePicker Props <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | Date | - | The currently selected date/time. | | `onChange` | {'(date: Date | undefined) => void'} | - | Callback when date/time changes. | | `mode` | 'date' | 'time' | 'datetime' | 'datetime' | Selection mode. | | `minimumDate` | Date | - | Minimum selectable date. | | `maximumDate` | Date | - | Maximum selectable date. | | `locale` | string | - | Locale for formatting (e.g., 'en-US'). | | `is24Hour` | boolean | - | Use 24-hour format for time (Android only). | | `disabled` | boolean | false | Disable the picker. | | `placeholder` | string | - | Placeholder text when no date selected. | | `format` | string | - | Custom display format (e.g., 'YYYY-MM-DD HH:mm'). | ### DateTimePickerTrigger Props <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `size` | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Size of the trigger. | | `variant` | 'outline' | 'underlined' | 'rounded' | 'outline' | Visual variant. | ### DateTimePickerInput Props Displays the selected date/time value. Inherits all TextInput props. ### DateTimePickerIcon Props <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `as` | Component | - | Icon component to render. | | `size` | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Size of the icon. | ## Format Patterns When using the `format` prop, you can use these patterns: | Pattern | Description | Example | | ------- | -------------------- | ------- | | YYYY | Full year | 2024 | | MM | Month (01-12) | 01 | | DD | Day of month (01-31) | 15 | | HH | Hours (00-23) | 14 | | mm | Minutes (00-59) | 30 | | ss | Seconds (00-59) | 45 | ## Features - **Cross-platform**: Uses native pickers on iOS/Android, custom on web - **Expo Go compatible**: Works without ejecting or building - **Multiple modes**: Date only, Time only, or DateTime combined - **Fully accessible**: Keyboard navigation, screen reader support - **Customizable styling**: Support for sizes, variants, and custom themes - **Min/Max date restrictions**: Limit selectable date ranges - **Locale support**: International date/time formatting ## Accessibility - **Keyboard Navigation**: Tab to focus, Enter/Space to open, Escape to close - **Screen Readers**: Announces selected date and picker state - **ARIA Labels**: Proper labeling for input and dialog ## 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. #### Different Modes DateTimePicker supports three modes: date only, time only, and combined datetime selection ```jsx function Example() { const [date, setDate] = React.useState(new Date()); const [time, setTime] = React.useState(new Date()); const [dateTime, setDateTime] = React.useState(new Date()); return ( Date Only Time Only (12-hour) Date & Time ); }` ``` #### Variants & Sizes DateTimePicker supports multiple visual variants (outline, underlined, rounded) and sizes (sm, md, lg, xl) ```jsx function Example() { const [date, setDate] = React.useState(new Date()); return ( Outline Variant (Default) Underlined Variant Rounded Variant Different Sizes ); }` ``` #### With Form Control Use DateTimePicker with FormControl for proper labeling, helper text, and validation states ```jsx function Example() { const [date, setDate] = React.useState(null); const [isInvalid, setIsInvalid] = React.useState(false); // Validate that date is not in the past React.useEffect(() => { if (date) { const today = new Date(); today.setHours(0, 0, 0, 0); setIsInvalid(date < today); } }, [date]); return ( Event Date Choose a future date for your event Event date cannot be in the past ); }` ``` --- ## Divider URL: /ui/docs/components/divider/index # Divider gluestack-ui's Divider component ensures a well-structured interface. Use the Divider component for clean content separation in your design with flexible orientation options. This is an illustration of **Divider** component. ```jsx function Example() { return (
Easy Difficult
) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add divider ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```jsx 'use client'; const dividerStyle = tva({ base: 'bg-border', variants: { orientation: { vertical: 'w-px h-full', horizontal: 'h-px w-auto', }, }, }); type IUIDividerProps = React.ComponentPropsWithoutRef & VariantProps; const Divider = React.forwardRef< React.ComponentRef, IUIDividerProps >(function Divider({ className, orientation = 'horizontal', ...props }, ref) { return ( ); }); Divider.displayName = 'Divider'; export { Divider }; ``` ### 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 ``` ```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. #### Divider It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. ### Props Divider 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. #### Divider <> | Name | Value | Default | | --- | --- | --- | | `orientation` | vertical | horizontal | horizontal | ### 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. #### Variants A Divider component with different layouts offers versatile options for visually dividing content in various arrangements, such as horizontal or vertical orientations, enabling flexible and visually appealing designs for organizing and structuring elements within a user interface. ```jsx function Example() { return ( gluestack-ui Universal component library Installation API Reference Examples ); }` ``` #### Orientation A Divider component with a specified divider orientation allows for clear visual separation of content, either horizontally or vertically, providing a structured and organized layout within a user interface. ```jsx function Example() { return ( HEALTH Benefits of Oranges Oranges are a great source of vitamin C, which is essential for a healthy immune system. Wade Warrem 6th Oct, 2019 5 mins read TECHNOLOGY How AI can benefit your business AI can automate tasks and processes, allowing for increasing efficiency and productivity. Wade Warrem 6th Oct, 2019 5 mins read ); }` ``` #### With & Without Inset A Divider component used with or without inset adds visual hierarchy and distinction by creating a dividing line either with or without indentation, providing options for organizing and structuring content within a user interface. ```jsx function Example() { return ( ); }` ``` #### Adding content within a Divider A Divider component with added content allows for the inclusion of additional text or elements alongside the dividing line, enhancing the visual and informational aspects of the divider while providing a seamless integration of content within a user interface. ```jsx function Example() { return ( Search Results TECHNOLOGY How AI can benefit your business AI can automate tasks and processes, allowing for increasing efficiency and productivity. 5 mins read ); }` ``` --- ## Drawer URL: /ui/docs/components/drawer/index # Drawer Implement a responsive Drawer component in React & React Native for navigation and content display. Learn how to install, customize, and integrate it into your project. This is an illustration of **Drawer** component. ```jsx function Example() { const [showDrawer, setShowDrawer] = React.useState(false); return ( <> { setShowDrawer(false); }} > Drawer This is the basic drawer component. ); }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add drawer ``` **Manual:** > Note: At present, we have integrated `react-native-reanimated` for animation. You have the option to remove this and implement your own custom animation wrapper. ### Step 1: Install the following dependencies: ```bash npm i react-native-reanimated ``` ### Step 2: Copy and paste the following code into your project. ```ts 'use client'; const SCOPE = 'MODAL'; const AnimatedPressable = Animated.createAnimatedComponent(Pressable); const AnimatedView = Animated.createAnimatedComponent(View); const UIDrawer = createDrawer({ Root: withStyleContext(View as any, SCOPE), Backdrop: AnimatedPressable, Content: AnimatedView, Body: ScrollView, CloseButton: Pressable, Footer: View, Header: View, }); const drawerStyle = tva({ base: 'w-full h-full web:pointer-events-none relative', variants: { size: { sm: '', md: '', lg: '', full: '', }, anchor: { left: 'items-start', right: 'items-end', top: 'justify-start', bottom: 'justify-end', }, }, }); const drawerBackdropStyle = tva({ base: 'absolute left-0 top-0 right-0 bottom-0 bg-[#000]/50 web:cursor-default', }); const drawerContentStyle = tva({ base: 'bg-background shadow-hard-5 p-6 absolute', parentVariants: { size: { sm: '', md: '', lg: '', full: '', }, anchor: { left: 'h-full border-r border-border/80', right: 'h-full border-l border-border/80', top: 'w-full border-b border-border/80 rounded-b-xl', bottom: 'w-full border-t border-border/80 rounded-t-xl', }, }, parentCompoundVariants: [ { size: 'sm', anchor: 'left', class: 'sm:w-1/4 w-2/5', }, { size: 'sm', anchor: 'right', class: 'sm:w-1/4 w-2/5', }, { size: 'sm', anchor: 'top', class: 'h-1/4', }, { size: 'sm', anchor: 'bottom', class: 'h-1/4', }, { size: 'md', anchor: 'left', class: 'w-1/2', }, { size: 'md', anchor: 'right', class: 'w-1/2', }, { size: 'md', anchor: 'top', class: 'h-1/2', }, { size: 'md', anchor: 'bottom', class: 'h-1/2', }, { size: 'lg', anchor: 'left', class: 'w-3/4', }, { size: 'lg', anchor: 'right', class: 'w-3/4', }, { size: 'lg', anchor: 'top', class: 'h-3/4', }, { size: 'lg', anchor: 'bottom', class: 'h-3/4', }, { size: 'full', anchor: 'left', class: 'w-full', }, { size: 'full', anchor: 'right', class: 'w-full', }, { size: 'full', anchor: 'top', class: 'h-full', }, { size: 'full', anchor: 'bottom', class: 'h-full', }, ], }); const drawerCloseButtonStyle = tva({ base: 'z-10 rounded-sm p-2 data-[focus-visible=true]:bg-accent web:cursor-pointer web:outline-0 data-[hover=true]:bg-accent/50', }); const drawerHeaderStyle = tva({ base: 'justify-between items-center flex-row pb-4', }); const drawerBodyStyle = tva({ base: 'mt-4 mb-6 shrink-0', }); const drawerFooterStyle = tva({ base: 'flex-col-reverse gap-2 sm:flex-row sm:justify-end pt-4', }); type IDrawerProps = React.ComponentProps & VariantProps & { className?: string }; type IDrawerBackdropProps = React.ComponentProps & VariantProps & { className?: string }; type IDrawerContentProps = React.ComponentProps & VariantProps & { className?: string }; type IDrawerHeaderProps = React.ComponentProps & VariantProps & { className?: string }; type IDrawerBodyProps = React.ComponentProps & VariantProps & { className?: string }; type IDrawerFooterProps = React.ComponentProps & VariantProps & { className?: string }; type IDrawerCloseButtonProps = React.ComponentProps< typeof UIDrawer.CloseButton > & VariantProps & { className?: string }; const Drawer = React.forwardRef< React.ComponentRef, IDrawerProps >(function Drawer({ className, size = 'md', anchor = 'left', ...props }, ref) { return ( ); }); const DrawerBackdrop = React.forwardRef< React.ComponentRef, IDrawerBackdropProps >(function DrawerBackdrop({ className, ...props }, ref) { return ( ); }); const DrawerContent = React.forwardRef< React.ComponentRef, IDrawerContentProps >(function DrawerContent({ className, ...props }, ref) { const { size: parentSize, anchor: parentAnchor } = useStyleContext(SCOPE); // Calculate positioning classes const customClass = parentAnchor === 'left' || parentAnchor === 'right' ? `top-0 ${parentAnchor === 'left' ? 'left-0' : 'right-0'}` : `left-0 ${parentAnchor === 'top' ? 'top-0' : 'bottom-0'}`; // Select entering and exiting animations based on anchor const enteringAnimation = parentAnchor === 'left' ? SlideInLeft.duration(200).easing(Easing.in(Easing.cubic)) : parentAnchor === 'right' ? SlideInRight.duration(200) : parentAnchor === 'top' ? SlideInUp.duration(200) : SlideInDown.duration(200); const exitingAnimation = parentAnchor === 'left' ? SlideOutLeft.duration(200) : parentAnchor === 'right' ? SlideOutRight.duration(200) : parentAnchor === 'top' ? SlideOutUp.duration(200) : SlideOutDown.duration(200); return ( ); }); const DrawerHeader = React.forwardRef< React.ComponentRef, IDrawerHeaderProps >(function DrawerHeader({ className, ...props }, ref) { return ( ); }); const DrawerBody = React.forwardRef< React.ComponentRef, IDrawerBodyProps >(function DrawerBody({ className, ...props }, ref) { return ( ); }); const DrawerFooter = React.forwardRef< React.ComponentRef, IDrawerFooterProps >(function DrawerFooter({ className, ...props }, ref) { return ( ); }); const DrawerCloseButton = React.forwardRef< React.ComponentRef, IDrawerCloseButtonProps >(function DrawerCloseButton({ className, ...props }, ref) { return ( ); }); Drawer.displayName = 'Drawer'; DrawerBackdrop.displayName = 'DrawerBackdrop'; DrawerContent.displayName = 'DrawerContent'; DrawerHeader.displayName = 'DrawerHeader'; DrawerBody.displayName = 'DrawerBody'; DrawerFooter.displayName = 'DrawerFooter'; DrawerCloseButton.displayName = 'DrawerCloseButton'; export { Drawer, DrawerBackdrop, DrawerBody, DrawerCloseButton, DrawerContent, DrawerFooter, DrawerHeader }; ``` ### 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. #### Drawer Contains all View related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view#props) component. <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `isOpen` | boolean | - | If true, the drawer will open. Useful for controllable state behavior. | | `onClose` | () => any | - | Callback invoked when the drawer is closed. | | `defaultIsOpen` | boolean | - | Specifies the default open state of the Drawer | | `initialFocusRef` | {`React.RefObject`} | - | The ref of element to receive focus when the drawer opens. | | `finalFocusRef` | {`React.RefObject`} | - | The ref of element to receive focus when the drawer closes | | `avoidKeyboard` | boolean | - | If true, the Drawer will avoid the keyboard. | | `closeOnOverlayClick` | boolean | - | If true, the Drawer will close when the overlay is clicked. | | `isKeyboardDismissable` | boolean | - | If true, the keyboard can dismiss the Drawer | | `children` | any | - | The content to display inside the Drawer | #### DrawerBackdrop 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. #### DrawerContent 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. <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `focusable` | boolean | false | If true, Drawer Content will be focusable. | #### DrawerHeader It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### DrawerCloseButton It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### DrawerBody It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component #### DrawerFooter It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. ### Props Modal 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. #### Modal <> | Name | Value | Default | | --- | --- | --- | | `size` | xs | sm | md | lg | full | sm | | `anchor` | left | right | top | bottom | left | ### 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. #### Filter ```jsx function Example() { const [showDrawer, setShowDrawer] = React.useState(false); const [categories, setCategories] = React.useState([]); const [brands, setBrands] = React.useState([]); const [colors, setColors] = React.useState([]); return ( <> { setShowDrawer(false); }} > FILTERS Categories { setCategories(keys); }} > Tops (143,234) Bottoms (5,431,234) Price Range 0 10,000 ); }` ``` #### Sidebar Menu ```jsx function Example() { const [showDrawer, setShowDrawer] = React.useState(false); return ( <> { setShowDrawer(false); }} > User Image User Name abc@gmail.com My Profile Saved Address Orders Saved Cards ); }` ``` --- ## Fab URL: /ui/docs/components/fab/index # Fab Improve your React Native app with the FAB component. Learn how to implement a React Native FAB button using gluestack-ui for a smooth UI experience. This is an illustration of **Fab** component. ```jsx function Example() { return ( Quick start ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add fab ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```ts 'use client'; const SCOPE = 'FAB'; const Root = withStyleContext(Pressable, SCOPE); const StyledUIIcon = styled(UIIcon, { className: "style" }); const UIFab = createFab({ Root: Root, Label: Text, Icon: StyledUIIcon, }); const fabStyle = tva({ base: 'group/fab bg-primary rounded-full z-20 p-4 flex-row items-center justify-center absolute hover:bg-primary/90 active:bg-primary/80 disabled:opacity-40 disabled:pointer-events-all disabled:cursor-not-allowed data-[focus=true]:web:outline-none data-[focus-visible=true]:web:ring-2 data-[focus-visible=true]:web:ring-indicator-info shadow-hard-2', variants: { size: { sm: 'px-3.5 py-1.5', md: 'px-4 py-2', lg: 'px-5 py-3', }, placement: { 'top right': 'top-4 right-4', 'top left': 'top-4 left-4', 'bottom right': 'bottom-4 right-4', 'bottom left': 'bottom-4 left-4', 'top center': 'top-4 self-center', 'bottom center': 'bottom-4 self-center', }, }, }); const fabLabelStyle = tva({ base: 'text-primary-foreground/90 font-normal font-body tracking-md text-left mx-2', variants: { isTruncated: { true: '', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, size: { 'sm': 'text-sm', 'md': 'text-base', 'lg': 'text-lg', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, }, parentVariants: { size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg', }, }, }); const fabIconStyle = tva({ base: 'text-primary-foreground/90 fill-none', variants: { size: { 'sm': 'h-3.5 w-3.5', 'md': 'h-4 w-4', 'lg': 'h-5 w-5', }, }, }); type IFabProps = Omit, 'context'> & VariantProps; const Fab = React.forwardRef, IFabProps>( function Fab( { size = 'md', placement = 'bottom right', className, ...props }, ref ) { return ( ); } ); type IFabLabelProps = React.ComponentPropsWithoutRef & VariantProps; const FabLabel = React.forwardRef< React.ComponentRef, IFabLabelProps >(function FabLabel( { size, isTruncated = false, bold = false, underline = false, strikeThrough = false, className, ...props }, ref ) { const { size: parentSize } = useStyleContext(SCOPE); return ( ); }); type IFabIconProps = React.ComponentPropsWithoutRef & VariantProps & { height?: number; width?: number; }; const FabIcon = React.forwardRef< React.ComponentRef, IFabIconProps >(function FabIcon({ size, className, ...props }, ref) { const { size: parentSize } = useStyleContext(SCOPE); if (typeof size === 'number') { return ( ); } else if ( (props.height !== undefined || props.width !== undefined) && size === undefined ) { return ( ); } return ( ); }); Fab.displayName = 'Fab'; FabLabel.displayName = 'FabLabel'; FabIcon.displayName = 'FabIcon'; export { Fab, FabIcon, FabLabel }; ``` ### 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. #### Fab Contains all fab related layout style props and actions. It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) component. <> | `placement` | "top left" | "top right" | "bottom left" | "bottom right" | "top center" | "bottom center" | "bottom right" | Placement of the Fab | | --- | --- | --- | --- | | `isHovered` | bool | false | To manually set hover to the fab. | | `isPressed` | bool | false | To manually set pressable state to the fab. | | `isFocused` | bool | false | To manually set focused state to the fab. | | `isDisabled` | bool | false | To manually set disable to the fab. | #### FabLabel 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. #### FabIcon 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. ### 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 Fab 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 fab's action. #### Screen Reader - VoiceOver: When the fab is focused, the screen reader will announce the fab's label and its current state. #### Focus Management - The `onFocus` and `onBlur` props manage focus states and provide visual cues to users. This is especially important for users who rely on keyboard navigation. ### Dependencies The following are the lists of all the libraries and packages the component relies on. This information will help you ensure that your project has all the necessary dependencies installed to use the component. - `@gluestack-ui/utils` - `@react-native-aria/focus` - `@react-native-aria/interactions` ### Props Fab 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). ### 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. <> | `focus` | data-focus | `true | false` | | --- | --- | --- | | `focusVisible` | data-focus-visible | `true | false` | #### Fab <> | `size` | sm | md | lg | md | | --- | --- | --- | | `placement` | top right | top left | bottom right | bottom left | top center | bottom center | bottom right | > 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. #### FAB with Icon A Fab component with an icon adds a visually striking and easily recognizable button that triggers a specific action or function within a user interface. ```jsx function Example() { const data = [ { uri: 'https://images.unsplash.com/photo-1599566150163-29194dcaad36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=774&q=80', name: 'Kevin James', msg: "Hi Rachel, What's up", }, { uri: 'https://images.unsplash.com/photo-1527980965255-d3b416303d12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1180&q=80', name: 'Jacob Jones', msg: 'Good Morning!', }, { uri: 'https://images.unsplash.com/photo-1633332755192-727a05c4013d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1180&q=80', name: 'Albert Flores', msg: 'Coffee?', }, ]; return ( {data.map((chatData, index) => { return ( {chatData.name} {chatData.msg} ); })} ); }` ``` #### FAB with Icon and Text A Fab component with an icon and text combines a visual icon with accompanying text to create a prominent and descriptive button that triggers a specific action or function within a user interface. ```jsx function Example() { return ( Search HEALTH Benefits of Oranges Oranges are a great source of vitamin C, which is essential for a healthy immune system. Wade Warrem 6th Oct, 2019 5 mins read TECHNOLOGY How AI can benefit your business AI can automate tasks and processes, allowing for increasing efficiency and productivity. Wade Warrem 6th Oct, 2019 5 mins read ); }` ``` #### Placement A Fab component with placement options allows for flexible positioning of the button within a user interface, enabling convenient and intuitive access to key actions or functionalities in various locations. ```jsx function Example() { return ( Prepare any feedback or updates. Review progress on goals and projects. Ask challenges and discuss. ); }` ``` #### Custom Placement A Fab component with custom placement allows for flexible positioning of the button according to specific design requirements or user interface preferences, providing tailored and intuitive access to key actions or functionalities. ```jsx function Example() { return ( image Fresh Orange - Imported (Loose) Rs 146(Rs.24.33/pc) DETAILS Oranges are a great source of vitamin C, which is essential for a healthy immune system. Oranges are a great source of vitamin C, which is important for maintaining a healthy immune system. Vitamin C also helps with the absorption of iron and the production of collagen, which supports healthy skin, teeth, and bones. READ MORE ); }` ``` --- ## FormControl URL: /ui/docs/components/form-control/index # FormControl Enhance form usability with FormControl components in React. Manage validation, disabled states, and more. Easy integration for seamless form handling. This is an illustration of **FormControl** component. ```jsx function App () { const [isInvalid, setIsInvalid] = React.useState(false); const [inputValue, setInputValue] = React.useState("12345"); const handleSubmit = () => { if (inputValue.length < 6) { setIsInvalid(true); } else { setIsInvalid(false); } }; return ( Password setInputValue(text)} /> Must be at least 6 characters. At least 6 characters are required. ); }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add form-control ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```tsx 'use client'; const SCOPE = 'FORM_CONTROL'; const formControlStyle = tva({ base: 'flex flex-col', }); const formControlErrorIconStyle = tva({ base: 'text-destructive fill-none h-[18px] w-[18px]', }); const formControlErrorStyle = tva({ base: 'flex flex-row justify-start items-center mt-1 gap-1', }); const formControlErrorTextStyle = tva({ base: 'text-destructive text-xs font-body', variants: { isTruncated: { true: 'web:truncate', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, }, }); const formControlHelperStyle = tva({ base: 'flex flex-row justify-start items-center mt-1 font-body', }); const formControlHelperTextStyle = tva({ base: 'text-foreground/70 font-body text-sm', variants: { isTruncated: { true: 'web:truncate', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, }, }); const formControlLabelStyle = tva({ base: 'flex flex-row justify-start items-center mb-1', }); const formControlLabelTextStyle = tva({ base: 'font-medium text-foreground text-base font-body', variants: { isTruncated: { true: 'web:truncate', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, }, }); const formControlLabelAstrickStyle = tva({ base: 'font-medium text-foreground text-base', variants: { isTruncated: { true: 'web:truncate', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, }, }); type IFormControlLabelAstrickProps = React.ComponentPropsWithoutRef< typeof Text > & VariantProps; const FormControlLabelAstrick = React.forwardRef< React.ComponentRef, IFormControlLabelAstrickProps >(function FormControlLabelAstrick({ className, ...props }, ref) { return ( ); }); const StyledUIIcon = styled(UIIcon, { className: "style" }); export const UIFormControl = createFormControl({ Root: withStyleContext(View, SCOPE), Error: View, ErrorText: Text, ErrorIcon: StyledUIIcon, Label: View, LabelText: Text, LabelAstrick: FormControlLabelAstrick, Helper: View, HelperText: Text, }); type IFormControlProps = React.ComponentProps & VariantProps; const FormControl = React.forwardRef< React.ComponentRef, IFormControlProps >(function FormControl({ className, ...props }, ref) { return ( ); }); type IFormControlErrorProps = React.ComponentProps & VariantProps; const FormControlError = React.forwardRef< React.ComponentRef, IFormControlErrorProps >(function FormControlError({ className, ...props }, ref) { return ( ); }); type IFormControlErrorTextProps = React.ComponentProps< typeof UIFormControl.Error.Text > & VariantProps; const FormControlErrorText = React.forwardRef< React.ComponentRef, IFormControlErrorTextProps >(function FormControlErrorText({ className, ...props }, ref) { return ( ); }); type IFormControlErrorIconProps = React.ComponentProps< typeof UIFormControl.Error.Icon > & VariantProps; const FormControlErrorIcon = React.forwardRef< React.ComponentRef, IFormControlErrorIconProps >(function FormControlErrorIcon({ className, ...props }, ref) { return ( ); }); type IFormControlLabelProps = React.ComponentProps & VariantProps & { htmlFor?: string; role?: string; }; const FormControlLabel = React.forwardRef< React.ComponentRef, IFormControlLabelProps >(function FormControlLabel({ className, ...props }, ref) { return ( ); }); type IFormControlLabelTextProps = React.ComponentProps< typeof UIFormControl.Label.Text > & VariantProps; const FormControlLabelText = React.forwardRef< React.ComponentRef, IFormControlLabelTextProps >(function FormControlLabelText({ className, ...props }, ref) { return ( ); }); type IFormControlHelperProps = React.ComponentProps< typeof UIFormControl.Helper > & VariantProps; const FormControlHelper = React.forwardRef< React.ComponentRef, IFormControlHelperProps >(function FormControlHelper({ className, ...props }, ref) { return ( ); }); type IFormControlHelperTextProps = React.ComponentProps< typeof UIFormControl.Helper.Text > & VariantProps; const FormControlHelperText = React.forwardRef< React.ComponentRef, IFormControlHelperTextProps >(function FormControlHelperText({ className, ...props }, ref) { return ( ); }); FormControl.displayName = 'FormControl'; FormControlError.displayName = 'FormControlError'; FormControlErrorText.displayName = 'FormControlErrorText'; FormControlErrorIcon.displayName = 'FormControlErrorIcon'; FormControlLabel.displayName = 'FormControlLabel'; FormControlLabelText.displayName = 'FormControlLabelText'; FormControlLabelAstrick.displayName = 'FormControlLabelAstrick'; FormControlHelper.displayName = 'FormControlHelper'; FormControlHelperText.displayName = 'FormControlHelperText'; export { FormControl, FormControlError, FormControlErrorText, FormControlErrorIcon, FormControlLabel, FormControlLabelText, FormControlLabelAstrick, FormControlHelper, FormControlHelperText, }; ``` ### 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. #### FormControl It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. <> | `isInvalid` | boolean | false | When true, invalid state. | | --- | --- | --- | --- | | `isRequired` | boolean | false | If true, astrick gets activated. | | `isDisabled` | boolean | false | Disabled state true. | | `isReadOnly` | boolean | false | To manually set read-only state. | #### FormControlLabel It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### FormControlLabelText It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component. #### FormControlHelper It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### FormControlHelperText It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component. #### FormControlError It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### FormControlErrorIcon It inherits all the properties of gluestack Style's [AsForwarder](/style/docs/api/as-forwarder) component. #### FormControlErrorText It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component. ### Features - Keyboard support for actions. - Support for hover, focus and active states. - Option to add your styles or use the default styles. ### 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. #### Form Control with Radio The Radio Component can be incorporated within the FormControl. ```jsx function Example() { const [values, setValues] = React.useState("Mango"); return ( Favourite fruit Mango Apple Orange Choose the fruit you like the most ); }` ``` #### Form Control with Checkbox The Checkbox Component can be incorporated within the FormControl. ```jsx function App() { const [values, setValues] = React.useState(['bits']); return ( Sign up for newsletters { setValues(keys); }} > Daily Bits Event Updates Sponsorship Subscribe to newsletters for updates ); }` ``` #### Form Control with Error Error messages can be displayed using FormControl. ```jsx function App () { return ( Which time slot works best for you? Monday Tuesday Wednesday Choose one time slot for the meeting ) }` ``` #### SignIn Form A form for signing in to an account. ```jsx function App () { const [isInvalid, setIsInvalid] = React.useState(false); const [inputValue, setInputValue] = React.useState(""); const [values, setValues] = React.useState("Male"); const handleSubmit = () => { if (inputValue.length < 6) { setIsInvalid(true); } else { setIsInvalid(false); } }; return ( Get Started Welcome to gluestack-ui - Elevate your app with our components Name setInputValue(text)} /> Gender Male Female Other ); }` ``` #### Form Control with Textarea The Textarea Component can be incorporated within the FormControl. ```jsx function Example() { return ( Comment Enter your feedback or comments ); }` ``` #### Form Control with Form Actions Form Action Buttons can also be utilized in conjunction with FormControl. ```jsx function Example() { return ( ); }` ``` --- ## Grid URL: /ui/docs/components/grid/index # Grid Discover a powerful Grid component for React & React Native with customizable layout and behavior. Perfect for creating responsive grid layouts in your UI design. This is an illustration of **Grid** component. ```jsx function Example() { return ( ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add grid ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```jsx const GridContext = createContext({}); // Parses Tailwind gap/gap-x class to pixels (1 Tailwind unit = 4px) // e.g. gap-3 → 12, gap-x-4 → 16, gap-px → 1 function parseGapFromClassName(className: string = ''): number { if (/\bgap-x-px\b/.test(className)) return 1; const gapXMatch = className.match(/\bgap-x-(\d+(?:\.\d+)?)\b/); if (gapXMatch) return parseFloat(gapXMatch[1]) * 4; if (/\bgap-px\b/.test(className)) return 1; const gapMatch = className.match(/\bgap-(\d+(?:\.\d+)?)\b/); if (gapMatch) return parseFloat(gapMatch[1]) * 4; return 0; } function generateResponsiveNumColumns({ gridClass }: { gridClass: string }) { const gridClassNamePattern = /\b(?:\w+:)?grid-cols-?\d+\b/g; const numColumns = gridClass?.match(gridClassNamePattern); if (!numColumns) { return 12; } const regex = /^(?:(\w+):)?grid-cols-?(\d+)$/; const result: any = {}; numColumns.forEach((classname) => { const match = classname.match(regex); if (match) { const prefix = match[1] || 'default'; const value = parseInt(match[2], 10); result[prefix] = value; } }); return result; } function generateResponsiveColSpans({ gridItemClassName, }: { gridItemClassName: string; }) { const gridClassNamePattern = /\b(?:\w+:)?col-span-?\d+\b/g; const colSpan: any = gridItemClassName?.match(gridClassNamePattern); if (!colSpan) { return 1; } const regex = /^(?:(\w+):)?col-span-?(\d+)$/; const result: any = {}; colSpan.forEach((classname: any) => { const match = classname.match(regex); if (match) { const prefix = match[1] || 'default'; const value = parseInt(match[2], 10); result[prefix] = value; } }); return result; } type IGridProps = ViewProps & VariantProps & { gap?: number; rowGap?: number; columnGap?: number; flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse'; padding?: number; paddingLeft?: number; paddingRight?: number; paddingStart?: number; paddingEnd?: number; borderWidth?: number; borderLeftWidth?: number; borderRightWidth?: number; _extra: { className: string; }; }; const Grid = forwardRef, IGridProps>( function Grid({ className, _extra, children, ...props }, ref) { const [calculatedWidth, setCalculatedWidth] = useState(null); const gridClass = _extra?.className; const obj = generateResponsiveNumColumns({ gridClass }); const responsiveNumColumns: any = useBreakpointValue(obj); // Resolve column gap: explicit prop wins, then parse from className const resolvedGap = props?.columnGap ?? props?.gap ?? parseGapFromClassName(className); const childrenWithProps = React.Children.map(children, (child, index) => { if (React.isValidElement(child)) { return React.cloneElement(child, { key: index } as any); } return child; }); const gridClassMerged = `${Platform.select({ web: gridClass ?? '', })}`; const contextValue = useMemo(() => { return { calculatedWidth, numColumns: responsiveNumColumns, flexDirection: props?.flexDirection || 'row', gap: resolvedGap, }; }, [calculatedWidth, responsiveNumColumns, props, resolvedGap]); const borderLeftWidth = props?.borderLeftWidth || props?.borderWidth || 0; const borderRightWidth = props?.borderRightWidth || props?.borderWidth || 0; const borderWidthToSubtract = borderLeftWidth + borderRightWidth; return ( { const paddingLeftToSubtract = props?.paddingStart || props?.paddingLeft || props?.padding || 0; const paddingRightToSubtract = props?.paddingEnd || props?.paddingRight || props?.padding || 0; const gridWidth = Math.floor(event.nativeEvent.layout.width) - paddingLeftToSubtract - paddingRightToSubtract - borderWidthToSubtract; setCalculatedWidth(gridWidth); }} {...props} > {!!calculatedWidth && childrenWithProps} ); } ); type IGridItemProps = ViewProps & VariantProps & { _extra: { className: string; }; }; const GridItem = forwardRef, IGridItemProps>( function GridItem({ className, _extra, ...props }, ref) { const { calculatedWidth, numColumns, flexDirection, gap } = useContext(GridContext); const gridItemClass = _extra?.className; const responsiveColSpan = (useBreakpointValue( generateResponsiveColSpans({ gridItemClassName: gridItemClass }) ) ?? 1) as number; const flexBasisValue = useMemo(() => { if (!calculatedWidth || !numColumns || responsiveColSpan <= 0) { return 'auto'; } if (flexDirection?.includes('column')) { return 'auto'; } // CSS grid-equivalent formula: // colWidth = (W - gap*(numColumns-1)) / numColumns // itemWidth = colSpan*colWidth + (colSpan-1)*gap // Simplified: colSpan*(W + gap)/numColumns - gap const itemWidth = (responsiveColSpan * (calculatedWidth + gap)) / numColumns - gap; return Math.max(0, Math.floor(itemWidth)); }, [calculatedWidth, responsiveColSpan, numColumns, gap, flexDirection]); return ( ); } ); Grid.displayName = 'Grid'; GridItem.displayName = 'GridItem'; export { Grid, GridItem }; ``` > 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 your project. ```jsx type IGridProps = React.ComponentPropsWithoutRef<'div'> & VariantProps & { gap?: number; rowGap?: number; columnGap?: number; flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse'; padding?: number; paddingLeft?: number; paddingRight?: number; paddingStart?: number; paddingEnd?: number; _extra: { className: string; }; }; const Grid = React.forwardRef(function Grid( { className, _extra, ...props }, ref ) { const gridClass = _extra?.className; const finalGridClass = gridClass ?? ''; return (
); }); type IGridItemProps = React.ComponentPropsWithoutRef<'div'> & VariantProps & { index?: number; _extra: { className: string; }; }; const GridItem = React.forwardRef( function GridItem({ className, _extra, ...props }, ref) { const gridItemClass = _extra?.className; const finalGridItemClass = gridItemClass ?? ''; return (
); } ); Grid.displayName = 'Grid'; GridItem.displayName = 'GridItem'; export { Grid, GridItem }; ``` ### Step 3: Copy and paste the following code into your project. ```jsx const gridBaseStyle = isWeb ? 'grid grid-cols-12' : 'box-border flex-row flex-wrap justify-start'; const gridItemBaseStyle = isWeb ? 'w-auto col-span-1' : ''; export const gridStyle = tva({ base: `w-full ${gridBaseStyle}`, }); export const gridItemStyle = tva({ base: `w-full ${gridItemBaseStyle}`, }); ``` ### 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 () => ( ); ``` > Note: Our responsive grid component is based on a 12-column grid layout. It follows the CSS **grid system** on the web and **flexbox layout** on native devices. Since the grid layout is only supported on the web, passing the grid-cols and col-span classNames inside \_extra is **recommended** for the grid component to work effortlessly on both web and native. Our grid component currently does not support rowSpan. > Note: The immediate parent of **GridItem** must be **Grid**. There should be no higher-order component (HOC) between them. ### Component Props This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration. ### Grid

{`Renders a

on web and a View on native.`}

<> | `Web` | {`
`} | | --- | --- | | `Native` | {``} | #### Props <> | `_extra` | object | - | {`Accepts grid-cols-* className. Value for * can range from 1 to 12. Default value is grid-cols-12.`} | | --- | --- | --- | --- | ### GridItem

{`Renders a

on web and a View on native.`}

<> | `Web` | {`
`} | | --- | --- | | `Native` | {``} | #### Props <> | `_extra` | object | - | {`Accepts col-span-* className. Value for * can range from 1 to 12. Default value is col-span-1.`} | | --- | --- | --- | --- | ### 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. #### Setting the gap between grid items Use the gap-* utilities to change the gap between both rows and columns in grid. ```jsx function Example() { return ( A B C ); }` ``` #### Changing row and column gaps independently Use the gap-x-* and gap-y-* utilities to change the gap between columns and rows independently. ```jsx function Example() { return ( 01 02 03 04 05 06 ); }` ``` #### Nested Grids The following example depicts how easily you can nest grids to create complex layouts with multiple levels of nesting. ```jsx function Example() { return ( 01 02 1 2 3 4 1 2 ); }` ``` #### Responsive Grids You can pass different grid-cols-* and col-span-* values at different breakpoints to create a more responsive layout. ```jsx function Example() { return ( 01 02 03 04 05 06 ); }` ``` #### Why we introduced \_extra prop? Grid Layout is not supported in native. In order to make our component universal, we introduced the `_extra` prop to pass the grid classnames to the grid component. We use the `_extra` prop to pass the grid classnames to the grid component & extract the neccessary styles from the provided classname. This approach provides a SSR friendly solution for responsiveness. --- ## Heading URL: /ui/docs/components/heading/index # Heading Explore the gluestack-ui Heading Component with installation steps, API reference, and usage examples. Customize React Native headings with different sizes and styles easily for your projects. This is an illustration of **Heading** component. ```jsx function Example() { return I am a Heading }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add heading ``` **Manual:** ### Step 1: Copy and paste the following code into index.tsx in your project. ```jsx type IHeadingProps = VariantProps & React.ComponentPropsWithoutRef & { as?: React.ElementType; }; const H1 = styled(H1Base, { className: 'style' }); const H2 = styled(H2Base, { className: 'style' }); const H3 = styled(H3Base, { className: 'style' }); const H4 = styled(H4Base, { className: 'style' }); const H5 = styled(H5Base, { className: 'style' }); const H6 = styled(H6Base, { className: 'style' }); const MappedHeading = memo( forwardRef, IHeadingProps>( function MappedHeading( { size, className, isTruncated, bold, underline, strikeThrough, sub, italic, highlight, ...props }, ref ) { switch (size) { case '5xl': case '4xl': case '3xl': return (

); case '2xl': return (

); case 'xl': return (

); case 'lg': return (

); case 'md': return (

); case 'sm': case 'xs': return (
); default: return (

); } } ) ); const Heading = memo( forwardRef, IHeadingProps>(function Heading( { className, size = 'lg', as: AsComp, ...props }, ref ) { const { isTruncated, bold, underline, strikeThrough, sub, italic, highlight, } = props; if (AsComp) { return ( ); } return ( ); }) ); Heading.displayName = 'Heading'; export { Heading }; ``` > 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 IHeadingProps = VariantProps & React.ComponentPropsWithoutRef<'h1'> & { as?: React.ElementType; }; const MappedHeading = memo( forwardRef(function MappedHeading( { size, className, isTruncated, bold, underline, strikeThrough, sub, italic, highlight, ...props }, ref ) { switch (size) { case '5xl': case '4xl': case '3xl': return (

); case '2xl': return (

); case 'xl': return (

); case 'lg': return (

); case 'md': return (

); case 'sm': case 'xs': return (
); default: return (

); } }) ); const Heading = memo( forwardRef(function Heading( { className, size = 'lg', as: AsComp, ...props }, ref ) { const { isTruncated, bold, underline, strikeThrough, sub, italic, highlight, } = props; if (AsComp) { return ( ); } return ( ); }) ); Heading.displayName = 'Heading'; export { Heading }; ``` ### Step 3: Copy and paste the following code into styles.tsx in your project. ```jsx const baseStyle = isWeb ? 'font-sans tracking-sm bg-transparent border-0 box-border display-inline list-none margin-0 padding-0 position-relative text-start no-underline whitespace-pre-wrap word-wrap-break-word' : ''; export const headingStyle = tva({ base: `text-foreground font-bold font-heading tracking-sm my-0 ${baseStyle}`, variants: { isTruncated: { true: 'truncate', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, size: { '5xl': 'text-6xl', '4xl': 'text-5xl', '3xl': 'text-4xl', '2xl': 'text-3xl', 'xl': 'text-2xl', 'lg': 'text-xl', 'md': 'text-lg', 'sm': 'text-base', 'xs': 'text-sm', }, }, }); ``` ### 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. ```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. #### Heading Heading renders a heading element with a range of sizes and semantic meanings. It offers a variety of props to customize the appearance of the heading. | `5xl, 4xl, 3xl` | `{`

`}` | `H1` | | --- | --- | --- | | `2xl` | `{`

`}` | `H2` | | `xl` | `{`

`}` | `H3` | | `lg` | `{`

`}` | `H4` | | `md` | `{`

`}` | `H5` | | `sm, xs` | `{`
`}` | `H6` | ### Props #### Heading Heading component is created using Heading component from @expo/html-elements on native. It extends all the props supported by [Expo HTML Heading Elements](https://www.npmjs.com/package/@expo/html-elements/v/0.0.5) and the props mentioned below. | `isTruncated` | true | false | false | | --- | --- | --- | | `bold` | true | false | false | | `underline` | true | false | false | | `strikeThrough` | true | false | false | | `sub` | true | false | false | | `italic` | true | false | false | | `highlight` | true | false | false | | `size` | 5xl | 4xl | 3xl | 2xl | xl | lg | md | sm | xs | 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. #### Heading Sizes Heading component offers a range of sizes, including `xs`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl`, `4xl`, and `5xl`, allowing users to customize the size of the heading according to their design requirements. ```jsx function App() { const sizes = [ 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl' ]; return (
{sizes.map((size, index) => ( {size} ))}
); }` ``` --- ## HStack URL: /ui/docs/components/hstack/index # HStack Use the gluestack-ui HStack component in React Native to align elements horizontally. Easily customize layouts with spacing and reverse props. Learn how to install and use HStack today! This is an illustration of **HStack** component. ```jsx function Example() { return ( ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add hstack ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```tsx type IHStackProps = ViewProps & VariantProps; const HStack = React.forwardRef, IHStackProps>( function HStack({ className, space, reversed, ...props }, ref) { return ( ); } ); HStack.displayName = 'HStack'; export { HStack }; ``` ### 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. #### HStack HStack 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. <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `space` | string | - | It sets the space between children. By default there is no space between the HStack items. | | `reversed` | boolean | false | When true, it places the HStack items in reverse direction. | ### Accessibility The accessibility of a HStack is primarily determined by the accessibility information of the components it contains. When you pass an accessible component inside a HStack, its accessibility attributes, such as labels and hints, will be utilized by assistive technologies like screen readers. ### 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. #### HStack Reversed HStack component with the reversed prop reverses the order of horizontally stacked elements, allowing for customized layouts and visual arrangements of content within a user interface. ```jsx function Example() { return ( ); }` ``` --- ## Icon URL: /ui/docs/components/icon/index # Icon Use gluestack-ui Icon component to enhance your web and mobile app with scalable component icons. A must-have React Native icon library for modern development! This is an illustration of **Icon** component. ```jsx function Example() { return ( ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add icon ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```tsx export const UIIcon = createIcon({ Root: PrimitiveIcon, }) as React.ForwardRefExoticComponent< React.ComponentPropsWithoutRef & React.RefAttributes> >; const iconStyle = tva({ base: 'text-foreground fill-none pointer-events-none', variants: { size: { '2xs': 'h-3 w-3', 'xs': 'h-3.5 w-3.5', 'sm': 'h-4 w-4', 'md': 'h-[18px] w-[18px]', 'lg': 'h-5 w-5', 'xl': 'h-6 w-6', }, }, }); const StyledUIIcon = styled(UIIcon, { className: "style" }); type IIConProps = IPrimitiveIcon & VariantProps & React.ComponentPropsWithoutRef; const Icon = React.forwardRef, IIConProps>( function Icon({ size = 'md', className, ...props }, ref) { if (typeof size === 'number') { return ( ); } else if ( (props.height !== undefined || props.width !== undefined) && size === undefined ) { return ( ); } return ( ); } ); export { Icon }; type ParameterTypes = Omit[0], 'Root'>; const createIconUI = ({ ...props }: ParameterTypes) => { const UIIconCreateIcon = createIcon({ Root: Svg, ...props, }) as React.ForwardRefExoticComponent< React.ComponentPropsWithoutRef & React.RefAttributes> >; return React.forwardRef>(function UIIcon( { className, size, ...inComingProps }: VariantProps & React.ComponentPropsWithoutRef, ref ) { return ( ); }); }; export { createIconUI as createIcon }; // All Icons const AddIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); AddIcon.displayName = 'AddIcon'; export { AddIcon }; const AlertCircleIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); AlertCircleIcon.displayName = 'AlertCircleIcon'; export { AlertCircleIcon }; const ArrowUpIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const ArrowDownIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const ArrowRightIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const ArrowLeftIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); ArrowUpIcon.displayName = 'ArrowUpIcon'; ArrowDownIcon.displayName = 'ArrowDownIcon'; ArrowRightIcon.displayName = 'ArrowRightIcon'; ArrowLeftIcon.displayName = 'ArrowLeftIcon'; export { ArrowUpIcon, ArrowDownIcon, ArrowRightIcon, ArrowLeftIcon }; const AtSignIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> <> ), }); AtSignIcon.displayName = 'AtSignIcon'; export { AtSignIcon }; const BellIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); BellIcon.displayName = 'BellIcon'; export { BellIcon }; const CalendarDaysIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); CalendarDaysIcon.displayName = 'CalendarDaysIcon'; export { CalendarDaysIcon }; const CheckIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const CheckCircleIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); CheckIcon.displayName = 'CheckIcon'; CheckCircleIcon.displayName = 'CheckCircleIcon'; export { CheckIcon, CheckCircleIcon }; const ChevronUpIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', d: 'M12 10L8 6L4 10', path: ( <> ), }); const ChevronDownIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const ChevronLeftIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const ChevronRightIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const ChevronsLeftIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const ChevronsRightIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const ChevronsUpDownIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); ChevronUpIcon.displayName = 'ChevronUpIcon'; ChevronDownIcon.displayName = 'ChevronDownIcon'; ChevronLeftIcon.displayName = 'ChevronLeftIcon'; ChevronRightIcon.displayName = 'ChevronRightIcon'; ChevronsLeftIcon.displayName = 'ChevronsLeftIcon'; ChevronsRightIcon.displayName = 'ChevronsRightIcon'; ChevronsUpDownIcon.displayName = 'ChevronsUpDownIcon'; export { ChevronUpIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon, ChevronsRightIcon, ChevronsUpDownIcon, }; const CircleIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); CircleIcon.displayName = 'CircleIcon'; export { CircleIcon }; const ClockIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); ClockIcon.displayName = 'ClockIcon'; export { ClockIcon }; const CloseIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); const CloseCircleIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); CloseIcon.displayName = 'CloseIcon'; CloseCircleIcon.displayName = 'CloseCircleIcon'; export { CloseIcon, CloseCircleIcon }; const CopyIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); CopyIcon.displayName = 'CopyIcon'; export { CopyIcon }; const DownloadIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); DownloadIcon.displayName = 'DownloadIcon'; export { DownloadIcon }; const EditIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); EditIcon.displayName = 'EditIcon'; export { EditIcon }; const EyeIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); EyeIcon.displayName = 'EyeIcon'; const EyeOffIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); EyeOffIcon.displayName = 'EyeOffIcon'; export { EyeIcon, EyeOffIcon }; const FavouriteIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); FavouriteIcon.displayName = 'FavouriteIcon'; export { FavouriteIcon }; const GlobeIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); GlobeIcon.displayName = 'GlobeIcon'; export { GlobeIcon }; const GripVerticalIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); GripVerticalIcon.displayName = 'GripVerticalIcon'; export { GripVerticalIcon }; const HelpCircleIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); HelpCircleIcon.displayName = 'HelpCircleIcon'; export { HelpCircleIcon }; const InfoIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); InfoIcon.displayName = 'InfoIcon'; export { InfoIcon }; const LinkIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); LinkIcon.displayName = 'LinkIcon'; const ExternalLinkIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); ExternalLinkIcon.displayName = 'ExternalLinkIcon'; export { LinkIcon, ExternalLinkIcon }; const LoaderIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); LoaderIcon.displayName = 'LoaderIcon'; export { LoaderIcon }; const LockIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); LockIcon.displayName = 'LockIcon'; export { LockIcon }; const MailIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); MailIcon.displayName = 'MailIcon'; export { MailIcon }; const MenuIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); MenuIcon.displayName = 'MenuIcon'; export { MenuIcon }; const MessageCircleIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); MessageCircleIcon.displayName = 'MessageCircleIcon'; export { MessageCircleIcon }; const MoonIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); MoonIcon.displayName = 'MoonIcon'; export { MoonIcon }; const PaperclipIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); PaperclipIcon.displayName = 'PaperclipIcon'; export { PaperclipIcon }; const PhoneIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); PhoneIcon.displayName = 'PhoneIcon'; export { PhoneIcon }; const PlayIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); PlayIcon.displayName = 'PlayIcon'; export { PlayIcon }; const RemoveIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); RemoveIcon.displayName = 'RemoveIcon'; export { RemoveIcon }; const RepeatIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); RepeatIcon.displayName = 'RepeatIcon'; const Repeat1Icon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); Repeat1Icon.displayName = 'Repeat1Icon'; export { RepeatIcon, Repeat1Icon }; const SearchIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); SearchIcon.displayName = 'SearchIcon'; export { SearchIcon }; const SettingsIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); SettingsIcon.displayName = 'SettingsIcon'; export { SettingsIcon }; const ShareIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); ShareIcon.displayName = 'ShareIcon'; export { ShareIcon }; const SlashIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); SlashIcon.displayName = 'SlashIcon'; export { SlashIcon }; const StarIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); StarIcon.displayName = 'StarIcon'; export { StarIcon }; const SunIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); SunIcon.displayName = 'SunIcon'; export { SunIcon }; const ThreeDotsIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); ThreeDotsIcon.displayName = 'ThreeDotsIcon'; export { ThreeDotsIcon }; const TrashIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); TrashIcon.displayName = 'TrashIcon'; export { TrashIcon }; const UnlockIcon = createIcon({ Root: Svg, viewBox: '0 0 24 24', path: ( <> ), }); UnlockIcon.displayName = 'UnlockIcon'; export { UnlockIcon }; ``` ### 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. #### Icon Pre built icons provided by gluestack-ui inherits all the properties of React Native SVG's [Svg](https://github.com/software-mansion/react-native-svg/tree/main/Example) on native and [svg](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg) html element on web. The Icon components inherits all the properties that third party library provides and can be directly applied as props. ### Features - support of props on any svg icon ### Accessibility We have outlined the various features that ensure the Icon component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards. Role: img is passed <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `size` | 2xs | xs | sm | md | lg | xl | md | The size of the icon. | ### 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. #### All Gluestack Icons Below is a list of all of the icons in the library. ```jsx function Example() { return (
); }` ``` #### Lucide Icons (Recommended) [Lucide](https://lucide.dev/docs/lucide-react-native) is an open source icon library for displaying icons for react-native. `gluestack-ui` provides an easy integration with lucide icons. ```jsx function Example() { return ( ); }` ``` #### SVG & Custom Icons We can directly create Icon using `createIcon` function exported from `@/components/ui/icon` and use it by passing it in `as` prop in `Icon` component. CreateIcon function takes viewBox, d, path etc as parameters. We can use svgs from other icon libraries like fluent, react-icons etc. **Notes to remember while using createIcon function** - Pass svg props such as `viewBox` in `createIcon` directly. - Copy the svg code without the SVG tag directly into the function argument `Path` or `D` or whichever prop justifies your svg. - Replace HTML SVG tags (e.g., ``, `` etc) with the corresponding React Native SVG components (e.g., ``, `` etc) for native. - For any color property (i.e. fill, stroke etc), if you want to override that color, pass "currentColor" instead of a colorCode or className. on Web: `` on Native: `` ```jsx function App() { const GluestackIcon = createIcon({ viewBox: "0 0 32 32", path: ( <> ), }) return }` ``` --- ## Image Viewer URL: /ui/docs/components/image-viewer/index # Image Viewer The Image Viewer component provides an interactive way to display images with advanced gesture support. It includes pinch-to-zoom, double-tap zoom, swipe navigation between images, and slide-to-dismiss functionality. Built with React Native Reanimated for smooth 60fps animations that run on the UI thread. This is an illustration of **Image Viewer** component. A basic image viewer with zoom, swipe navigation, and slide-to-dismiss gestures ```jsx function Example() { const images = [ { url: 'https://images.unsplash.com/photo-1682687220742-aba13b6e50ba?w=800&auto=format&fit=crop&q=60', alt: 'Mountain landscape' }, { url: 'https://images.unsplash.com/photo-1682687221038-404670e01d4c?w=800&auto=format&fit=crop&q=60', alt: 'Ocean waves' }, { url: 'https://images.unsplash.com/photo-1682687982501-1e58ab814714?w=800&auto=format&fit=crop&q=60', alt: 'Desert sunset' } ]; const thumbnailSource = { uri: images[0].url }; return ( Tap an image to view gallery Gallery thumbnail ); }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add image-viewer ``` **Manual:** ### Step 1: Install the following dependencies: ```bash npm i react-native-gesture-handler ``` ### Step 2: Copy and paste the following code into your project. ```jsx 'use client'; const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window'); const AnimatedView = Animated.createAnimatedComponent(View); const AnimatedImage = Animated.createAnimatedComponent(RNImage); const imageViewerStyle = tva({ base: 'w-full', }); const imageViewerModalStyle = tva({ base: 'flex-1 bg-[#000]/50', }); const imageViewerContentStyle = tva({ base: 'flex-1 justify-center items-center overflow-hidden web:flex web:justify-center web:items-center', }); const imageViewerCloseButtonStyle = tva({ base: 'absolute top-12 right-4 z-50 w-10 h-10 rounded-full bg-black/60 justify-center items-center', }); const imageViewerNavigationStyle = tva({ base: 'absolute inset-0 flex-row justify-between items-center px-2', }); const imageViewerNavButtonStyle = tva({ base: 'w-12 h-12 rounded-full bg-black/50 justify-center items-center', }); const imageViewerCounterStyle = tva({ base: 'absolute bottom-12 left-0 right-0 items-center', }); const imageViewerCounterTextStyle = tva({ base: 'text-white text-sm font-medium bg-black/60 px-4 py-2 rounded-full', }); interface ImageItem { url: string; alt?: string; } interface ImageViewerProps { images: ImageItem[]; defaultOpen?: boolean; isOpen?: boolean; onOpenChange?: (isOpen: boolean) => void; onIndexChange?: (index: number) => void; initialIndex?: number; children?: React.ReactNode; } interface ImageViewerTriggerProps { children: React.ReactNode; onPress?: () => void; } // Context for ImageViewer - with default values to prevent errors interface ImageViewerContextType { images: ImageItem[]; currentIndex: number; isOpen: boolean; open: () => void; close: () => void; goNext: () => void; goPrevious: () => void; } const ImageViewerContext = React.createContext({ images: [], currentIndex: 0, isOpen: false, open: () => {}, close: () => {}, goNext: () => {}, goPrevious: () => {}, }); const useImageViewerContext = () => { const context = React.useContext(ImageViewerContext); return context; }; // Single Zoomable Image Component const ZoomableImage = React.memo( React.forwardRef(function ZoomableImage( { image, index, currentIndex, onSwipeLeft, onSwipeRight, onDismiss, onZoomChange, }: { image: ImageItem; index: number; currentIndex: number; onSwipeLeft: () => void; onSwipeRight: () => void; onDismiss: () => void; onZoomChange?: (isZoomed: boolean) => void; }, ref ) { const scale = useSharedValue(1); const savedScale = useSharedValue(1); const translateX = useSharedValue(0); const translateY = useSharedValue(0); const savedTranslateX = useSharedValue(0); const savedTranslateY = useSharedValue(0); const dismissProgress = useSharedValue(0); const opacity = useSharedValue(1); // Notify parent when zoom changes useEffect(() => { const isZoomed = scale.value > 1; if (index === currentIndex && onZoomChange) { onZoomChange(isZoomed); } }, [scale.value, index, currentIndex, onZoomChange]); // Expose reset method to parent React.useImperativeHandle(ref, () => ({ resetZoom: () => { scale.value = 1; savedScale.value = 1; translateX.value = 0; translateY.value = 0; savedTranslateX.value = 0; savedTranslateY.value = 0; if (onZoomChange) { onZoomChange(false); } }, isZoomed: () => scale.value > 1, })); const pinchGesture = Gesture.Pinch() .onUpdate((event) => { scale.value = savedScale.value * event.scale; }) .onEnd(() => { if (scale.value < 1) { scale.value = withSpring(1); savedScale.value = 1; translateX.value = withSpring(0); translateY.value = withSpring(0); savedTranslateX.value = 0; savedTranslateY.value = 0; if (onZoomChange) runOnJS(onZoomChange)(false); } else if (scale.value > 4) { scale.value = withSpring(4); savedScale.value = 4; if (onZoomChange) runOnJS(onZoomChange)(true); } else { savedScale.value = scale.value; if (onZoomChange) runOnJS(onZoomChange)(scale.value > 1); } }); // Pan gesture for when zoomed - allows free movement with edge detection const panGesture = Gesture.Pan() .enabled(scale.value > 1) .onUpdate((event) => { // Calculate max translation bounds for zoomed image const maxTranslateX = ((scale.value - 1) * SCREEN_WIDTH) / 2; const maxTranslateY = ((scale.value - 1) * SCREEN_HEIGHT * 0.8) / 2; // Allow panning within bounds const newTranslateX = savedTranslateX.value + event.translationX; const newTranslateY = savedTranslateY.value + event.translationY; // Clamp to bounds translateX.value = Math.max( -maxTranslateX, Math.min(maxTranslateX, newTranslateX) ); translateY.value = Math.max( -maxTranslateY, Math.min(maxTranslateY, newTranslateY) ); }) .onEnd((event) => { const maxTranslateX = ((scale.value - 1) * SCREEN_WIDTH) / 2; // Check if at horizontal edge and swiped further (outer range offset) const isAtRightEdge = translateX.value >= maxTranslateX - 5; const isAtLeftEdge = translateX.value <= -maxTranslateX + 5; const swipeThreshold = 50; // If at edge and swiped with enough velocity/distance, navigate if ( isAtLeftEdge && event.translationX > swipeThreshold && event.velocityX > 0 ) { // At left edge, swiped right -> go to previous runOnJS(onSwipeRight)(); // Reset zoom after navigation scale.value = withSpring(1); savedScale.value = 1; translateX.value = withSpring(0); translateY.value = withSpring(0); savedTranslateX.value = 0; savedTranslateY.value = 0; if (onZoomChange) runOnJS(onZoomChange)(false); } else if ( isAtRightEdge && event.translationX < -swipeThreshold && event.velocityX < 0 ) { // At right edge, swiped left -> go to next runOnJS(onSwipeLeft)(); // Reset zoom after navigation scale.value = withSpring(1); savedScale.value = 1; translateX.value = withSpring(0); translateY.value = withSpring(0); savedTranslateX.value = 0; savedTranslateY.value = 0; if (onZoomChange) runOnJS(onZoomChange)(false); } else { // Save position within bounds savedTranslateX.value = translateX.value; savedTranslateY.value = translateY.value; } }); // Vertical pan gesture for dismiss - only when NOT zoomed const dismissGesture = Gesture.Pan() .enabled(scale.value <= 1) .activeOffsetY([-10, 10]) // Activate only for vertical movement .failOffsetX([-20, 20]) // Fail if horizontal movement is too much .onUpdate((event) => { dismissProgress.value = Math.abs(event.translationY) / (SCREEN_HEIGHT * 0.3); translateY.value = event.translationY; opacity.value = interpolate( dismissProgress.value, [0, 1], [1, 0.3], Extrapolate.CLAMP ); }) .onEnd((event) => { if (Math.abs(event.translationY) > 120) { runOnJS(onDismiss)(); } else { translateY.value = withSpring(0); opacity.value = withTiming(1, { duration: 200 }); dismissProgress.value = withTiming(0, { duration: 200 }); } }); const lastTapX = useSharedValue(0); const lastTapY = useSharedValue(0); const doubleTapGesture = Gesture.Tap() .numberOfTaps(2) .onBegin((event) => { lastTapX.value = event.x; lastTapY.value = event.y; }) .onEnd(() => { if (scale.value > 1) { scale.value = withSpring(1); savedScale.value = 1; translateX.value = withSpring(0); translateY.value = withSpring(0); savedTranslateX.value = 0; savedTranslateY.value = 0; if (onZoomChange) runOnJS(onZoomChange)(false); } else { const zoomScale = 2.5; scale.value = withSpring(zoomScale); savedScale.value = zoomScale; const centerX = SCREEN_WIDTH / 2; const centerY = SCREEN_HEIGHT * 0.4; const deltaX = centerX - lastTapX.value; const deltaY = centerY - lastTapY.value; const targetX = (deltaX * zoomScale) / zoomScale; const targetY = (deltaY * zoomScale) / zoomScale; translateX.value = withSpring(targetX); translateY.value = withSpring(targetY); savedTranslateX.value = targetX; savedTranslateY.value = targetY; if (onZoomChange) runOnJS(onZoomChange)(true); } }); const composedGesture = Gesture.Race( doubleTapGesture, Gesture.Simultaneous( pinchGesture, Gesture.Exclusive(panGesture, dismissGesture) ) ); const animatedStyle = useAnimatedStyle(() => ({ transform: [ { translateX: translateX.value }, { translateY: translateY.value }, { scale: scale.value }, ], opacity: opacity.value, })); return ( ); }) ); // FlatList-based Image Gallery Component const SlidableImageGallery = React.memo(function SlidableImageGallery({ images, currentIndex, onIndexChange, onDismiss, }: { images: ImageItem[]; currentIndex: number; onIndexChange: (index: number) => void; onDismiss: () => void; }) { const flatListRef = useRef(null); const [localIndex, setLocalIndex] = useState(currentIndex); const scrollOffsetRef = useRef(0); const imageRefsRef = useRef>(new Map()); const [isCurrentImageZoomed, setIsCurrentImageZoomed] = useState(false); // Handle zoom change from current image const handleZoomChange = useCallback((isZoomed: boolean) => { setIsCurrentImageZoomed(isZoomed); }, []); // Track previous currentIndex to detect external changes const prevCurrentIndexRef = useRef(currentIndex); // Scroll to index when currentIndex changes from outside (button press) useEffect(() => { // Only scroll if currentIndex changed externally (not from manual scroll) if (currentIndex !== prevCurrentIndexRef.current && flatListRef.current) { // Reset zoom on current image before scrolling to new one const currentImageRef = imageRefsRef.current.get(localIndex); if (currentImageRef?.resetZoom) { currentImageRef.resetZoom(); } flatListRef.current.scrollToIndex({ index: currentIndex, animated: true, }); setLocalIndex(currentIndex); prevCurrentIndexRef.current = currentIndex; } }, [currentIndex]); // Handle scroll - track position without updating state const handleScroll = useCallback((event: any) => { scrollOffsetRef.current = event.nativeEvent.contentOffset.x; }, []); // Handle scroll end - update index when scroll completes const handleMomentumScrollEnd = useCallback( (event: any) => { const contentOffsetX = event.nativeEvent.contentOffset.x; const newIndex = Math.round(contentOffsetX / SCREEN_WIDTH); if ( newIndex !== localIndex && newIndex >= 0 && newIndex < images.length ) { setLocalIndex(newIndex); prevCurrentIndexRef.current = newIndex; // Update ref to prevent effect from scrolling back onIndexChange(newIndex); } }, [localIndex, images.length, onIndexChange] ); const goNext = useCallback(() => { if (currentIndex < images.length - 1) { onIndexChange(currentIndex + 1); } }, [currentIndex, images.length, onIndexChange]); const goPrevious = useCallback(() => { if (currentIndex > 0) { onIndexChange(currentIndex - 1); } }, [currentIndex, onIndexChange]); // Render each image item const renderItem = useCallback( ({ item, index }: { item: ImageItem; index: number }) => ( { if (ref) { imageRefsRef.current.set(index, ref); } else { imageRefsRef.current.delete(index); } }} image={item} index={index} currentIndex={localIndex} onSwipeLeft={goNext} onSwipeRight={goPrevious} onDismiss={onDismiss} onZoomChange={index === localIndex ? handleZoomChange : undefined} /> ), [localIndex, goNext, goPrevious, onDismiss, handleZoomChange] ); // Get item layout for better performance const getItemLayout = useCallback( (_: any, index: number) => ({ length: SCREEN_WIDTH, offset: SCREEN_WIDTH * index, index, }), [] ); const keyExtractor = useCallback( (item: ImageItem, index: number) => `image-${index}-${item.url}`, [] ); return ( ); }); // Main ImageViewer Component const ImageViewer = React.forwardRef( function ImageViewer( { images, defaultOpen = false, isOpen: controlledIsOpen, onOpenChange, onIndexChange, initialIndex = 0, children, }, ref ) { const [uncontrolledIsOpen, setUncontrolledIsOpen] = useState(defaultOpen); const [currentIndex, setCurrentIndex] = useState(initialIndex); const isControlled = controlledIsOpen !== undefined; const isOpen = isControlled ? controlledIsOpen : uncontrolledIsOpen; useEffect(() => { onIndexChange?.(currentIndex); }, [currentIndex, onIndexChange]); const open = useCallback(() => { if (!isControlled) { setUncontrolledIsOpen(true); } onOpenChange?.(true); }, [isControlled, onOpenChange]); const close = useCallback(() => { if (!isControlled) { setUncontrolledIsOpen(false); } onOpenChange?.(false); }, [isControlled, onOpenChange]); const goNext = useCallback(() => { setCurrentIndex((prev) => Math.min(prev + 1, images.length - 1)); }, [images.length]); const goPrevious = useCallback(() => { setCurrentIndex((prev) => Math.max(prev - 1, 0)); }, []); const contextValue = React.useMemo( () => ({ images, currentIndex, isOpen, open, close, goNext, goPrevious, }), [images, currentIndex, isOpen, open, close, goNext, goPrevious] ); return ( {children} ); } ); // Trigger Component const ImageViewerTrigger = React.forwardRef< React.ElementRef, ImageViewerTriggerProps >(function ImageViewerTrigger({ children, onPress, ...props }, ref) { const { open } = useImageViewerContext(); const handlePress = useCallback(() => { onPress?.(); open(); }, [onPress, open]); return ( {children} ); }); // Content Component (Uses gluestack Overlay for proper portal rendering) // Context provider is placed INSIDE the Overlay so portaled content has access const ImageViewerContent = React.forwardRef< View, { children?: React.ReactNode } >(function ImageViewerContent({ children }, ref) { const context = useImageViewerContext(); const { images, currentIndex, isOpen, close, goNext, goPrevious } = context; const currentImage = images[currentIndex]; return ( {currentImage && ( { if (newIndex > currentIndex) { goNext(); } else if (newIndex < currentIndex) { goPrevious(); } }} onDismiss={close} /> )} {children} ); }); // Close Button Component const ImageViewerCloseButton = React.forwardRef< React.ElementRef, { className?: string } >(function ImageViewerCloseButton({ className, ...props }, ref) { const { close } = useImageViewerContext(); return ( ); }); // Navigation Component const ImageViewerNavigation = React.forwardRef( function ImageViewerNavigation({ className }, ref) { const { goPrevious, goNext, currentIndex, images } = useImageViewerContext(); const canGoPrevious = currentIndex > 0; const canGoNext = currentIndex < images.length - 1; return ( {canGoPrevious && ( )} {canGoNext && ( )} ); } ); // Counter Component const ImageViewerCounter = React.forwardRef( function ImageViewerCounter({ className }, ref) { const { currentIndex, images } = useImageViewerContext(); return ( {currentIndex + 1} / {images.length} ); } ); export { ImageViewer, ImageViewerTrigger, ImageViewerContent, ImageViewerCloseButton, ImageViewerNavigation, ImageViewerCounter, }; export type { ImageItem, ImageViewerProps, ImageViewerTriggerProps }; ``` ### 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 ``` ```tsx 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. #### ImageViewer The root component that manages the image viewer state and provides context to child components. <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `images` | ImageItem[] | - | Array of images to display. Each item should have url and optional alt. | | `defaultOpen` | boolean | false | Whether the viewer is open by default (uncontrolled). | | `isOpen` | boolean | - | Controlled open state. Use with onOpenChange for controlled mode. | | `onOpenChange` | (isOpen: boolean) ={'>'} void | - | Callback when open state changes. | | `initialIndex` | number | 0 | Index of the image to show initially. | #### ImageViewerTrigger The trigger element that opens the image viewer when pressed. <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `children` | React.ReactNode | - | Content to render as the trigger (usually an Image). | | `onPress` | () ={'>'} void | - | Additional callback when trigger is pressed. | #### ImageViewerContent The modal content that displays the image with gesture support. <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `children` | React.ReactNode | - | Child components like CloseButton, Navigation, Counter. | #### ImageViewerCloseButton A button to close the image viewer. <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `className` | string | - | Additional Tailwind classes for styling. | #### ImageViewerNavigation Navigation buttons to move between images in the gallery. <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `className` | string | - | Additional Tailwind classes for styling. | #### ImageViewerCounter Displays the current image index and total count (e.g., "3 / 10"). <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `className` | string | - | Additional Tailwind classes for styling. | ### Gestures The Image Viewer supports the following gestures: - **Pinch to Zoom**: Use two fingers to zoom in and out of images - **Double Tap**: Double tap to zoom in (2.5x) or reset zoom - **Pan**: When zoomed in, drag to pan around the image - **Swipe Left/Right**: Navigate between images in the gallery - **Swipe Up/Down**: Dismiss the viewer by swiping vertically ### Accessibility Adheres to the Dialog [WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/). We have outlined the various features that ensure the Image Viewer component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards. - **ARIA Labels**: All interactive elements have appropriate ARIA labels - **Keyboard Navigation**: Full keyboard support with Tab, Enter, Escape, and Arrow keys - **Focus Management**: Focus is trapped within the modal when open - **Screen Reader Support**: Images announce their alt text to screen readers ### Keyboard Interactions - `Tab` - Moves focus to the next focusable element. - `Shift + Tab` - Moves focus to the previous focusable element. - `Enter` or `Space` - Activates the focused button. - `Escape` - Closes the image viewer. - `Left Arrow` - Navigate to previous image. - `Right Arrow` - Navigate to next image. ### Screen Reader - VoiceOver: Announces image alt text and current position (e.g., "Image 3 of 10") - TalkBack: Provides similar announcements on Android ## Types ```typescript interface ImageItem { url: string; alt?: string; } interface ImageViewerProps { images: ImageItem[]; defaultOpen?: boolean; isOpen?: boolean; onOpenChange?: (isOpen: boolean) => void; initialIndex?: number; } ``` ## 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. #### Controlled Mode Use controlled props to programmatically control the image viewer state and track current image index ```jsx function Example() { const [isOpen, setIsOpen] = React.useState(false); const [currentIndex, setCurrentIndex] = React.useState(0); const images = [ { url: 'https://images.unsplash.com/photo-1682687220742-aba13b6e50ba?w=800&auto=format&fit=crop&q=60', alt: 'Mountain landscape' }, { url: 'https://images.unsplash.com/photo-1682687221038-404670e01d4c?w=800&auto=format&fit=crop&q=60', alt: 'Ocean waves' }, { url: 'https://images.unsplash.com/photo-1682687982501-1e58ab814714?w=800&auto=format&fit=crop&q=60', alt: 'Desert sunset' } ]; return ( Controlled Mode Current index: {currentIndex + 1} / {images.length} | Viewer is {isOpen ? 'open' : 'closed'} {images.map((image, index) => ( { setCurrentIndex(index); setIsOpen(true); }} > {image.alt} ))} ); }` ``` #### Single Image Image viewer works great with single images too, providing zoom and pan capabilities ```jsx function Example() { const images = [ { url: 'https://images.unsplash.com/photo-1682687220742-aba13b6e50ba?w=800&auto=format&fit=crop&q=60', alt: 'Mountain landscape' } ]; const thumbnailSource = { uri: images[0].url }; return ( Single Image Tap the image to view it in full screen with zoom support Gallery thumbnail ); }` ``` --- ## Image URL: /ui/docs/components/image/index # Image Enhance your app with the Image component from gluestack-ui. Build seamless UI component images in React & React Native with ease. Explore the docs now! This is an illustration of **Image** component. ```jsx function Example() { return ( image ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add image ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```tsx const imageStyle = tva({ base: 'max-w-full', variants: { size: { '2xs': 'h-6 w-6', 'xs': 'h-10 w-10', 'sm': 'h-16 w-16', 'md': 'h-20 w-20', 'lg': 'h-24 w-24', 'xl': 'h-32 w-32', '2xl': 'h-64 w-64', 'full': 'h-full w-full', 'none': '', }, }, }); const UIImage = createImage({ Root: RNImage }); type ImageProps = VariantProps & React.ComponentProps; const Image = React.forwardRef< React.ComponentRef, ImageProps & { className?: string } >(function Image({ size = 'md', className, ...props }, ref) { return ( ); }); Image.displayName = 'Image'; export { Image }; ``` ### 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. #### Image It inherits all the properties of React Native's [Image](https://reactnative.dev/docs/image) component. <> | Platform | Output | | --- | --- | | `Web` | {``} | | `Native` | {``} | ### Props Image component is created using Image component from react-native. It extends all the props supported by [React Native Image](https://reactnative.dev/docs/image#props). <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `source` | ImageSourcePropType | - | The source of the image | | `alt` | string | - | The alt text for the image | | `size` | 2xs | xs | sm | md | lg | xl | 2xl | full | md | The size of the image | ### Examples The Examples section offers visual previews of the component, letting you quickly identify the best fit for your needs. Just copy the code and use it in your project. #### Basic Unitools Image The below example will run for both Expo and Next.js projects. For installation steps, refer to the [Installation](https://unitools.geekyants.com/packages/image/) section of `@unitools/image`. ```ts ``` {/* */} ```jsx function Example() { return ( Logo ); }` ``` --- ## Input URL: /ui/docs/components/input/index # Input A feature-rich React Native Input component – supports icons, validation, and styling options for seamless user input in your mobile app. This is an illustration of **Input** component. ```jsx function Example() { return ( ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add input ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```tsx 'use client'; const SCOPE = 'INPUT'; const StyledUIIcon = styled(UIIcon, { className: "style" }); const UIInput = createInput({ Root: withStyleContext(View, SCOPE), Icon: StyledUIIcon, Slot: Pressable, Input: TextInput, }); const inputStyle = tva({ base: 'min-h-9 w-full flex-row items-center rounded-md border border-border dark:bg-input/30 bg-transparent shadow-xs transition-[color,box-shadow] overflow-hidden data-[focus=true]:outline-none data-[focus=true]:border-ring dark:data-[focus=true]:border-ring data-[focus=true]:web:ring-[3px] data-[focus=true]:web:ring-ring/50 data-[invalid=true]:border-destructive/40 dark:data-[invalid=true]:border-destructive/40 data-[invalid=true]:web:ring-destructive/20 dark:data-[invalid=true]:web:ring-destructive/40 data-[disabled=true]:pointer-events-none data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50 px-3 gap-2', }); const inputIconStyle = tva({ base: 'justify-center items-center text-muted-foreground fill-none h-4 w-4', }); const inputSlotStyle = tva({ base: 'justify-center items-center web:disabled:cursor-not-allowed', }); const inputFieldStyle = tva({ base: 'flex-1 text-foreground text-sm md:text-sm py-1 h-full placeholder:text-muted-foreground web:outline-none ios:leading-[0px] web:cursor-text web:data-[disabled=true]:cursor-not-allowed', }); type IInputProps = React.ComponentProps & VariantProps & { className?: string }; const Input = React.forwardRef, IInputProps>( function Input({ className, ...props }, ref) { return ( ); } ); type IInputIconProps = React.ComponentProps & VariantProps & { className?: string; height?: number; width?: number; }; const InputIcon = React.forwardRef< React.ComponentRef, IInputIconProps >(function InputIcon({ className, ...props }, ref) { return ( ); }); type IInputSlotProps = React.ComponentProps & VariantProps & { className?: string }; const InputSlot = React.forwardRef< React.ComponentRef, IInputSlotProps >(function InputSlot({ className, ...props }, ref) { return ( ); }); type IInputFieldProps = React.ComponentProps & VariantProps & { className?: string }; const InputField = React.forwardRef< React.ComponentRef, IInputFieldProps >(function InputField({ className, ...props }, ref) { return ( ); }); Input.displayName = 'Input'; InputIcon.displayName = 'InputIcon'; InputSlot.displayName = 'InputSlot'; InputField.displayName = 'InputField'; export { Input, InputField, InputIcon, InputSlot }; ``` ### 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 () => ( {/* Some Icon Component */} ); ``` ### Component Props This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration. #### Input It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. <> | Platform | Output | | --- | --- | | `Web` | {``} | | `Native` | {``} | ### Props <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `isInvalid` | boolean | false | When true, the input displays an error state | | `isDisabled` | boolean | false | When true, the input is disabled and cannot be edited | | `isHovered` | boolean | false | When true, the input displays a hover state | | `isFocused` | boolean | false | When true, the input displays a focus state | | `isRequired` | boolean | false | If true, sets aria-required="true" on the input | | `isReadOnly` | boolean | false | If true, the input value cannot be edited | | `size` | xl | lg | md | sm | md | The size of the input | | `variant` | underlined | outline | rounded | outline | The variant of the input | ### 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 Input 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/TR/wai-aria-1.2/#textbox). #### Keyboard - Setting the `aria-label` and `aria-hint` to help users understand the purpose and function of the Input #### Screen Reader - Compatible with screen readers such as VoiceOver and Talk-back. - The `accessible` and `aria-label` props to provide descriptive information about the Input - Setting `aria-traits` and `aria-hint` to provide contextual information about the various states of the Input, such as "double tap to edit". ### 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. #### Input with FormControl The Input component integrates with an icon and a button, providing users with a comprehensive login window inside a FormControl component. ```jsx function Example() { const [showPassword, setShowPassword] = React.useState(false); const handleState = () => { setShowPassword((showState) => { return !showState; }); }; return ( Login Email Password ); }` ``` #### Input with Icons The Input with Icons is a variation of the Input component that displays icons next to input field. It's commonly used in apps for a more visual representation of options and easier navigation. ```jsx function Example() { return ( ); }` ``` --- ## Link URL: /ui/docs/components/link/index # Link Enhance navigation with a React Native link component. Seamless UI link design for intuitive user experiences. Learn more! This is an illustration of **Link** component. ```jsx function Example() { return ( gluestack ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add link ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```tsx 'use client'; export const UILink = createLink({ Root: withStyleContext(Pressable), Text: Text, }); const linkStyle = tva({ base: 'group/link web:outline-0 data-[disabled=true]:web:cursor-not-allowed data-[focus-visible=true]:web:ring-2 data-[focus-visible=true]:web:ring-indicator-primary data-[focus-visible=true]:web:outline-0 data-[disabled=true]:opacity-4 ', }); const linkTextStyle = tva({ base: 'underline text-primary data-[hover=true]:text-primary/80 data-[hover=true]:no-underline data-[active=true]:text-destructive/80 font-normal font-body web:font-sans web:tracking-sm web:my-0 web:bg-transparent web:border-0 web:box-border web:inline web:list-none web:m-0 web:p-0 web:relative web:text-start web:whitespace-pre-wrap web:break-words', variants: { isTruncated: { true: 'web:truncate', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, size: { '2xs': 'text-2xs', 'xs': 'text-xs', 'sm': 'text-sm', 'md': 'text-base', 'lg': 'text-lg', 'xl': 'text-xl', '2xl': 'text-2xl', '3xl': 'text-3xl', '4xl': 'text-4xl', '5xl': 'text-5xl', '6xl': 'text-6xl', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, }, }); type ILinkProps = React.ComponentProps & VariantProps & { className?: string }; const Link = React.forwardRef, ILinkProps>( function Link({ className, ...props }, ref) { return ( ); } ); type ILinkTextProps = React.ComponentProps & VariantProps & { className?: string }; const LinkText = React.forwardRef< React.ComponentRef, ILinkTextProps >(function LinkText({ className, size = 'md', ...props }, ref) { return ( ); }); Link.displayName = 'Link'; LinkText.displayName = 'LinkText'; export { Link, LinkText }; ``` ### 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. #### Link Contains all link related layout style props and actions. It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) component. <> | Platform | Output | | --- | --- | | `Web` | {``} | | `Native` | {``} | ### Props <> | Prop | Type | Default | Description | | --- | --- | --- | --- | | `href` | string | - | URL that should be opened on Link press | | `onPress` | (event?: GestureResponderEvent) => any | - | Callback that will be invoked on Link press | | `isExternal` | boolean | false | If true, link will be opened in new tab on web | | `isHovered` | boolean | false | When true, the link displays a hover state | | `isFocusVisible` | boolean | false | To manually set focus visible state to the link | | `size` | xs | sm | md | lg | xl | 2xl | 3xl | 4xl | 5xl | 6xl | md | The size of the link | ### Features - Keyboard support for actions - Support for hover and focus states - Option to add your styles or use the default styles ### Accessibility We have outlined the various features that ensure the Link 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/link/). #### Keyboard - `Tab`: Moves focus to the next focusable element - `Enter`: Users should be able to open a link #### Screen Reader - VoiceOver: When a link receives focus, screen readers should announce a descriptive link name ### 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. #### Default Link ```jsx function Example() { return ( Design inspiration from  pinterest.com ); }` ``` #### Link with Icon ```jsx function Example() { return ( Go to  Pinterest ); }` ``` --- ## LiquidGlass URL: /ui/docs/components/liquid-glass/index # LiquidGlass A glass effect component for React Native built on top of [expo-glass-effect](https://docs.expo.dev/versions/latest/sdk/glass-effect/) with gluestack-ui styling and NativeWind className support. > **Note:** LiquidGlass is **not supported in Next.js**. It is intended for React Native and Expo environments only. `expo-glass-effect` requires native iOS/Android APIs that are not available in browser environments. On iOS, liquid glass requires **iOS 26+** (`isLiquidGlassAvailable`). On Android and older iOS, a standard glass blur fallback is used (`isGlassEffectAPIAvailable`). This is an illustration of the **LiquidGlass** component. A simple GlassView displaying content with the default glass effect style. ```jsx function Example() { return ( {/* Colorful background so the glass effect is visible */} {/* Glass card */} Glass Effect Built with expo-glass-effect ) }` ``` #### With GlassContainer Use GlassContainer to group multiple GlassView elements so they visually merge together when close to each other. ```jsx function Example() { return ( {/* Colorful background so the glass effect is visible */} {/* GlassContainer merges adjacent GlassViews */} Notifications 3 new alerts Messages 2 unread ) }` ``` #### With Color Scheme Override the glass effect's appearance independently from the system theme using the colorScheme prop. ```jsx function Example() { return ( {/* Colorful background so the glass effect is visible */} Light Always light Dark Always dark Auto System ) }` ``` #### With Glass Style Compare the 'regular' and 'clear' glass effect styles side by side. ```jsx function Example() { return ( {/* Colorful background so the glass effect is visible */} Regular Frosted Clear Transparent ) }` ``` ## Installation ### Run the following command: ```bash npx gluestack-ui add liquid-glass ``` ## API Reference To use this component in your project, include the following import statement in your file. ```jsx ``` ```jsx export default () => ( Glass Effect ); ``` ### Component Props This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration. #### GlassView A view that applies a native glass blur effect. Extends React Native's [View](https://reactnative.dev/docs/view) with glass-specific props. ### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `glassEffectStyle` | 'regular' | 'clear' | 'regular' | {`The visual style of the glass effect. 'regular' gives a frosted look; 'clear' is more transparent.`} | | `tintColor` | string | - | {`A color tint applied on top of the glass blur effect.`} | | `isInteractive` | boolean | false | {`When true, the glass effect responds to user interaction (e.g. press feedback on iOS 26+ liquid glass).`} | | `colorScheme` | 'auto' | 'light' | 'dark' | 'auto' | {`Overrides the system appearance for the glass effect. Use when your app has its own theme toggle independent of the system setting.`} | | `className` | string | - | {`NativeWind className for layout, sizing, border radius, and other style utilities.`} | Also inherits all props of React Native's [View](https://reactnative.dev/docs/view). #### GlassContainer A container that groups multiple `GlassView` elements together. When glass views are close enough (controlled by `spacing`), they visually merge into one connected glass surface on iOS 26+ with liquid glass enabled. ### Props | Prop | Type | Default | Description | | --- | --- | --- | --- | | `spacing` | number | - | {`The distance threshold at which glass elements start merging together. Controls when adjacent GlassView elements blend into one surface.`} | | `className` | string | - | {`NativeWind className for layout, sizing, padding, and other style utilities.`} | Also inherits all props of React Native's [View](https://reactnative.dev/docs/view). ### Utility Functions #### isLiquidGlassAvailable ```ts ``` Returns `true` on iOS 26+ devices that support the native liquid glass effect. Use this to conditionally render enhanced UI when the full liquid glass API is available. #### isGlassEffectAPIAvailable ```ts ``` Returns `true` when any glass effect is supported (iOS and Android with blur fallback). Use this to gate glass-based UI behind a capability check. --- ## Menu URL: /ui/docs/components/menu/index # Menu Build a user-friendly interface with gluestack-ui menu component in React & React Native, designed for easy navigation and accessibility. This is an illustration of **Menu** component. ```jsx function Example() { return ( { return ( ) }} > Add account Community Plugins Settings ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add menu ``` **Manual:** > Note: At present, we have integrated `react-native-reanimated` for animation. You have the option to remove this and implement your own custom animation wrapper. ### 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 AnimatedView = Animated.createAnimatedComponent(ScrollView); const menuStyle = tva({ base: 'rounded-md bg-popover text-popover-foreground border border-border p-1 shadow-hard-5 max-h-[300px] overflow-y-auto', }); const menuItemStyle = tva({ base: 'min-w-[200px] p-3 flex-row items-center rounded data-[hover=true]:bg-accent data-[hover=true]:text-accent-foreground data-[active=true]:bg-accent data-[active=true]:text-accent-foreground data-[focus=true]:bg-accent data-[focus=true]:text-accent-foreground data-[focus=true]:web:outline-none data-[focus=true]:web:outline-0 data-[disabled=true]:opacity-40 data-[disabled=true]:web:cursor-not-allowed data-[focus-visible=true]:web:outline-2 data-[focus-visible=true]:web:outline-ring data-[focus-visible=true]:web:outline data-[focus-visible=true]:web:cursor-pointer data-[disabled=true]:data-[focus=true]:bg-transparent', }); const menuBackdropStyle = tva({ base: 'absolute top-0 bottom-0 left-0 right-0 web:cursor-default', }); const menuSeparatorStyle = tva({ base: 'bg-border h-px w-full', }); const menuItemLabelStyle = tva({ base: 'text-popover-foreground font-normal font-body', variants: { isTruncated: { true: 'web:truncate', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, }, }); const BackdropPressable = React.forwardRef< React.ComponentRef, React.ComponentPropsWithoutRef & VariantProps >(function BackdropPressable({ className, ...props }, ref) { return ( ); }); type IMenuItemProps = VariantProps & { className?: string; } & React.ComponentPropsWithoutRef; const Item = React.forwardRef< React.ComponentRef, IMenuItemProps >(function Item({ className, ...props }, ref) { return ( ); }); const Separator = React.forwardRef< React.ComponentRef, React.ComponentPropsWithoutRef & VariantProps >(function Separator({ className, ...props }, ref) { return ( ); }); const StyledAnimatedView = styled(AnimatedView, { className: 'style', }); export const UIMenu = createMenu({ Root: StyledAnimatedView, Item: Item, Label: Text, Backdrop: BackdropPressable, Separator: Separator, }); type IMenuProps = React.ComponentProps & VariantProps & { className?: string }; type IMenuItemLabelProps = React.ComponentProps & VariantProps & { className?: string }; const Menu = React.forwardRef, IMenuProps>( function Menu({ className, ...props }, ref) { return ( ); } ); const MenuItem = UIMenu.Item; const MenuItemLabel = React.forwardRef< React.ComponentRef, IMenuItemLabelProps >(function MenuItemLabel( { className, isTruncated, bold, underline, strikeThrough, sub, italic, highlight, ...props }, ref ) { return ( ); }); const MenuSeparator = UIMenu.Separator; Menu.displayName = 'Menu'; MenuItem.displayName = 'MenuItem'; MenuItemLabel.displayName = 'MenuItemLabel'; MenuSeparator.displayName = 'MenuSeparator'; export { Menu, MenuItem, MenuItemLabel, MenuSeparator }; ``` ### 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 () => ( ); ``` > Note: The immediate parent of **MenuItem** must be **Menu**. There should be no higher-order component (HOC) between them. ### Component Props This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration. #### Menu Contains all menu 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 | | --- | --- | --- | --- | | `trigger` | {'(_props: any, state: { open: boolean; }) => Element'} | - | Function that returns a React Element. This element will be used as a Trigger for the Menu | | `placement` | "bottom" | "top" | "right" | "left" | "top left" | "top right" | "bottom left" | "bottom right" | "right top" | "right bottom" | "left top" | "left bottom" | bottom left | Menu placement | | `defaultIsOpen` | boolean | false | If true, the menu will be opened by default | | `onOpen` | {'() => void'} | - | This function will be invoked when the menu is opened | | `onClose` | {'() => void'} | - | This function will be invoked when menu is closed | | `isOpen` | boolean | false | Whether the menu is opened. Useful for controlling the open state | | `offset` | number | - | The additional offset applied along the main axis | | `crossOffset` | number | - | The additional offset applied along the cross axis | | `disabledKeys` | string[] | - | Item keys in this collection will be disabled | | `closeOnSelect` | boolean | true | Whether menu is closed after option is selected | | `selectedKeys` | {"'all' | Iterable"} | - | The currently selected keys in the collection (controlled) | | `selectionMode` | {"'none' | 'single' | 'multiple'"} | none | The type of selection that is allowed in the collection | | `onSelectionChange` | {"(keys: 'all' | Iterable) => void"} | - | Handler that is called when the selection changes | #### MenuItemLabel 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. #### MenuSeparator Contains all view 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 ### Accessibility We have outlined the various features that ensure the Menu 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 [MENU WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menubar/). #### Keyboard - `Space`: Opens/closes the menu - `Enter`: Opens/closes the menu - `Arrow Down`: Moves focus to the next focusable element - `Arrow Up`: Moves focus to the previous focusable element - `Esc`: Closes the menu and moves focus to menuTrigger #### Screen Reader - VoiceOver: When the menu is opened, the screen reader will say, Menu item group with 4 items. You are currently on a menu item group. To select this menu item, press Control + Option + Space. To close the menu, press Escape. #### States - `aria-labelledby` identifies the element that labels the current element. - `aria-label` defines a string value that labels the current element. ### 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. #### Menu with Tag ```jsx function Example() { return ( { return ( ); }} > Membership Pro Orders Address Book Earn & Redeem Coupons Help Center Logout ); }` ``` #### Menu with Selection ```jsx function Example() { const [selected, setSelected] = React.useState(new Set([])); return ( { setSelected(keys); }} closeOnSelect={true} trigger={({ ...triggerProps }) => { return ( ); }} > Account Settings Help Centre Contact Support Download Mobile App Install Chrome Extension ); }` ``` --- ## Modal URL: /ui/docs/components/modal/index # Modal Create smooth and accessible modals in React & React Native. Implement React modal components for alerts, forms, and notifications with ease. Optimize modal component for better user engagement. This is an illustration of **Modal** component. ```jsx function Example() { const [showModal, setShowModal] = React.useState(false); return ( <> { setShowModal(false); }} size="{{size}}" > Modal Title This is the modal body. You can add any content here. ); }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add modal ``` **Manual:** > Note: At present, we have integrated `react-native-reanimated` for animation. You have the option to remove this and implement your own custom animation wrapper. ### 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 AnimatedPressable = Animated.createAnimatedComponent(Pressable); const AnimatedView = Animated.createAnimatedComponent(View); const SCOPE = 'MODAL'; const StyledAnimatedPressable = styled(AnimatedPressable, { className: 'style' }); const StyledAnimatedView = styled(AnimatedView, { className: 'style' }); const UIModal = createModal({ Root: withStyleContext(View as any, SCOPE), Backdrop: StyledAnimatedPressable, Content: StyledAnimatedView, Body: ScrollView, CloseButton: Pressable, Footer: View, Header: View, }); const modalStyle = tva({ base: 'group/modal w-full h-full justify-center items-center web:pointer-events-none', variants: { size: { xs: '', sm: '', md: '', lg: '', full: '', }, }, }); const modalBackdropStyle = tva({ base: 'absolute left-0 top-0 right-0 bottom-0 bg-[#000]/50 web:cursor-default', }); const modalContentStyle = tva({ base: 'bg-background rounded-md overflow-hidden border border-border/80 shadow-hard-2 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 modalBodyStyle = tva({ base: 'mt-2 mb-6', }); const modalCloseButtonStyle = tva({ base: 'group/modal-close-button z-10 rounded data-[focus-visible=true]:web:bg-background/90 web:outline-0 cursor-pointer', }); const modalHeaderStyle = tva({ base: 'justify-between items-center flex-row', }); const modalFooterStyle = tva({ base: 'flex-row justify-end items-center gap-2', }); type IModalProps = React.ComponentProps & VariantProps & { className?: string }; type IModalBackdropProps = React.ComponentProps & VariantProps & { className?: string }; type IModalContentProps = React.ComponentProps & VariantProps & { className?: string }; type IModalHeaderProps = React.ComponentProps & VariantProps & { className?: string }; type IModalBodyProps = React.ComponentProps & VariantProps & { className?: string }; type IModalFooterProps = React.ComponentProps & VariantProps & { className?: string }; type IModalCloseButtonProps = React.ComponentProps & VariantProps & { className?: string }; const Modal = React.forwardRef, IModalProps>( ({ className, size = 'md', ...props }, ref) => ( ) ); const ModalBackdrop = React.forwardRef< React.ComponentRef, IModalBackdropProps >(function ModalBackdrop({ className, ...props }, ref) { return ( ); }); const ModalContent = React.forwardRef< React.ComponentRef, IModalContentProps >(function ModalContent({ className, size, ...props }, ref) { const { size: parentSize } = useStyleContext(SCOPE); return ( ); }); const ModalHeader = React.forwardRef< React.ComponentRef, IModalHeaderProps >(function ModalHeader({ className, ...props }, ref) { return ( ); }); const ModalBody = React.forwardRef< React.ComponentRef, IModalBodyProps >(function ModalBody({ className, ...props }, ref) { return ( ); }); const ModalFooter = React.forwardRef< React.ComponentRef, IModalFooterProps >(function ModalFooter({ className, ...props }, ref) { return ( ); }); const ModalCloseButton = React.forwardRef< React.ComponentRef, IModalCloseButtonProps >(function ModalCloseButton({ className, ...props }, ref) { return ( ); }); Modal.displayName = 'Modal'; ModalBackdrop.displayName = 'ModalBackdrop'; ModalContent.displayName = 'ModalContent'; ModalHeader.displayName = 'ModalHeader'; ModalBody.displayName = 'ModalBody'; ModalFooter.displayName = 'ModalFooter'; ModalCloseButton.displayName = 'ModalCloseButton'; export { Modal, ModalBackdrop, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader }; ``` ### 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. #### Modal Contains all View related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view#props) component. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `isOpen` | boolean | - | If true, the modal will open. Useful for controllable state behavior | | `onClose` | {'() => any'} | - | Callback invoked when the modal is closed | | `useRNModal` | boolean | false | If true, renders react-native native modal | | `defaultIsOpen` | boolean | - | Specifies the default open state of the Modal | | `initialFocusRef` | {'React.RefObject'} | - | The ref of element to receive focus when the modal opens | | `finalFocusRef` | {'React.RefObject'} | - | The ref of element to receive focus when the modal closes | | `avoidKeyboard` | boolean | - | If true, the Modal will avoid the keyboard | | `closeOnOverlayClick` | boolean | - | If true, the Modal will close when the overlay is clicked | | `isKeyboardDismissable` | boolean | - | If true, the keyboard can dismiss the Modal | | `children` | any | - | The content to display inside the Modal | #### ModalBackdrop 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. #### ModalContent 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. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `focusable` | boolean | false | If true, Modal Content will be focusable | #### ModalHeader It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### ModalCloseButton It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### ModalBody It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### ModalFooter It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. ### Accessibility We have outlined the various features that ensure the Modal 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/docs/) which follows the [Dialog Modal WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/). ### Props Modal 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. #### Modal <> | 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. #### Multiple Modals A user interface featuring multiple modal components that allow for layered interaction and context-specific information display. ```jsx function Example() { const [showModal, setShowModal] = React.useState(false); const [showModal2, setShowModal2] = React.useState(false); const [showModal3, setShowModal3] = React.useState(false); return ( <> { setShowModal(false); }} > Forgot password? No worries, we'll send you reset instructions { setShowModal2(false); }} > Reset password A verification code has been sent to you. Enter code below. Didn't receive the email? Click to resend { setShowModal3(false); }} > Set new password Almost done. Enter your new password and you are all set. ); }` ``` #### Onboarding Message ```jsx function Example() { const [showModal, setShowModal] = React.useState(false); return ( <> { setShowModal(false); }} > image Welcome to the dashboard We are glad to have you on board, Here are some quick tips to let you up and running. ); }` ``` #### Delete Post ```jsx function Example() { const [showModal, setShowModal] = React.useState(false); return ( <> { setShowModal(false); }} > Delete blog post Are you sure you want to delete this post? This action cannot be undone. ); }` ``` #### Invite Friends to Project ```jsx function Example() { const [showModal, setShowModal] = React.useState(false); return ( <> { setShowModal(false); }} > Invite your team You have created a new project! Invite colleagues to collaborate on this project. ); }` ``` --- ## Popover URL: /ui/docs/components/popover/index # Popover Improve user experience with a React Popover component—perfect for contextual modals, tooltips & interactive UI elements. Works seamlessly in React & React Native! This is an illustration of **Popover** component. ```jsx function App() { const [isOpen, setIsOpen] = React.useState(false) const handleOpen = () => { setIsOpen(true) } const handleClose = () => { setIsOpen(false) } return ( { return ( ) }} > Alex, Annie and many others are already enjoying the Pro features, don't miss out on the fun! ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add popover ``` **Manual:** > Note: At present, we have integrated `react-native-reanimated` for animation. You have the option to remove this and implement your own custom animation wrapper. ### 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 AnimatedPressable = Animated.createAnimatedComponent(Pressable); const AnimatedView = Animated.createAnimatedComponent(View); const SCOPE = 'POPOVER'; const StyledAnimatedView = styled(AnimatedView, { className: 'style' }); const StyledAnimatedPressable = styled(AnimatedPressable, { className: 'style' }); const UIPopover = createPopover({ Root: withStyleContext(StyledAnimatedView, SCOPE), Arrow: View, Backdrop: StyledAnimatedPressable, Body: ScrollView, CloseButton: Pressable, Content: View, Footer: View, Header: View }); const popoverStyle = tva({ base: 'group/popover w-full h-full justify-center items-center web:pointer-events-none', }); const popoverArrowStyle = tva({ base: 'bg-popover z-[1] border absolute overflow-hidden h-3.5 w-3.5 border-border dark:border-border/90', variants: { placement: { 'top left': 'data-[flip=false]:border-t-0 data-[flip=false]:border-l-0 data-[flip=true]:border-b-0 data-[flip=true]:border-r-0', 'top': 'data-[flip=false]:border-t-0 data-[flip=false]:border-l-0 data-[flip=true]:border-b-0 data-[flip=true]:border-r-0', 'top right': 'data-[flip=false]:border-t-0 data-[flip=false]:border-l-0 data-[flip=true]:border-b-0 data-[flip=true]:border-r-0', 'bottom': 'data-[flip=false]:border-b-0 data-[flip=false]:border-r-0 data-[flip=true]:border-t-0 data-[flip=true]:border-l-0', 'bottom left': 'data-[flip=false]:border-b-0 data-[flip=false]:border-r-0 data-[flip=true]:border-t-0 data-[flip=true]:border-l-0', 'bottom right': 'data-[flip=false]:border-b-0 data-[flip=false]:border-r-0 data-[flip=true]:border-t-0 data-[flip=true]:border-l-0', 'left': 'data-[flip=false]:border-l-0 data-[flip=false]:border-b-0 data-[flip=true]:border-r-0 data-[flip=true]:border-t-0', 'left top': 'data-[flip=false]:border-l-0 data-[flip=false]:border-b-0 data-[flip=true]:border-r-0 data-[flip=true]:border-t-0', 'left bottom': 'data-[flip=false]:border-l-0 data-[flip=false]:border-b-0 data-[flip=true]:border-r-0 data-[flip=true]:border-t-0', 'right': 'data-[flip=false]:border-r-0 data-[flip=false]:border-t-0 data-[flip=true]:border-l-0 data-[flip=true]:border-b-0', 'right top': 'data-[flip=false]:border-r-0 data-[flip=false]:border-t-0 data-[flip=true]:border-l-0 data-[flip=true]:border-b-0', 'right bottom': 'data-[flip=false]:border-r-0 data-[flip=false]:border-t-0 data-[flip=true]:border-l-0 data-[flip=true]:border-b-0', }, }, }); const popoverBackdropStyle = tva({ base: 'absolute left-0 top-0 right-0 bottom-0 web:cursor-default', }); const popoverCloseButtonStyle = tva({ base: 'group/popover-close-button z-[1] rounded-sm p-2 data-[focus-visible=true]:bg-accent web:outline-0 web:cursor-pointer data-[hover=true]:bg-accent/50', }); const popoverContentStyle = tva({ base: 'bg-popover text-popover-foreground rounded-lg overflow-hidden border border-border dark:border-border/10 shadow-md p-4 w-full max-w-xs web:pointer-events-auto', }); const popoverHeaderStyle = tva({ base: 'flex-row justify-between items-center', }); const popoverBodyStyle = tva({ base: '', }); const popoverFooterStyle = tva({ base: 'flex-row justify-between items-center', }); type IPopoverProps = React.ComponentProps & VariantProps & { className?: string }; type IPopoverArrowProps = React.ComponentProps & VariantProps & { className?: string }; type IPopoverContentProps = React.ComponentProps & VariantProps & { className?: string }; type IPopoverHeaderProps = React.ComponentProps & VariantProps & { className?: string }; type IPopoverFooterProps = React.ComponentProps & VariantProps & { className?: string }; type IPopoverBodyProps = React.ComponentProps & VariantProps & { className?: string }; type IPopoverBackdropProps = React.ComponentProps & VariantProps & { className?: string }; type IPopoverCloseButtonProps = React.ComponentProps< typeof UIPopover.CloseButton > & VariantProps & { className?: string }; const Popover = React.forwardRef< React.ComponentRef, IPopoverProps >(function Popover({ className, placement = 'bottom', ...props }, ref) { return ( ); }); const PopoverContent = React.forwardRef< React.ComponentRef, IPopoverContentProps >(function PopoverContent({ className, ...props }, ref) { return ( ); }); const PopoverArrow = React.forwardRef< React.ComponentRef, IPopoverArrowProps >(function PopoverArrow({ className, ...props }, ref) { const { placement } = useStyleContext(SCOPE); return ( ); }); const PopoverBackdrop = React.forwardRef< React.ComponentRef, IPopoverBackdropProps >(function PopoverBackdrop({ className, ...props }, ref) { return ( ); }); const PopoverBody = React.forwardRef< React.ComponentRef, IPopoverBodyProps >(function PopoverBody({ className, ...props }, ref) { return ( ); }); const PopoverCloseButton = React.forwardRef< React.ComponentRef, IPopoverCloseButtonProps >(function PopoverCloseButton({ className, ...props }, ref) { return ( ); }); const PopoverFooter = React.forwardRef< React.ComponentRef, IPopoverFooterProps >(function PopoverFooter({ className, ...props }, ref) { return ( ); }); const PopoverHeader = React.forwardRef< React.ComponentRef, IPopoverHeaderProps >(function PopoverHeader({ className, ...props }, ref) { return ( ); }); Popover.displayName = 'Popover'; PopoverArrow.displayName = 'PopoverArrow'; PopoverBackdrop.displayName = 'PopoverBackdrop'; PopoverContent.displayName = 'PopoverContent'; PopoverHeader.displayName = 'PopoverHeader'; PopoverFooter.displayName = 'PopoverFooter'; PopoverBody.displayName = 'PopoverBody'; PopoverCloseButton.displayName = 'PopoverCloseButton'; export { Popover, PopoverArrow, PopoverBackdrop, PopoverBody, PopoverCloseButton, PopoverContent, PopoverFooter, PopoverHeader }; ``` ### 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. #### Popover It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `defaultIsOpen` | boolean | - | Specifies the default open state of the popover. | | `isOpen` | boolean | - | If true, the popover will open. Useful for controllable state behavior. | | `trapFocus` | boolean | true | Whether popover should trap focus. | | `focusScope` | boolean | true | Whether focus should be outside of popover or not | | `shouldFlip` | boolean | true | Whether the element should flip its orientation when there is insufficient room. | | `initialFocusRef` | {`React.RefObject`} | - | The ref of element to receive focus when the popover opens. | | `finalFocusRef` | {`React.RefObject`} | - | The ref of element to receive focus when the popover closes | | `trigger` | {`() => any`} | - | Function that returns a React Element. This element will be used as a Trigger for the popover. | | `crossOffset` | number | - | The additional offset applied along the cross axis between the element and its trigger element. | | `offset` | number | - | The additional offset applied along the main axis between the element and its trigger element. | | `shouldOverlapWithTrigger` | boolean | false | Determines whether popover content should overlap with the trigger. | | `isKeyboardDismissable` | boolean | - | If true, the keyboard can dismiss the popover. | | `placement` | 'top' | 'top left' | 'top right' | 'bottom' | 'bottom left' | 'bottom right' | 'right' | 'right top' | 'right bottom' | 'left' | 'left top' | 'left bottom' | 'bottom' | Popover placement | | `useRNModal` | boolean | false | If true, renders react-native native modal. | | `avoidKeyboard` | boolean | - | If true, the popover will avoid the keyboard. | | `onOpen` | () => any | - | This function will be invoked when popover is opened. | | `onClose` | () => any | - | This function will be invoked when popover is closed. | | `children` | any | - | The content to display inside the popover. | #### PopoverBackdrop It is React Native's [Pressable](https://reactnative.dev/docs/pressable) 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. #### PopoverContent Contains all backdrop related layout style props and actions. 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. #### PopoverArrow Pointer indicator for the popover. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. Root `Popover` `offset` and `crossOffset` position the **popover content** relative to the trigger. `PopoverArrow` accepts its own optional `offset` and `crossOffset` to adjust **only the arrow** (same axis meaning as on `Popover`: cross axis shifts along the trigger edge for top/bottom placements via `left`, and along the vertical edge for left/right placements via `top`; main-axis `offset` nudges the arrow along the perpendicular edge from `getArrowStyles`). <> | Name | Type | Default | Description | | --- | --- | --- | --- | | `crossOffset` | number | - | Extra cross-axis offset for the arrow only (does not move popover content). | | `offset` | number | - | Extra main-axis offset for the arrow only (does not move popover content). | #### PopoverHeader It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### PopoverFooter It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### PopoverBody It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### PopoverCloseButton It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) component. ### Features - Keyboard support for actions. ### Accessibility We have outlined the various features that ensure the Popover component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards. It adheres to the [Dialog WAI-ARIA design pattern.](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/). #### Keyboard - `Space`: Opens/closes the popover. - `Enter`: Opens/closes the popover. - `Tab`: Moves focus to the next focusable element. - `Shift + Tab`: Moves focus to the previous focusable element. - `Esc`: Closes the popover and moves focus to PopoverTrigger. #### Screen Reader - VoiceOver: When we click on button, the screen reader will announce Popover, menu expanded, button and when we close it then it says Popover, menu collapsed, button. Popover component i ### Props Popover 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. #### Popover <> | 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. #### Popover used along with multiple Avatars ```jsx function App() { const [isOpen, setIsOpen] = React.useState(false); const handleOpen = () => { setIsOpen(true); }; const handleClose = () => { setIsOpen(false); }; return ( { return ( ); }} > John Doe John Doe John Doe John Doe Alex, Annie and many others are already enjoying the Pro features, don't miss out on the fun! ) }` ``` #### Popover with Inline Text Trigger ```jsx function Example() { const [isOpen, setIsOpen] = React.useState(false); return ( setIsOpen(false)} onOpen={() => setIsOpen(true)} offset={10} trigger={(triggerProps) => { return ( Read the{' '} highlighted release notes {' '} to open a popover from inline text. ); }} > Inline text triggers position from the last touch or click point. ); }` ``` #### Popover with CTA ```jsx function App() { const [isOpen, setIsOpen] = React.useState(false); const [values, setValues] = React.useState(['work']); const handleOpen = () => { setIsOpen(true); }; const handleClose = () => { setIsOpen(false); }; return ( { return ( ); }} > Are you interested in using Pro for work or personal use? We can recommend plans that are right for you. With our personalized approach, you can trust that the plans we recommend will align perfectly with your goals. { setValues(keys); }} className="pl-1" > For personal projects For work ) }` ``` #### Popover with Image ```jsx function App() { const [isOpen, setIsOpen] = React.useState(false) const handleOpen = () => { setIsOpen(true) } const handleClose = () => { setIsOpen(false) } return ( { return ( ) }} > image Sign up to the newsletter and get 25% Off ) }` ``` #### Popover with InputField ```jsx function Example() { const [showPopover, setShowPopover] = React.useState(false); return ( setShowPopover(false)} onOpen={() => setShowPopover(true)} offset={8} trigger={(triggerProps) => { return ( ); }} > People with access JC Jane Cooper janecooper09@gmail.com CM Catherine Miller catherinemiller88@gmail.com ); }` ``` --- ## Portal URL: /ui/docs/components/portal/index # Portal Learn how to use the Portal component in React and React Native to render content outside the DOM hierarchy. Explore installation, API reference, and props. This is an illustration of **Portal** component. ```jsx function App() { const [visible, setVisible] = React.useState(false) const handleClose = () => setVisible(false) return ( <> Portal Content ) }` ``` > Note: The portal component renders its children outside the parent component's DOM hierarchy. However, it is important to note that the portal component is created using React context. This means that the portal component will not work if the parent component is not wrapped in a `GluestackUIProvider` or `OverlayProvider`. ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add portal ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```tsx 'use client'; const StyledOverlay = styled(Overlay, { className: "style" }); const Portal = React.forwardRef< React.ComponentRef, React.ComponentProps >(function Portal({ ...props }, ref) { return ; }); Portal.displayName = 'Portal'; export { Portal }; ``` ### 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. #### Portal | Prop | Type | Default | Description | | --- | --- | --- | --- | | `isOpen` | boolean | - | If true, the portal will open. | | `isKeyboardDismissable` | boolean | - | If true, the keyboard can dismiss the portal. | | `useRNModal` | boolean | false | If true, renders react-native native modal. | | `useRNModalOnAndroid` | boolean | false | If true, renders react-native native modal only in android. | | `onRequestClose` | {`((event: NativeSyntheticEvent) => void) | undefined`} | - | Callback is called when the user taps the hardware back button on Android or the menu button on Apple TV. This is required on Apple TV and Android. Only applicable when `useRNModal` is true. | | `animationPreset` | "fade" | "slide" | "none" | "fade" | The animation preset for the portal. | > Note: The portal component can be used to create a modal, popover, menu, tooltip, or any other component that needs to be rendered outside the parent component's DOM hierarchy. However, We recommend using our components like `Modal`, `Popover`, `Menu`, `Tooltip` for these use cases since they handle all the accessibility. --- ## Pressable URL: /ui/docs/components/pressable/index # Pressable Simplify interactive UI with the Pressable component in React Native. Manage hover, pressed, and focus events efficiently. Install now and improve your mobile app responsiveness! This is an illustration of **Pressable** component. ```jsx function Example() { return ( console.log("Hello")} className="p-5 bg-primary" > Press me ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add pressable ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```tsx 'use client'; const UIPressable = createPressable({ Root: withStyleContext(RNPressable), }); const pressableStyle = tva({ base: 'data-[focus-visible=true]:outline-none data-[focus-visible=true]:ring-indicator-info data-[focus-visible=true]:ring-2 data-[disabled=true]:opacity-40', }); type IPressableProps = Omit< React.ComponentProps, 'context' > & VariantProps; const Pressable = React.forwardRef< React.ComponentRef, IPressableProps >(function Pressable({ className, ...props }, ref) { return ( ); }); Pressable.displayName = 'Pressable'; export { Pressable }; ``` ### 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. #### Pressable It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) 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. ### 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 | | --- | --- | --- | | `disabled` | data-disabled | `true | false` | | `focusVisible` | data-focus-visible | `true | false` | | `active` | data-active | `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. #### Pressable child elements according to its states You can change the child elements according to the states of Pressable component. ```jsx function Example() { return ( {({ pressed }) => ( PRESSABLE )} ); }` ``` --- ## Progress URL: /ui/docs/components/progress/index # Progress Enhance your app with a responsive Progress component. gluestack-ui offers a React Native progress bar for tracking steps, ensuring a smooth progress bar UI experience. This is an illustration of **Progress** component. ```jsx function Example() { return (
) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add progress ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```tsx 'use client'; const SCOPE = 'PROGRESS'; export const UIProgress = createProgress({ Root: withStyleContext(View, SCOPE), FilledTrack: View, }); const progressStyle = tva({ base: 'bg-primary/20 relative h-2 w-full overflow-hidden rounded-full', variants: { orientation: { horizontal: 'w-full h-2', vertical: 'h-full w-2 justify-end', }, }, }); const progressFilledTrackStyle = tva({ base: 'bg-primary transition-all', variants: { orientation: { horizontal: 'h-full', vertical: 'w-full', }, }, }); type IProgressProps = VariantProps & React.ComponentProps; type IProgressFilledTrackProps = VariantProps & React.ComponentProps; const Progress = React.forwardRef< React.ComponentRef, IProgressProps >(function Progress( { className, orientation = 'horizontal', ...props }, ref ) { return ( ); }); const ProgressFilledTrack = React.forwardRef< React.ComponentRef, IProgressFilledTrackProps >(function ProgressFilledTrack({ className, ...props }, ref) { const { orientation: parentOrientation } = useStyleContext(SCOPE); return ( ); }); export { Progress, ProgressFilledTrack }; ``` ### 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. #### Progress It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | number | - | It is used to set the progress of the progress bar | #### ProgressFilledTrack It also inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. ### Accessibility We have outlined the various features that ensure the Fab 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://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/progressbar_role). #### Keyboard - Tab: Moves focus to the next focusable element. #### Screen Reader - VoiceOver: When the Progress is focused, the screen reader will announce it's a progress and it's current progress indicator. ### Props Progress 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). #### Progress | Name | Value | Default | | --- | --- | --- | | `size` | xs | sm | md | lg | xl | 2xl | md | | `orientation` | vertical | horizontal | horizontal | > 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. #### Value Progress component with value ```jsx function Example() { return ( Downloading 55% ); }` ``` #### Color You can add track color to progress bar as per our needs. ```jsx function Example() { return ( ); }` ``` #### Custom You can customize the progress bar. Below is the example where we have change the theme of progress bar. ```jsx function Example() { return ( Internal Storage 14GB ); }` ``` --- ## Radio URL: /ui/docs/components/radio/index # Radio Enhance your UI with a React Native radio button. Easily integrate radio buttons component with full accessibility support. This is an illustration of **Radio** component. ```jsx function Example() { return ( Label ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add radio ``` **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 SCOPE = 'Radio'; const StyledIcon = styled(UIIcon, { className: { target: 'style', }, }); const UIRadio = createRadio({ Root: (Platform.OS === 'web' ? withStyleContext(View, SCOPE) : withStyleContext(Pressable, SCOPE)) as ReturnType< typeof withStyleContext >, Group: View, Icon: StyledIcon, Indicator: View, Label: Text, }); const radioStyle = tva({ base: 'group/radio flex-row justify-start items-center gap-2 web:cursor-pointer data-[disabled=true]:web:cursor-not-allowed data-[disabled=true]:opacity-50', variants: { size: { sm: 'gap-1.5', md: 'gap-2', lg: 'gap-2', }, }, }); const radioGroupStyle = tva({ base: 'gap-3', }); const radioIconStyle = tva({ base: 'rounded-full absolute stroke-none fill-primary h-2 w-2', parentVariants: { size: { sm: 'h-[9px] w-[9px]', md: 'h-3 w-3', lg: 'h-4 w-4', }, }, }); const radioIndicatorStyle = tva({ base: 'relative justify-center items-center aspect-square h-4 w-4 shrink-0 rounded-full border border-border 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-[invalid=true]:ring-destructive/20 data-[invalid=true]:border-destructive data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50', parentVariants: { size: { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6', }, }, }); const radioLabelStyle = tva({ base: 'text-foreground text-sm font-medium web:select-none web:cursor-pointer data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50 font-body', parentVariants: { size: { '2xs': 'text-2xs', 'xs': 'text-xs', 'sm': 'text-sm', 'md': 'text-base', 'lg': 'text-lg', 'xl': 'text-xl', '2xl': 'text-2xl', '3xl': 'text-3xl', '4xl': 'text-4xl', '5xl': 'text-5xl', '6xl': 'text-6xl', }, }, }); type IRadioProps = Omit, 'context'> & VariantProps; const Radio = React.forwardRef, IRadioProps>( function Radio({ className, size = 'md', ...props }, ref) { return ( ); } ); type IRadioGroupProps = React.ComponentProps & VariantProps; const RadioGroup = React.forwardRef< React.ComponentRef, IRadioGroupProps >(function RadioGroup({ className, ...props }, ref) { return ( ); }); type IRadioIndicatorProps = React.ComponentProps & VariantProps; const RadioIndicator = React.forwardRef< React.ComponentRef, IRadioIndicatorProps >(function RadioIndicator({ className, ...props }, ref) { const { size } = useStyleContext(SCOPE); return ( ); }); type IRadioLabelProps = React.ComponentProps & VariantProps; const RadioLabel = React.forwardRef< React.ComponentRef, IRadioLabelProps >(function RadioLabel({ className, ...props }, ref) { const { size } = useStyleContext(SCOPE); return ( ); }); type IRadioIconProps = React.ComponentProps & VariantProps & { height?: number; width?: number; }; const RadioIcon = React.forwardRef< React.ComponentRef, IRadioIconProps >(function RadioIcon({ className, size, ...props }, ref) { const { size: parentSize } = useStyleContext(SCOPE); if (typeof size === 'number') { return ( ); } else if ( (props.height !== undefined || props.width !== undefined) && size === undefined ) { return ( ); } return ( ); }); Radio.displayName = 'Radio'; RadioGroup.displayName = 'RadioGroup'; RadioIndicator.displayName = 'RadioIndicator'; RadioLabel.displayName = 'RadioLabel'; RadioIcon.displayName = 'RadioIcon'; export { Radio, RadioGroup, RadioIcon, RadioIndicator, RadioLabel }; ``` ### 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. #### Radio Contains all Radio 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 radio input. This is the value that will be returned on form submission.`} | | `onChange` | function | - | {`Function called when the state of the radio changes.`} | | `isDisabled` | bool | false | {`To manually set disable to the radio.`} | | `isInvalid` | bool | false | {`To manually set invalid to the radio.`} | | `isHovered` | bool | false | {`To manually set hover to the radio.`} | | `isFocusVisible` | bool | false | {`To manually set focus visible state to the radio.`} | | `isIndeterminate` | bool | false | {`To manually set indeterminate to the radio.`} | #### RadioIndicator Contains all Indicator related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### RadioIcon 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. | #### RadioLabel 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. #### RadioGroup 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 radio group. | | `onChange` | function | - | The callback fired when any children Radio is checked or unchecked. | | `isReadOnly` | bool | false | {`To manually set read-only to the radio group.`} | ### Features - Keyboard support for actions. - Support for hover, focus and active states. ### Accessibility We have outlined the various features that ensure the Radio 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/radio/). #### Keyboard - `Tab`: Moves focus to the next focusable element. - `Shift + Tab`: Moves focus to the previous focusable element. - `Space`: To check or uncheck focused radio. #### Screen Reader - VoiceOver: When the radio is focused, the screen reader will announce it's a radio and it's current state (check or uncheck) and it's label. #### Focus Management - The `onFocus` and `onBlur` props 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 Radio 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). ### 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` | ### 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 Radio Radio buttons provide a mutually exclusive selection mechanism, allowing users to choose a single option from a set of related choices. ```jsx function App () { const [values, setValues] = React.useState("Eng"); return ( English French German ) }` ``` #### Horizontal Radio buttons can be used horizontally. ```jsx function App() { const [values, setValues] = React.useState("Cash On Delivery"); return ( Credit Card Cash On Delivery ) }` ``` #### With help text Radio buttons can be used with helper text. ```jsx function App () { const [values, setValues] = React.useState("Read-only"); return ( Extended coverage Extra services included Basic coverage Nothing extra included ) }` ``` #### Form Control Radio buttons can be used inside FormControl for better control of states and functionality. ```jsx function Example() { return ( Which time slot works best for you? Monday Tuesday Wednesday Choose a time slot for the meeting ); }` ``` #### Label left Radio buttons can be used horizontally ```jsx function App() { const [values, setValues] = React.useState("Monday"); return ( Jane Cooper Wade Warren ) }` ``` #### Controlled The Radio components can be used with a controlled state, enabling precise management of the selected option through external state management. ```jsx function App () { const [values, setValues] = React.useState('Apartments'); return ( Apartments Houses ) }` ``` #### Uncontrolled The Radio components can be used with either a ref or no state, providing an uncontrolled state where the selected option is managed internally. ```jsx function App () { const radioRef = React.useRef(null); const handleRadioChange = (e) => { e.preventDefault(); const checkboxValue = radioRef.current.checked; }; return ( Hotels Living quarters ); }` ``` #### Radio group The Radio group component allows users to group radio and display them in a horizontal or vertical row for better visual representation and functionality.. ```jsx function RadioGroupExample() { const [values, setValues] = React.useState("1st"); return ( Label 1 Label 2 Label 3 ); }` ``` --- ## Select URL: /ui/docs/components/select/index # Select Enhance your React Native app with a customizable Select dropdown component. Supports accessibility, animations, and flexible styling for a smooth user experience. This is an illustration of **Select** component. ```jsx function Example() { return ( ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add select ``` **Manual:** ### Step 1: Install the following dependencies: ```bash npm i @legendapp/motion ``` ### Step 2: Copy and paste the following code into your project. ```tsx 'use client'; const SelectTriggerWrapper = React.forwardRef< React.ComponentRef, React.ComponentProps >(function SelectTriggerWrapper({ ...props }, ref) { return ; }); const selectIconStyle = tva({ base: 'text-foreground/50 fill-none', parentVariants: { size: { '2xs': 'h-3 w-3', 'xs': 'h-3.5 w-3.5', 'sm': 'h-4 w-4', 'md': 'h-[18px] w-[18px]', 'lg': 'h-5 w-5', 'xl': 'h-6 w-6', }, }, }); const selectStyle = tva({ base: '', }); const selectTriggerStyle = tva({ base: 'border border-border rounded flex-row items-center overflow-hidden data-[hover=true]:border-primary/80 data-[focus=true]:border-primary/80 data-[disabled=true]:opacity-40 data-[disabled=true]:data-[hover=true]:border-border/80', variants: { size: { xl: 'min-h-12', lg: 'min-h-11', md: 'min-h-10', sm: 'min-h-9', }, variant: { underlined: 'border-0 border-b rounded-none data-[hover=true]:border-primary/80 data-[focus=true]:border-primary/80 data-[focus=true]:web:shadow-[inset_0_-1px_0_0] data-[focus=true]:web:shadow-primary/80 data-[invalid=true]:border-destructive data-[invalid=true]:web:shadow-destructive', outline: 'data-[focus=true]:border-primary/80 data-[focus=true]:web:shadow-[inset_0_0_0_1px] data-[focus=true]:data-[hover=true]:web:shadow-primary/80 data-[invalid=true]:web:shadow-[inset_0_0_0_1px] data-[invalid=true]:border-destructive data-[invalid=true]:web:shadow-destructive data-[invalid=true]:data-[hover=true]:border-destructive', rounded: 'rounded-full data-[focus=true]:border-primary/80 data-[focus=true]:web:shadow-[inset_0_0_0_1px] data-[focus=true]:web:shadow-primary/80 data-[invalid=true]:border-destructive data-[invalid=true]:web:shadow-destructive', }, }, }); const selectInputStyle = tva({ base: 'px-3 placeholder:text-foreground/50 web:w-full h-full text-foreground/90 pointer-events-none web:outline-none ios:leading-[0px] py-0', parentVariants: { size: { xl: 'text-xl', lg: 'text-lg', md: 'text-base', sm: 'text-sm', }, variant: { underlined: 'px-0', outline: '', rounded: 'px-4', }, }, }); const StyledIcon = styled(UIIcon, { className: { target: 'style', }, }); const UISelect = createSelect( { Root: View, Trigger: withStyleContext(SelectTriggerWrapper), Input: TextInput, Icon: StyledIcon, }, { Portal: Actionsheet, Backdrop: ActionsheetBackdrop, Content: ActionsheetContent, DragIndicator: ActionsheetDragIndicator, DragIndicatorWrapper: ActionsheetDragIndicatorWrapper, Item: ActionsheetItem, ItemText: ActionsheetItemText, ScrollView: ActionsheetScrollView, VirtualizedList: ActionsheetVirtualizedList, FlatList: ActionsheetFlatList, SectionList: ActionsheetSectionList, SectionHeaderText: ActionsheetSectionHeaderText, } ); type ISelectProps = VariantProps & React.ComponentProps & { className?: string }; const Select = React.forwardRef< React.ComponentRef, ISelectProps >(function Select({ className, ...props }, ref) { return ( ); }); type ISelectTriggerProps = VariantProps & React.ComponentProps & { className?: string }; const SelectTrigger = React.forwardRef< React.ComponentRef, ISelectTriggerProps >(function SelectTrigger( { className, size = 'md', variant = 'outline', ...props }, ref ) { return ( ); }); type ISelectInputProps = VariantProps & React.ComponentProps & { className?: string }; const SelectInput = React.forwardRef< React.ComponentRef, ISelectInputProps >(function SelectInput({ className, ...props }, ref) { const { size: parentSize, variant: parentVariant } = useStyleContext(); return ( ); }); type ISelectIcon = VariantProps & React.ComponentProps & { className?: string }; const SelectIcon = React.forwardRef< React.ComponentRef, ISelectIcon >(function SelectIcon({ className, size, ...props }, ref) { const { size: parentSize } = useStyleContext(); if (typeof size === 'number') { return ( ); } else if ( //@ts-expect-error : web only (props?.height !== undefined || props?.width !== undefined) && size === undefined ) { return ( ); } return ( ); }); Select.displayName = 'Select'; SelectTrigger.displayName = 'SelectTrigger'; SelectInput.displayName = 'SelectInput'; SelectIcon.displayName = 'SelectIcon'; // Actionsheet Components const SelectPortal = UISelect.Portal; const SelectBackdrop = UISelect.Backdrop; const SelectContent = UISelect.Content; const SelectDragIndicator = UISelect.DragIndicator; const SelectDragIndicatorWrapper = UISelect.DragIndicatorWrapper; const SelectItem = UISelect.Item; const SelectScrollView = UISelect.ScrollView; const SelectVirtualizedList = UISelect.VirtualizedList; const SelectFlatList = UISelect.FlatList; const SelectSectionList = UISelect.SectionList; const SelectSectionHeaderText = UISelect.SectionHeaderText; export { Select, SelectTrigger, SelectInput, SelectIcon, SelectPortal, SelectBackdrop, SelectContent, SelectDragIndicator, SelectDragIndicatorWrapper, SelectItem, SelectScrollView, SelectVirtualizedList, SelectFlatList, SelectSectionList, SelectSectionHeaderText, }; ``` ### 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. #### Select Contains all Select related layout style and high-level select props. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. > Note: Please note that this select component renders as an Actionsheet on `iOS` and `Android` devices for a native feel. On `web`, it behaves like a regular select element and renders the default HTML options. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `isDisabled` | boolean | false | {`When true, the select is disabled and cannot be edited.`} | | `isInvalid` | boolean | false | {`When true, the select displays an error state.`} | | `isRequired` | boolean | false | {`When true, sets aria-required="true" on the input.`} | | `isHovered` | boolean | false | {`When true, the select displays a hover state.`} | | `isFocusVisible` | boolean | false | {`When true, the focus ring of select will be visible.`} | | `isFocused` | boolean | false | {`When true, the select displays a focus state.`} | | `closeOnOverlayClick` | boolean | false | {`When true, the select will close when the overlay is clicked.`} | | `selectedValue` | string | - | {`Sets the currently selected option value, allowing the component to render with the corresponding option pre-selected.`} | | `initialLabel` | string | - | {`Sets the initial selected Label for a select component.`} | | `defaultValue` | string | - | {`Sets the initial selected option value for a select component.`} | | `onOpen` | {`() => any`} | - | {`Callback to be invoked when Select Dropdown or Actionsheet is opened.`} | | `onValueChange` | {`() => any`} | - | {`Callback to be invoked when Select value is changed.`} | | `onClose` | {`() => any`} | - | {`Callback to be invoked when Select Dropdown or Actionsheet is closed.`} | > Note: Please note that if you want to use `initialLabel` prop, then you also need to pass `defaultValue` or `selectedValue` prop, to select the option value for a select component. #### SelectTrigger It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) component. #### SelectInput It inherits all the properties of React Native's [TextInput](https://reactnative.dev/docs/textInput) component. #### SelectIcon It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) component. #### SelectPortal It is internally mapped wth gluestack-ui's [Actionsheet](/ui/docs/components/disclosure/actionsheet) component, which inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. > Note: If **snapPoints** are not provided to `SelectPortal`, then it's essential to set **maxHeight** to `SelectContent`. #### SelectBackdrop It is internally mapped wth gluestack-ui's [ActionsheetBackdrop](https://ui.gluestack.io/docs/components/disclosure/actionsheet) component, which 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. #### SelectContent It is internally mapped wth gluestack-ui's [ActionsheetContent](https://ui.gluestack.io/docs/components/disclosure/actionsheet) component, which 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. #### SelectDragIndicatorWrapper It is internally mapped wth gluestack-ui's [ActionsheetDragIndicatorWrapper](https://ui.gluestack.io/docs/components/disclosure/actionsheet) component, which inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### SelectDragIndicator It is internally mapped wth gluestack-ui's [ActionsheetDragIndicator](https://ui.gluestack.io/docs/components/disclosure/actionsheet) component, which inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### SelectItem It internally uses gluestack-ui's [ActionsheetItem](https://ui.gluestack.io/docs/components/disclosure/actionsheet) and [Actionsheet.ItemText](https://ui.gluestack.io/docs/components/disclosure/actionsheet) component, which inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/pressable) component. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `isDisabled` | bool | false | {`When true, its disabled state activates.`} | | `label` | string | - | {`setting label that displays to the user.`} | | `value` | string | - | {`The value to be used for the item. This is the value that will be returned on form submission.`} | | `textStyle` | inherits all the properties of react native text | - | {`This prop only works on native.`} | #### SelectScrollView It is internally mapped wth gluestack-ui's [Actionsheet.ScrollView](/ui/docs/components/disclosure/actionsheet) component, which inherits all the properties of React Native's [ScrollView](https://reactnative.dev/docs/scrollview#props) component. #### SelectVirtualizedList It is internally mapped wth gluestack-ui's [Actionsheet.VirtualizedList](/ui/docs/components/disclosure/actionsheet) component, which inherits all the properties of React Native's [VirtualizedList](https://reactnative.dev/docs/virtualizedlist#props) component. #### SelectFlatList It is internally mapped wth gluestack-ui's [Actionsheet.FlatList](/ui/docs/components/disclosure/actionsheet) component, which inherits all the properties of React Native's [FlatList](https://reactnative.dev/docs/flatlist#props) component. #### SelectSectionList It is internally mapped wth gluestack-ui's [Actionsheet.SectionList](/ui/docs/components/disclosure/actionsheet) component, which inherits all the properties of React Native's [SectionList](https://reactnative.dev/docs/sectionlist#props) component. #### SelectSectionHeaderText It is internally mapped wth gluestack-ui's [Actionsheet.SectionHeaderText](/ui/docs/components/disclosure/actionsheet) component, which inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text#props) component. ### Features - Support for native and web platforms separately. - Support for passing custom trigger. ### Accessibility We have outlined the various features that ensure the Select 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://react-spectrum.adobe.com/react-aria/Selecthtml). #### Keyboard - `Tab + Space`: Triggers the select's action. #### Screen Reader - VoiceOver: When the select is focused, the screen reader will announce the select content. ### Props Select 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. #### Select | Name | Value | Default | | --- | --- | --- | | `size` | sm | md | lg | xl | md | | `variant` | underlined | outline | rounded | outline | ### 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` | | `disabled` | `data-disabled` | `true | false` | | `focus` | `data-focus` | `true | false` | | `invalid` | `data-invalid` | `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. #### Select FormControlled The Select component with FormControl example demonstrates how to manage its state using the FormControl component. This allows for easy integration with forms and form validation. ```jsx function Example() { return ( Choose your favorite color You can only select one option Mandatory field ); }` ``` --- ## Skeleton URL: /ui/docs/components/skeleton/index # Skeleton Discover the ultimate gluestack-ui Skeleton component for React & React Native. Improve app loading visuals with gluestack-ui easy-to-use Skeleton. This is an illustration of **Skeleton** component. ```jsx function Example() { return ( ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add skeleton ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```jsx type ISkeletonProps = React.ComponentProps & VariantProps & { isLoaded?: boolean; startColor?: string; speed?: number | string; }; type ISkeletonTextProps = React.ComponentProps & VariantProps & { _lines?: number; isLoaded?: boolean; startColor?: string; }; const Skeleton = forwardRef, ISkeletonProps>( function Skeleton( { className, variant, children, startColor = 'bg-accent', isLoaded = false, speed = 4, ...props }, ref ) { if (!isLoaded) { return ( ); } else { return children; } } ); const SkeletonText = forwardRef< React.ComponentRef, ISkeletonTextProps >(function SkeletonText( { className, _lines, isLoaded = false, startColor = 'bg-accent', gap = 2, children, ...props }, ref ) { if (!isLoaded) { if (_lines) { return ( {Array.from({ length: _lines }).map((_, index) => ( ))} ); } else { return ( ); } } else { return children; } }); Skeleton.displayName = 'Skeleton'; SkeletonText.displayName = 'SkeletonText'; export { Skeleton, SkeletonText }; ``` > 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: Copy and paste the following code into index.web.tsx in your project. ```jsx ``` ### Step 3: Copy and paste the following code into styles.tsx in your project. ```jsx export const skeletonStyle = tva({ base: 'w-full h-full rounded-sm', variants: { variant: { sharp: 'rounded-none', circular: 'rounded-full', rounded: 'rounded-md', }, speed: { 1: 'duration-750', 2: 'duration-100', 3: 'duration-1500', 4: 'duration-2000', }, }, }); export const skeletonTextStyle = tva({ base: 'rounded-sm w-full', variants: { speed: { 1: 'duration-750', 2: 'duration-1000', 3: 'duration-1500', 4: 'duration-2000', }, gap: { 1: 'gap-1', 2: 'gap-2', 3: 'gap-3', 4: 'gap-4', }, }, }); ``` ### 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. ### Skeleton Renders a `
` on web and a `Animated.View` on native and has the following properties: | Platform | Output | | --- | --- | | `{`Web`}` | {`
`} | | `Native` | {``} | | Prop | Type | Default | Description | | --- | --- | --- | --- | | `variant` | rounded | sharp | circular | rounded | Shape of the skeleton component | | `startColor` | string | bg-background-200 | Sets the color of the skeleton animation | | `isLoaded` | bool | false | When true, the skeleton content will be displayed | | `speed` | number | 2 | Sets the animation speed of the skeleton component | ### SkeletonText Renders a `
` on web and a `Animated.View` on native and has the following properties: | Platform | Output | | --- | --- | | `{`Web`}` | {`
`} | | `Native` | {``} | | Prop | Type | Default | Description | | --- | --- | --- | --- | | `lines` | number | - | Number of lines in text skeleton | | `startColor` | string | bg-background-200 | Sets the color of the skeleton animation | | `isLoaded` | bool | false | When true, the skeleton content will be displayed | | `speed` | number | 2 | Sets the animation speed of the skeleton component | | `gap` | number | - | Sets the gap between the text skeletons | ### 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. #### Using isLoaded prop Use the `isLoaded` prop to show the content after the skeleton content is loaded. ```jsx function Example() { 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. John Smith John Smith ); }` ``` --- ## Slider URL: /ui/docs/components/slider/index # Slider Create smooth, interactive controls with the gluestack-ui React Native Slider component. Customize track height, values, and states for a seamless slider UI experience. This is an illustration of **Slider** component. ```jsx function Example() { return (
) }` ``` ## Installation ### Run the following command: ```bash npx gluestack-ui add slider ``` ### Step 1: Copy and paste the following code into your project. ```jsx 'use client'; const SCOPE = 'SLIDER'; const Root = withStyleContext(View, SCOPE); export const UISlider = createSlider({ Root: Root, Thumb: View, Track: Pressable, FilledTrack: View, ThumbInteraction: View, }); const StyledTrack = styled(UISlider.Track, { className: 'style', }); const sliderStyle = tva({ base: 'justify-center items-center data-[disabled=true]:opacity-40 data-[disabled=true]:web:pointer-events-none', variants: { orientation: { horizontal: 'w-full', vertical: 'h-full', }, isReversed: { true: '', false: '', }, }, }); const sliderThumbStyle = tva({ base: 'bg-white border border-primary ring-ring/50 absolute rounded-full shadow-sm transition-[color,box-shadow] data-[hover=true]:ring-4 data-[focus-visible=true]:ring-4 data-[focus-visible=true]:outline-hidden disabled:pointer-events-none disabled:opacity-50 web:cursor-pointer h-4 w-4', }); const sliderTrackStyle = tva({ base: 'bg-muted rounded-full overflow-hidden', parentVariants: { orientation: { horizontal: 'w-full h-1.5 flex-row', vertical: 'h-full w-1.5 flex-col-reverse', }, isReversed: { true: '', false: '', }, }, parentCompoundVariants: [ { orientation: 'horizontal', isReversed: true, class: 'flex-row-reverse', }, { orientation: 'vertical', isReversed: true, class: 'flex-col', }, ], }); const sliderFilledTrackStyle = tva({ base: 'bg-primary', parentVariants: { orientation: { horizontal: 'h-full', vertical: 'w-full', }, }, }); type ISliderProps = React.ComponentProps & VariantProps; const Slider = React.forwardRef< React.ComponentRef, ISliderProps >(function Slider( { className, orientation = 'horizontal', isReversed = false, ...props }, ref ) { return ( ); }); type ISliderThumbProps = React.ComponentProps & VariantProps; const SliderThumb = React.forwardRef< React.ComponentRef, ISliderThumbProps >(function SliderThumb({ className, ...props }, ref) { return ( ); }); type ISliderTrackProps = React.ComponentProps & VariantProps; const SliderTrack = React.forwardRef< React.ComponentRef, ISliderTrackProps >(function SliderTrack({ className, ...props }, ref) { const { orientation: parentOrientation, isReversed, } = useStyleContext(SCOPE); return ( ); }); type ISliderFilledTrackProps = React.ComponentProps< typeof UISlider.FilledTrack > & VariantProps; const SliderFilledTrack = React.forwardRef< React.ComponentRef, ISliderFilledTrackProps >(function SliderFilledTrack({ className, ...props }, ref) { const { orientation: parentOrientation } = useStyleContext(SCOPE); return ( ); }); export { Slider, SliderFilledTrack, SliderThumb, SliderTrack }; ``` ### 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. #### Slider It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `onChange` | {'(value: number) => void'} | - | Function called when the state of the Slider changes. | | `isDisabled` | bool | false | When true, this will disable Slider | | `isReadOnly` | boolean | false | To manually set read-only to the checkbox. | | `sliderTrackHeight` | number | 8 | To change the slider track height. | | `defaultValue` | number | - | To change the slider value. | | `minValue` | number | - | The slider's minimum value | | `maxValue` | number | - | The slider's maximum value. | | `value` | number | - | The slider's current value. | | `step` | number | - | The slider's step value. | **Descendants Styling Props** Props to style child components. | Sx Prop | Description | | --- | --- | | `_thumb` | Prop to style SliderThumb Component | | `_track` | Prop to style SliderTrack Component | | `_filledTrack` | Prop to style SliderFilledTrack Component | #### SliderTrack It inherits all the properties of React Native's [Pressable](https://reactnative.dev/docs/Pressable) component. #### SliderFilledTrack It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. #### SliderThumb 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. ### Accessibility We have outlined the various features that ensure the Slider 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/slider/). #### Keyboard - `Tab`: Moves focus to the next focusable element. - `Right Arrow`: Increase the value of the slider by one step. - `Up Arrow`: Increase the value of the slider by one step. - `Left Arrow`: Decrease the value of the slider by one step. - `Down Arrow`: Decrease the value of the slider by one step. ### Screen Reader - VoiceOver: When the slider is focused, the screen reader will announce the slider's value. ### Props Slider 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). ### 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 | | --- | --- | --- | | `disabled` | data-disabled | `true | false` | | `focus` | data-focus | `true | false` | | `invalid` | data-invalid | `true | false` | --- ## Spinner URL: /ui/docs/components/spinner/index # Spinner Enhance your UI with the gluestack-ui Spinner component. A React Native spinner with ShadCN styling for smooth loading indicators. Optimize your spinner UI design with ease. This is an illustration of **Spinner** component. ```jsx function Example() { return }` ``` ## Installation ### Run the following command: ```bash npx gluestack-ui add spinner ``` ### Step 1: Copy and paste the following code into your project. ```jsx 'use client'; const StyledActivityIndicator = styled(ActivityIndicator, { className: { target: 'style', nativeStyleToProp: { color: true } }, }); const spinnerStyle = tva({}); const Spinner = React.forwardRef< React.ComponentRef, React.ComponentProps >(function Spinner( { className, color, focusable = false, 'aria-label': ariaLabel = 'loading', ...props }, ref ) { return ( ); }); Spinner.displayName = 'Spinner'; export { Spinner }; ``` ### 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 () => ; ``` --- ## Switch URL: /ui/docs/components/switch/index # Switch Enhance your UI with a sleek Switch Component. Built on React Native, it's customizable and accessible. Perfect for toggling options seamlessly. > The current component is functioning as expected, but we have decided to rebuild it entirely to align with our vision and enhance its performance. It is primarily based on React Native, and while we have made minimal modifications so far, our upcoming rebuild will involve significant changes to ensure it closely matches our desired structure and meets our objectives. This is an illustration of **Switch** component. ```jsx function Example() { return (
) }` ``` ## Installation ### Run the following command: ```bash npx gluestack-ui add switch ``` ### Step 1: Copy and paste the following code into your project. ```jsx 'use client'; const UISwitch = createSwitch({ Root: withStyleContext(RNSwitch), }); const switchStyle = tva({ base: 'data-[focus=true]:outline-0 data-[focus=true]:ring-2 data-[focus=true]:ring-indicator-primary web:cursor-pointer disabled:cursor-not-allowed data-[disabled=true]:opacity-40 data-[invalid=true]:border-destructive data-[invalid=true]:rounded-xl data-[invalid=true]:border-2', variants: { size: { sm: 'scale-[0.75]', md: '', lg: 'scale-[1.25]', }, }, }); type ISwitchProps = React.ComponentProps & VariantProps; const Switch = React.forwardRef< React.ComponentRef, ISwitchProps >(function Switch({ className, size = 'md', ...props }, ref) { return ( ); }); Switch.displayName = 'Switch'; export { Switch }; ``` ### 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. #### Switch Contains all switch related layout style props and actions. It inherits all the properties of React Native's [Switch](https://reactnative.dev/docs/switch) component. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `isDisabled` | boolean | false | When true, the switch is disabled and cannot be toggled | | `isInvalid` | boolean | false | When true, the switch displays an error state. | | `isRequired` | boolean | false | When true, sets aria-required="true" on the switch. | | `isHovered` | boolean | false | When true, the switch displays a hover state. | | `value` | boolean | false | The value of the switch. If true the switch will be turned on. | | `defaultValue` | boolean | false | The defaultValue of the switch. If true the switch will be turned on initially. | | `onToggle` | {`() => any`} | - | Callback to be invoked when switch value is changed. | ### 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/switch/). #### Keyboard - `Tab`: Moves focus to the next focusable element. - `Space`: Triggers the switch's action. ### Screen Reader - VoiceOver: When the switch is focused, the screen reader will announce the switch's action and its current state. ### Props Switch component is created using Switch component from react-native. It extends all the props supported by [React Native Switch](https://reactnative.dev/docs/switch#props). #### Switch | Name | Value | Default | | --- | --- | --- | | `size` | sm | md | lg | 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. ### 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. #### Switch With Label An example of a switch component with a label, which includes another switch component for toggling additional options. ```jsx function Example() { return ( Allow notifications ); }` ``` #### Checked State An example of a switch component used within a checked state component to represent a pre-selected or activated option. ```jsx function Example() { return ( Public profile ); }` ``` --- ## Table URL: /ui/docs/components/table/index # Table Effortlessly manage tabular data with gluestack-ui Table component. A fully customizable React Native table component for smooth data display in your UI. Perfect for any project! This is an illustration of **Table** component. ```jsx function Example() { return ( Customer Name Units Costs Rajesh Kumar 10 $130 Priya Sharma 12 $210 Total 22 $340
) }` ``` ## Installation ## 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. #### Table It inherits all the properties of of @expo/html-elements's [Table](https://www.npmjs.com/package/@expo/html-elements#table) on native and html table tag on web. #### TableHeader It inherits all the properties of of @expo/html-elements's [THead](https://www.npmjs.com/package/@expo/html-elements#thead) on native and html thead tag on web. #### TableRow It inherits all the properties of of @expo/html-elements's [TR](https://www.npmjs.com/package/@expo/html-elements#tr) on native and html tr tag on web. #### TableData It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component on native and html td tag on web. <> Prop Type Default Prop Type Default `useRNView` boolean If true renders a react-native view component instead of a text component. #### TableHead It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component on native and html th tag on web. <> Prop Type Default `useRNView` boolean If true renders a react-native view component instead of a text component. boolean If true renders a react-native view component instead of a text component. #### TableBody It inherits all the properties of of @expo/html-elements's [TBody](https://www.npmjs.com/package/@expo/html-elements#tbody) on native and html tbody tag on web. #### TableFooter It inherits all the properties of of @expo/html-elements's [TFoot](https://www.npmjs.com/package/@expo/html-elements#tfoot) on native and html tfoot tag on web. #### TableCaption It inherits all the properties of of @expo/html-elements's [Caption](https://www.npmjs.com/package/@expo/html-elements#caption) on native and html caption tag on web. ### 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. #### Table with caption This is an example of a Table component with a caption. ```jsx function App(){ return ( Name Email Address Phone Number Rajesh Kumar rajesh@example.com 1234567890 Priya Sharma priya@example.com 1234567890 Ravi Patel ravi@example.com 1234567890 Ananya Gupta ananya@example.com 1234567890 Arjun Singh arjun@example.com 1234567890 Nisha Verma nisha@example.com 1234567890 Showing recent membership details
); }` ``` #### Stripped table This example demonstrates a table with a striped background. ```jsx function App(){ return ( Order id Items Name City Order price Status 5771 3 Rajesh Kumar New Jersey $ 200 Completed 5231 2 Priya Sharma Austin $ 150 Processing 5771 3 Ravi Patel Seattle $ 215 Shipped 5231 4 Ananya Gupta California $ 88 Processing 5771 3 Arjun Singh Seattle $ 115 Completed 5771 3 Nisha Verma Seattle $ 115 Processing
); }` ``` #### Table with other components This example illustrates a table component with a badge component. ```jsx function App(){ return ( Order id Items Name City Order price Status 571 3 Rajesh Kumar New Jersey $ 200 Completed 5231 2 Priya Sharma Austin $ 150 Processing 5771 3 Ravi Patel Seattle $ 215 Shipped 5231 4 Ananya Gupta California $ 88 Processing 5771 3 Arjun Singh Seattle $ 115 Processing 5771 3 Nisha Verma Austin $ 115 Completed
); }` ``` #### Table with top and side header This example illustrates a table component with a top and a side header. ```jsx function App(){ return ( Player Pts Reb Ast Stl Blk LeBron James 30 10 5 5 2 Anthony Davis 21 15 10 3 6 Austin Reaves 18 8 15 3 3 Kobe Bryant 32 12 13 4 5
); }` ``` --- ## Tabs URL: /ui/docs/components/tabs/index # Tabs Create an organized UI using the gluestack-ui Tabs component in React & React Native. Add tabbed navigation seamlessly with animations. This is an illustration of **Tabs** component. ```jsx function Example() { return ( Home Profile Settings Welcome to the Home tab! Your profile information Settings and preferences ); }` ``` ## Installation ### Run the following command: ```bash npx gluestack-ui add tabs ``` > Note: At present, we have integrated `react-native-reanimated` for animations. You have the option to remove this and implement your own custom animation wrapper. ### Step 1: Install the following dependencies: ```bash npm i react-native-reanimated ``` ### Step 2: Copy and paste the following code into your project. ```jsx 'use client'; const SCOPE = 'TABS'; const AnimatedView = Animated.createAnimatedComponent(View); const AnimatedFlatList = Animated.createAnimatedComponent(FlatList); /** Styles */ const tabsStyle = tva({ base: 'w-full gap-1', }); const tabsListStyle = tva({ base: 'flex relative z-10 bg-muted p-1 rounded-lg', variants: { orientation: { horizontal: 'flex-row', vertical: 'flex-col', }, }, }); const tabsTriggerStyle = tva({ base: 'justify-center relative z-30 items-center web:outline-none data-[disabled=true]:opacity-40 data-[focus-visible=true]:web:ring-2 data-[focus-visible=true]:web:ring-primary/20 px-3 py-1.5 flex-row gap-1', parentVariants: { variant: { underlined: '', filled: 'rounded-lg', }, }, }); const tabsTriggerTextStyle = tva({ base: 'text-foreground/70 data-[selected=true]:text-foreground font-medium data-[hover=true]:text-foreground/90 ', }); const tabsTriggerIconStyle = tva({ base: 'h-4 w-4 fill-none pointer-events-none shrink-0 text-foreground/70 data-[selected=true]:text-foreground', }); const tabsContentStyle = tva({ base: 'p-2 h-auto', }); const tabsContentWrapperStyle = tva({ base: 'overflow-hidden rounded-lg', }); const tabsIndicatorStyle = tva({ base: 'pointer-events-none', parentVariants: { variant: { underlined: 'border-b border-primary', filled: 'bg-background z-20 rounded-lg', }, }, }); /** Creator */ const Root = withStyleContext(View, SCOPE); const StyledUIIcon = styled(UIIcon, { className: 'style', }); const UITabs = createTabs({ Root, List: View, Trigger: Pressable, Content: View, ContentWrapper: AnimatedView, TriggerText: Text, TriggerIcon: StyledUIIcon, Indicator: View, }); /** Type definitions */ type ITabsProps = React.ComponentPropsWithoutRef & VariantProps & { variant?: 'underlined' | 'filled'; }; type ITabsListProps = React.ComponentPropsWithoutRef; type ITabsTriggerProps = React.ComponentPropsWithoutRef; type ITabsContentProps = React.ComponentPropsWithoutRef; type ITabsContentWrapperProps = React.ComponentPropsWithoutRef< typeof UITabs.ContentWrapper >; type ITabsTriggerTextProps = React.ComponentPropsWithoutRef< typeof UITabs.TriggerText >; type ITabsTriggerIconProps = React.ComponentPropsWithoutRef< typeof UITabs.TriggerIcon > & { as?: React.ElementType; }; type ITabsIndicatorProps = React.ComponentPropsWithoutRef< typeof UITabs.Indicator >; /** Components */ const Tabs = React.forwardRef, ITabsProps>( ({ className, variant = 'filled', ...props }, ref) => { return ( ); } ); const TabsList = React.forwardRef< React.ComponentRef, ITabsListProps >(({ className, children, ...props }, ref) => { const context = React.useContext(TabsContext); const flatListRef = useRef(null); if (!context) return null; const { orientation, setScrollOffset, selectedKey, listRef } = context; // Shared value for indicator sync const animatedScrollOffset = useSharedValue(0); /** * Expose shared value to context */ useEffect(() => { if (context) { // @ts-ignore context.animatedScrollOffset = animatedScrollOffset; } }, [context, animatedScrollOffset]); /** * Auto scroll to selected tab */ useEffect(() => { if (orientation !== 'horizontal' || !selectedKey) return; const childArray = React.Children.toArray(children); const triggers = childArray.filter( (child: any) => child?.type?.displayName !== 'TabsIndicator' ); const selectedIndex = triggers.findIndex( (child: any) => child?.props?.value === selectedKey ); if (selectedIndex >= 0 && flatListRef.current) { const timer = setTimeout(() => { try { flatListRef.current.scrollToIndex({ index: selectedIndex, animated: true, viewPosition: 0.5, }); } catch {} }, 100); return () => clearTimeout(timer); } }, [selectedKey, orientation, children]); /** * Native animated scroll handler (ONLY for iOS / Android) */ const nativeScrollHandler = Platform.OS === 'web' ? undefined : useAnimatedScrollHandler({ onScroll: (event) => { 'worklet'; const x = event.contentOffset.x; animatedScrollOffset.value = x; runOnJS(setScrollOffset)(x); }, }); /** * Web scroll handler (JS thread) */ const handleWebScroll = (e: any) => { const x = e.nativeEvent.contentOffset.x; animatedScrollOffset.value = x; setScrollOffset(x); }; /** * Horizontal tabs → FlatList */ // Memoize the split so FlatList's `data` prop stays referentially stable // across scroll-driven re-renders (scrollOffset in context ticks on every // scroll event; without this, FlatList re-renders every cell every frame, // firing onLayout → measureTrigger on every scroll tick). const { triggers, indicator } = useMemo(() => { const childArray = React.Children.toArray(children); return { triggers: childArray.filter( (child: any) => child?.type?.displayName !== 'TabsIndicator' ), indicator: childArray.find( (child: any) => child?.type?.displayName === 'TabsIndicator' ), }; }, [children]); if (orientation === 'horizontal') { return ( {indicator} item as any} keyExtractor={(item: any, index) => item?.props?.value ?? `tab-${index}` } showsHorizontalScrollIndicator={false} scrollEventThrottle={16} style={{ zIndex: 100 }} onScroll={ Platform.OS === 'web' ? handleWebScroll : nativeScrollHandler } onScrollToIndexFailed={(info) => { setTimeout(() => { try { flatListRef.current?.scrollToIndex({ index: info.index, animated: false, viewPosition: 0.5, }); } catch {} }, 500); }} {...props} /> ); } /** * Vertical tabs → default List */ return ( {children} ); }); const TabsTrigger = React.forwardRef< React.ComponentRef, ITabsTriggerProps >(({ className, ...props }, ref) => { const { variant } = useStyleContext(SCOPE); return ( ); }); const TabsContent = React.forwardRef< React.ComponentRef, ITabsContentProps >(({ className, ...props }, ref) => { return ( ); }); const TabsContentWrapper = React.forwardRef< React.ComponentRef, ITabsContentWrapperProps >(({ className, targetHeight, ...props }: any, ref) => { const context = React.useContext(TabsContext); // Get the height of the selected content from the layouts Map const selectedLayout = context?.selectedKey ? context.contentLayouts.get(context.selectedKey) : null; const height = selectedLayout?.height || 0; // Use shared value for Reanimated with initial height const heightValue = useSharedValue(height); const isFirstRender = React.useRef(true); // Update shared value when height changes React.useEffect(() => { if (height > 0) { if (isFirstRender.current) { // Set initial height without animation heightValue.value = height; isFirstRender.current = false; } else { // Animate height changes heightValue.value = withSpring(height,{duration:100}); } } }, [height, heightValue]); // Animated style for height transitions const animatedStyle = useAnimatedStyle(() => { return { height: heightValue.value > 0 ? heightValue.value : 'auto', }; }, []); return ( ); }); const TabsTriggerText = React.forwardRef< React.ComponentRef, ITabsTriggerTextProps >(({ className, ...props }, ref) => { return ( ); }); const TabsTriggerIcon = React.forwardRef< React.ComponentRef, ITabsTriggerIconProps >(({ className, ...props }, ref) => { const { variant } = useStyleContext(SCOPE); // Remove `dataSet` ONLY on web to avoid React DOM warning const safeProps = Platform.OS === 'web' ? (() => { const { dataSet, ...rest } = props as any; return rest; })() : props; return ( ); }); const TabsIndicator = React.forwardRef< React.ComponentRef, ITabsIndicatorProps >(({ className, ...props }, ref) => { const context = React.useContext(TabsContext); const { variant } = useStyleContext(SCOPE); if (!context) { return null; } const { selectedKey, orientation, triggerLayouts, scrollOffset } = context; // @ts-ignore - Get animated scroll offset from context const animatedScrollOffset = context.animatedScrollOffset; return ( ); }); Tabs.displayName = 'Tabs'; TabsList.displayName = 'TabsList'; TabsTrigger.displayName = 'TabsTrigger'; TabsContent.displayName = 'TabsContent'; TabsContentWrapper.displayName = 'TabsContentWrapper'; TabsTriggerText.displayName = 'TabsTriggerText'; TabsTriggerIcon.displayName = 'TabsTriggerIcon'; TabsIndicator.displayName = 'TabsIndicator' export { Tabs, TabsList, TabsTrigger, TabsContent, TabsContentWrapper, TabsTriggerText, TabsTriggerIcon, TabsIndicator, }; ``` ```jsx interface TabsAnimatedIndicatorProps { selectedKey: any; orientation: 'horizontal' | 'vertical'; triggerLayouts: Map; scrollOffset?: number; animatedScrollOffset?: SharedValue; className?: string; style?: any; } const isWeb = Platform.OS === 'web'; export const TabsAnimatedIndicator = React.forwardRef< any, TabsAnimatedIndicatorProps >( ( { selectedKey, orientation, triggerLayouts, scrollOffset = 0, animatedScrollOffset, className, style, }, ref ) => { const animatedX = useSharedValue(0); const animatedY = useSharedValue(0); const animatedWidth = useSharedValue(0); const animatedHeight = useSharedValue(0); const [hasLayout, setHasLayout] = useState(false); // Create a shared value for scroll offset to use in worklet const scrollOffsetShared = useSharedValue(scrollOffset); // Update scroll offset shared value when scrollOffset changes useEffect(() => { if (!animatedScrollOffset) { scrollOffsetShared.value = scrollOffset; } }, [scrollOffset, scrollOffsetShared, animatedScrollOffset]); useEffect(() => { if (selectedKey && triggerLayouts.has(selectedKey)) { const layout = triggerLayouts.get(selectedKey); if (layout && layout.width > 0) { // Determine if this is the first time we're setting values const isFirstRender = !hasLayout; const duration = isFirstRender ? 0 : tabsAnimationConfig.indicatorDuration; // Store the absolute x position (not adjusted for scroll) animatedX.value = withDelay( 20, withTiming(layout.x, { duration: duration, easing: Easing.ease, }) ); animatedY.value = withTiming(layout.y, { duration: duration, easing: Easing.ease, }); animatedWidth.value = withTiming(layout.width, { duration: duration, easing: Easing.ease, }); animatedHeight.value = withTiming(layout.height, { duration: duration, easing: Easing.ease, }); if (!hasLayout) { setHasLayout(true); } } } }, [ selectedKey, triggerLayouts, hasLayout, animatedX, animatedY, animatedWidth, animatedHeight, ]); const animatedStyle = useAnimatedStyle(() => { 'worklet'; const scrollOffsetValue = animatedScrollOffset ? animatedScrollOffset.value : scrollOffsetShared.value; const xPos = orientation === 'horizontal' ? animatedX.value - scrollOffsetValue : animatedX.value; // Web: react-native-reanimated does not reliably flush the transform // array to a CSS transform string, so use left/top instead. // Native: keep transform for GPU-accelerated UI-thread animation. if (isWeb) { return { left: xPos, top: animatedY.value, width: animatedWidth.value, height: animatedHeight.value, }; } return { transform: [ { translateX: xPos } as { translateX: number }, { translateY: animatedY.value } as { translateY: number }, ], width: animatedWidth.value, height: animatedHeight.value, }; }, [orientation, animatedX, animatedY, animatedWidth, animatedHeight]); // Don't render indicator until we have valid layout data if (!hasLayout) { return null; } return ( ); } ); TabsAnimatedIndicator.displayName = 'TabsAnimatedIndicator'; ``` ```jsx export const tabsAnimationConfig = { indicatorDuration: 200, }; ``` ### 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 ``` ```tsx export default () => ( Tab 1 Tab 2 Content for Tab 1 Content for Tab 2 ); ``` ### Component Props This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration. #### Tabs Contains all the tabs component parts. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | string | - | The controlled value of the tab to activate. Use with onValueChange. | | `defaultValue` | string | - | The value of the tab that should be active when initially rendered. Use when you do not need to control the state. | | `onValueChange` | (value: string) => void | - | Event handler called when the value changes. | | `orientation` | 'horizontal' | 'vertical' | 'horizontal' | The orientation of the tabs. | | `activationMode` | 'automatic' | 'manual' | 'automatic' | Whether tabs activate automatically on focus or manually with Enter/Space. | | `disabled` | boolean | false | When true, prevents the user from interacting with the tabs. | | `variant` | 'underlined' | 'filled' | 'enclosed' | 'underlined' | The visual style variant of the tabs. | | `size` | 'sm' | 'md' | 'lg' | 'md' | The size of the tabs. | #### TabsList Contains the triggers that are aligned along the edge of the active content. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `scrollable` | boolean | false | When true, enables horizontal scrolling for overflow tabs. | | `snapToCenter` | boolean | true | When scrollable is true, snaps the selected tab to center. | #### TabsTrigger The button that activates its associated content. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | string | - | A unique value that associates the trigger with a content. | | `disabled` | boolean | false | When true, prevents the user from interacting with the tab. | #### TabsContent Contains the content associated with each trigger. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `value` | string | - | A unique value that associates the content with a trigger. | | `forceMount` | boolean | false | Used to force mounting when more control is needed. Useful for animation. | #### TabsTriggerText The text displayed within a tab trigger. It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component. #### TabsTriggerIcon The icon displayed within a tab trigger. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `as` | React.ComponentType | - | The icon component to render. | #### TabsIndicator The animated indicator that shows which tab is currently selected. Place this component inside TabsList to control its position. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. ### Accessibility Adheres to the [Tabs WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/). #### Keyboard Interactions | Key | Description | | --- | --- | | `Tab` | When focus moves onto the tabs, focuses the active trigger. When a trigger is focused, moves focus to the active content. | | `ArrowDown` | Moves focus to the next trigger in vertical orientation and activates its associated content. | | `ArrowUp` | Moves focus to the previous trigger in vertical orientation and activates its associated content. | | `ArrowLeft` | Moves focus to the previous trigger in horizontal orientation and activates its associated content. | | `ArrowRight` | Moves focus to the next trigger in horizontal orientation and activates its associated content. | | `Home` | Moves focus to the first trigger and activates its associated content. | | `End` | Moves focus to the last trigger and activates its associated content. | ## Examples ### With Indicator #### With Indicator The TabsIndicator animates between the selected tab trigger. It is positioned absolutely inside the TabsList and slides to follow the active tab. ```jsx function Example() { return ( Account Password Team Account Settings Manage your account settings and set email preferences. Password Settings Change your password and update security settings. Team Settings Manage your team members and their access levels. ); }` ``` ### Vertical Orientation #### Vertical Set orientation to vertical to render the tab list along the left side. The indicator animates vertically between the selected triggers. ```jsx function Example() { return ( Overview Analytics Reports Notifications Overview View a summary of your account activity and statistics. Analytics Detailed analytics and insights about your performance. Reports Generate and download custom reports. Notifications Manage your notification preferences. ); }` ``` ### With Icons #### With Icons Use TabsTriggerIcon with the as prop to add icons alongside the trigger text in each tab. ```jsx function Example() { return ( Explore Messages Favourites Alerts Explore Discover new content and trending topics. Messages Your recent conversations and direct messages. Favourites Items and pages you have starred for quick access. Alerts Stay up to date with your latest notifications. ); }` ``` ### Variants #### Variants Tabs supports two variants: underlined shows a sliding underline indicator beneath the active tab, and filled shows a filled background pill that slides between tabs. ```jsx function Example() { return ( Underlined Tab 1 Tab 2 Tab 3 Underlined tab content 1 Underlined tab content 2 Underlined tab content 3 Filled Tab 1 Tab 2 Tab 3 Filled tab content 1 Filled tab content 2 Filled tab content 3 ); }` ``` ### Scrollable #### Scrollable When there are more tabs than the available width, the TabsList scrolls horizontally. The animated indicator stays synchronized with the scroll position. ```jsx function Example() { return ( Monday Tuesday Wednesday Thursday Friday Saturday Sunday Monday Start the week strong with your planned tasks. Tuesday Keep the momentum going with focused work. Wednesday Midweek checkpoint — review your progress. Thursday Push through with energy to finish the week. Friday Wrap up the week and celebrate your wins. Saturday Rest, recharge, and enjoy your weekend. Sunday Prepare and plan for the upcoming week ahead. ); }` ``` ### Controlled #### Controlled A controlled Tabs component manages its active tab via the value and onValueChange props. This allows external UI to programmatically switch between tabs. ```jsx function Example() { const [activeTab, setActiveTab] = React.useState('tab1'); return ( Tab 1 Tab 2 Tab 3 Content for Tab 1 Active tab: {activeTab} Content for Tab 2 Active tab: {activeTab} Content for Tab 3 Active tab: {activeTab} ); }` ``` --- ## Text URL: /ui/docs/components/text/index # Text Enhance your app with gluestack-ui's Text component—an adaptable React Native text area with multiple styles, sizes, and formatting options for seamless UI design. This is an illustration of **Text** component. ```jsx function Example() { return Hello World! }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add text ``` **Manual:** ### Step 1: Copy and paste the following code into index.tsx in your project. ```jsx type ITextProps = React.ComponentProps & VariantProps; const Text = React.forwardRef, ITextProps>( function Text( { className, isTruncated, bold, underline, strikeThrough, size = 'md', sub, italic, highlight, ...props }, ref ) { return ( ); } ); Text.displayName = 'Text'; export { Text }; ``` > 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 ITextProps = React.ComponentProps<'span'> & VariantProps; const Text = React.forwardRef, ITextProps>( function Text( { className, isTruncated, bold, underline, strikeThrough, size = 'md', sub, italic, highlight, ...props }: { className?: string } & ITextProps, ref ) { return ( ); } ); Text.displayName = 'Text'; export { Text }; ``` ### Step 3: Copy and paste the following code into styles.tsx in your project. ```jsx const baseStyle = isWeb ? 'font-sans tracking-sm my-0 bg-transparent border-0 box-border display-inline list-none margin-0 padding-0 position-relative text-start no-underline whitespace-pre-wrap word-wrap-break-word' : ''; export const textStyle = tva({ base: `text-foreground font-body ${baseStyle}`, variants: { isTruncated: { true: 'web:truncate', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, size: { '2xs': 'text-2xs', 'xs': 'text-xs', 'sm': 'text-sm', 'md': 'text-base', 'lg': 'text-lg', 'xl': 'text-xl', '2xl': 'text-2xl', '3xl': 'text-3xl', '4xl': 'text-4xl', '5xl': 'text-5xl', '6xl': 'text-6xl', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, }, }); ``` ### 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. #### Text

{`Renders a on web and a Text on native.`}

| Platform | Output | | --- | --- | | `{`Web`}` | {``} | | `Native` | {``} | ### Props Text component is created using `Text` component from react-native on native and `span` html element on web. It accepts the following props: #### Text | Name | Value | Default | | --- | --- | --- | | `isTruncated` | true | false | | `bold` | true | false | | `underline` | true | false | | `strikeThrough` | true | false | | `sub` | true | false | | `italic` | true | false | | `highlight` | true | false | | `size` | 2xs | xs | sm | md | lg | xl | 2xl | 3xl | 4xl | 5xl | 6xl | 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. #### Text Sizes Text component comes in different sizes, such as `xs`, `sm`, `md`, `lg`, `xl`, `2xl`, `3xl`, `4xl`, `5xl`, and `6xl` allowing users to adjust the button size based on their design needs. ```jsx function App() { const sizes = [ 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', ]; return (
{sizes.map((size, index) => ( {size} ))}
); }` ``` Text component also accepts some shorthands for basic quick styling. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `bold` | boolean | false | Used to make the text bold. | | `isTruncated` | boolean | false | If true, it will render an ellipsis when the text exceeds the width of the viewport or maxWidth set. | | `italic` | boolean | false | Used to make the text italic. | | `underline` | boolean | false | Used underline the text. | | `strikeThrough` | boolean | false | A horizontal line through the center of the text. | | `highlight` | boolean | false | Used to highlight the text with a yellow background. | --- ## TextArea URL: /ui/docs/components/textarea/index # TextArea Easily integrate a React & React Native Textarea component with multi-line input. Customize size, state, and accessibility for seamless UI. This is an illustration of **TextArea** component. ```jsx function Example() { return ( ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add textarea ``` **Manual:** ### Step 1: Copy and paste the following code into your project. ```jsx 'use client'; const SCOPE = 'TEXTAREA'; const UITextarea = createTextarea({ Root: withStyleContext(View, SCOPE), Input: TextInput, }); const textareaStyle = tva({ base: 'w-full h-[100px] border border-border dark:bg-input/30 rounded data-[hover=true]:border-border/80 data-[focus=true]:border-primary/80 data-[focus=true]:data-[hover=true]:border-primary/80 data-[disabled=true]:opacity-40 data-[disabled=true]:bg-background/90 data-[disabled=true]:data-[hover=true]:border-border/80', variants: { variant: { default: 'data-[focus=true]:border-primary/80 data-[focus=true]:web:ring-1 data-[focus=true]:web:ring-inset data-[focus=true]:web:ring-indicator-primary data-[invalid=true]:border-destructive data-[invalid=true]:web:ring-1 data-[invalid=true]:web:ring-inset data-[invalid=true]:web:ring-indicator-error data-[invalid=true]:data-[hover=true]:border-destructive data-[invalid=true]:data-[focus=true]:data-[hover=true]:border-primary/80 data-[invalid=true]:data-[focus=true]:data-[hover=true]:web:ring-1 data-[invalid=true]:data-[focus=true]:data-[hover=true]:web:ring-inset data-[invalid=true]:data-[focus=true]:data-[hover=true]:web:ring-indicator-primary data-[invalid=true]:data-[disabled=true]:data-[hover=true]:border-destructive data-[invalid=true]:data-[disabled=true]:data-[hover=true]:web:ring-1 data-[invalid=true]:data-[disabled=true]:data-[hover=true]:web:ring-inset data-[invalid=true]:data-[disabled=true]:data-[hover=true]:web:ring-indicator-error ', }, size: { sm: '', md: '', lg: '', xl: '', }, }, }); const textareaInputStyle = tva({ base: 'p-2 web:outline-0 web:outline-none flex-1 text-foreground placeholder:text-foreground/60 web:cursor-text web:data-[disabled=true]:cursor-not-allowed', parentVariants: { size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg', xl: 'text-xl', }, }, }); type ITextareaProps = React.ComponentProps & VariantProps; const Textarea = React.forwardRef< React.ComponentRef, ITextareaProps >(function Textarea( { className, variant = 'default', size = 'md', ...props }, ref ) { return ( ); }); type ITextareaInputProps = React.ComponentProps & VariantProps; const TextareaInput = React.forwardRef< React.ComponentRef, ITextareaInputProps >(function TextareaInput({ className, ...props }, ref) { const { size: parentSize } = useStyleContext(SCOPE); return ( ); }); Textarea.displayName = 'Textarea'; TextareaInput.displayName = 'TextareaInput'; export { Textarea, TextareaInput }; ``` ### 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. #### Textarea It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `size` | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Changes the size of the Input Text | | `isInvalid` | bool | false | When true, the input displays an error state. | | `isDisabled` | bool | false | When true, the input is disabled and cannot be edited. | | `isHovered` | bool | false | When true, the input displays a hover state. | | `isFocused` | bool | false | When true, the input displays a focus state. | | `isRequired` | bool | false | If true, sets aria-required="true" on the input. | | `isReadOnly` | bool | false | If true, the input value cannot be edited. | **Descendants Styling Props** Props to style child components. | Sx Prop | Description | | --- | --- | | `{`_input`}` | Prop to style TextareaInput Component | #### TextareaInput Contains all TextInput related layout style props and actions. It inherits all the properties of React Native's [TextInput](https://reactnative.dev/docs/textInput) component. ### Accessibility We have outlined the various features that ensure the Textarea component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards. #### Keyboard - setting the aria-label and aria-hint #### Screen Reader - VoiceOver: accessible and aria-label props to describe the input's purpose - `aria-traits` and `aria-hint` for the various states of the input, such as "double tap to edit" ### Props Textarea component is created using TextInput component from react-native. It extends all the props supported by [React Native Text Input](https://reactnative.dev/docs/textinput#props) and the props mentioned below. #### Textarea | Name | Value | Default | | --- | --- | --- | | `size` | xl | lg | md | sm | md | ### 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` | | `focus` | data-focus | `true | false` | | `disabled` | data-disabled | `true | false` | | `invalid` | data-invalid | `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. #### FormControl The Textarea Component can be incorporated within the FormControl. ```jsx function Example() { return ( Write with me Start your story ); }` ``` --- ## Toast URL: /ui/docs/components/toast/index # Toast gluestack-ui Toast component for React Native lets you show toast messages effortlessly. Improve your Toast component with flexible placement, duration, and actions. This is an illustration of **Toast** component. ```jsx function Example() { const toast = useToast() const [toastId, setToastId] = React.useState(0) const handleToast = () => { if (!toast.isActive(toastId)) { showNewToast() } } const showNewToast = () => { const newId = Math.random() setToastId(newId) toast.show({ id: newId, placement: "top", duration: 3000, render: ({ id }) => { const uniqueToastId = "toast-" + id return ( Hello! This is a customized toast message. ) }, }) } return ( ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add toast ``` **Manual:** > Note: At present, we have integrated `react-native-reanimated` for animation. You have the option to remove this and implement your own custom animation wrapper. ### Step 1: Install the following dependencies: ```bash npm i react-native-reanimated ``` ### Step 2: Copy and paste the following code into your project. ```jsx 'use client'; const useToast = createToastHook(View); const SCOPE = 'TOAST'; const AnimatedView = Animated.createAnimatedComponent(View); const StyledAnimatedView = styled(AnimatedView, { className: 'style' }); const toastStyle = tva({ base: 'p-4 m-1 rounded-md gap-1 web:pointer-events-auto border-border', variants: { action: { error: 'bg-popover text-popover-foreground', warning: 'bg-popover text-popover-foreground', success: 'bg-popover text-popover-foreground', info: 'bg-popover text-popover-foreground', muted: 'bg-popover text-popover-foreground', }, variant: { solid: 'border border-border bg-popover shadow-soft-4', outline: 'border border-border bg-popover', }, }, }); const toastTitleStyle = tva({ base: 'font-medium font-body tracking-md text-left', variants: { isTruncated: { true: '', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, size: { '2xs': 'text-2xs', 'xs': 'text-xs', 'sm': 'text-sm', 'md': 'text-base', 'lg': 'text-lg', 'xl': 'text-xl', '2xl': 'text-2xl', '3xl': 'text-3xl', '4xl': 'text-4xl', '5xl': 'text-5xl', '6xl': 'text-6xl', }, }, parentVariants: { variant: { solid: '', outline: 'text-foreground', }, action: { error: '', warning: '', success: '', info: '', muted: '', }, }, parentCompoundVariants: [ { variant: 'solid', action: 'error', class: 'text-destructive-foreground', }, { variant: 'solid', action: 'warning', class: 'text-accent-foreground', }, { variant: 'solid', action: 'success', class: 'text-secondary-foreground', }, { variant: 'solid', action: 'info', class: 'text-popover-foreground', }, { variant: 'solid', action: 'muted', class: 'text-muted-foreground', }, { variant: 'outline', action: 'error', class: 'text-destructive', }, { variant: 'outline', action: 'warning', class: 'text-accent-foreground', }, { variant: 'outline', action: 'success', class: 'text-secondary-foreground', }, { variant: 'outline', action: 'info', class: 'text-popover-foreground', }, { variant: 'outline', action: 'muted', class: 'text-muted-foreground', }, ], }); const toastDescriptionStyle = tva({ base: 'font-normal font-body tracking-md text-left', variants: { isTruncated: { true: '', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, size: { '2xs': 'text-2xs', 'xs': 'text-xs', 'sm': 'text-sm', 'md': 'text-base', 'lg': 'text-lg', 'xl': 'text-xl', '2xl': 'text-2xl', '3xl': 'text-3xl', '4xl': 'text-4xl', '5xl': 'text-5xl', '6xl': 'text-6xl', }, }, parentVariants: { variant: { solid: 'text-muted-foreground', outline: 'text-muted-foreground', }, }, }); const Root = withStyleContext(StyledAnimatedView, SCOPE); type IToastProps = React.ComponentProps & { className?: string; } & VariantProps; const Toast = React.forwardRef, IToastProps>( function Toast( { className, variant = 'solid', action = 'muted', ...props }, ref ) { return ( ); } ); type IToastTitleProps = React.ComponentProps & { className?: string; } & VariantProps; const ToastTitle = React.forwardRef< React.ComponentRef, IToastTitleProps >(function ToastTitle({ className, size = 'md', children, ...props }, ref) { const { variant: parentVariant, action: parentAction } = useStyleContext(SCOPE); React.useEffect(() => { // Issue from react-native side // Hack for now, will fix this later AccessibilityInfo.announceForAccessibility(children as string); }, [children]); return ( {children} ); }); type IToastDescriptionProps = React.ComponentProps & { className?: string; } & VariantProps; const ToastDescription = React.forwardRef< React.ComponentRef, IToastDescriptionProps >(function ToastDescription({ className, size = 'md', ...props }, ref) { const { variant: parentVariant } = useStyleContext(SCOPE); return ( ); }); Toast.displayName = 'Toast'; ToastTitle.displayName = 'ToastTitle'; ToastDescription.displayName = 'ToastDescription'; export { Toast, ToastDescription, ToastTitle, useToast }; ``` ### 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. #### Toast It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `duration` | number or null | 5000 | The delay before the toast hides (in milliseconds). If set to null, toast will never dismiss. | | `onCloseComplete` | ()=>{} | - | Callback function to run side effects after the toast has closed. | | `placement` | 'top'| 'top right' | 'top left' | 'bottom' | 'bottom left' | 'bottom right' | bottom | Position of toast on the web page. | | `render?: (props: any)` | ReactNode | - | Renders a toast component | | `avoidKeyboard` | bool | false | If true and the keyboard is opened, the Toast will move up equivalent to the keyboard height. | | `containerStyle` | ViewStyle | - | Container style object for the toast. | #### ToastTitle 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. #### ToastDescription 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. ### Accessibility We have outlined the various features that ensure the Toast 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/alert/). #### Keyboard - `Tab + Enter`: Triggers the toast's action. #### Screen Reader - VoiceOver: When the toast is focused, the screen reader will announce the toast's title. ### Props Toast 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. #### Toast <> | Name | Value | Default | | --- | --- | --- | | `action` | `error | warning | success | info | muted` | `muted` | | `variant` | `solid | outline` | `solid` | ### 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. #### Toast in note talking platform ```jsx function Example() { const toast = useToast(); const [toastId, setToastId] = React.useState(0); const handleToast = () => { if (!toast.isActive(toastId)) { showNewToast(); } }; const showNewToast = () => { const newId = Math.random(); setToastId(newId); toast.show({ id: newId, placement: 'top', duration: 3000, render: ({ id }) => { const uniqueToastId = "toast-" + id; return ( Error! Something went wrong. toast.close(id)}> ); }, }); }; return ( ); }` ``` #### Social media notification ```jsx function Example() { const toast = useToast(); return ( ); }` ``` #### Software update toast ```jsx function Example() { const toast = useToast(); const [toastId, setToastId] = React.useState(0); const handleToast = () => { if (!toast.isActive(toastId)) { showNewToast(); } }; const showNewToast = () => { const newId = Math.random(); setToastId(newId); toast.show({ id: newId, placement: 'top', duration: 3000, render: ({ id }) => { const uniqueToastId = "toast-" + id; return ( Update available toast.close(id)}> A new software version is available for download. ); }, }); }; return ( ); }` ``` #### Message sent toast ```jsx function Example() { const toast = useToast(); return ( ); }` ``` --- ## Tooltip URL: /ui/docs/components/tooltip/index # Tooltip Create an intuitive UI using the gluestack-ui Tooltip component in React & React Native. Add hints & tooltips seamlessly. This is an illustration of **Tooltip** component. ```jsx function Example() { return ( { return ( ) }} > Tooltip ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add tooltip ``` **Manual:** > Note: At present, we have integrated the `@legendapp/motion` for animation. You have the option to remove this and implement your own custom animation wrapper. ### Step 1: Install the following dependencies: ```bash npm i @legendapp/motion ``` ### Step 2: Copy and paste the following code into your project. ```jsx 'use client'; type IMotionViewProps = React.ComponentProps & MotionComponentProps; const MotionView = Motion.View as React.ComponentType; const StyledMotionView = styled(MotionView, { className: 'style' }); export const UITooltip = createTooltip({ Root: withStyleContext(StyledMotionView), Content: MotionView, Text: Text, AnimatePresence: AnimatePresence, }); const tooltipStyle = tva({ base: 'w-full h-full web:pointer-events-none', }); const tooltipContentStyle = tva({ base: 'py-1 px-3 rounded-sm bg-background/90 web:pointer-events-auto', }); const tooltipTextStyle = tva({ base: 'font-normal tracking-normal web:select-none text-xs text-foreground/90', variants: { isTruncated: { true: 'line-clamp-1 truncate', }, bold: { true: 'font-bold', }, underline: { true: 'underline', }, strikeThrough: { true: 'line-through', }, size: { '2xs': 'text-2xs', 'xs': 'text-xs', 'sm': 'text-sm', 'md': 'text-base', 'lg': 'text-lg', 'xl': 'text-xl', '2xl': 'text-2xl', '3xl': 'text-3xl', '4xl': 'text-4xl', '5xl': 'text-5xl', '6xl': 'text-6xl', }, sub: { true: 'text-xs', }, italic: { true: 'italic', }, highlight: { true: 'bg-yellow-500', }, }, }); type ITooltipProps = React.ComponentProps & VariantProps & { className?: string }; type ITooltipContentProps = React.ComponentProps & VariantProps & { className?: string }; type ITooltipTextProps = React.ComponentProps & VariantProps & { className?: string }; const Tooltip = React.forwardRef< React.ComponentRef, ITooltipProps >(function Tooltip({ className, ...props }, ref) { return ( ); }); const TooltipContent = React.forwardRef< React.ComponentRef, ITooltipContentProps & { className?: string } >(function TooltipContent({ className, ...props }, ref) { return ( ); }); const TooltipText = React.forwardRef< React.ComponentRef, ITooltipTextProps & { className?: string } >(function TooltipText({ size, className, ...props }, ref) { return ( ); }); Tooltip.displayName = 'Tooltip'; TooltipContent.displayName = 'TooltipContent'; TooltipText.displayName = 'TooltipText'; export { Tooltip, TooltipContent, TooltipText }; ``` ### 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. #### Tooltip It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. | Prop | Type | Default | Description | | --- | --- | --- | --- | | `isOpen` | boolean | false | Whether the tooltip is opened. Useful for controlling the open state. | | `isDisabled` | boolean | false | Whether the tooltip is disabled. | | `defaultIsOpen` | boolean | false | If true, the popover will be opened by default. | | `onOpen` | () => void | true | This function will be invoked when the tooltip is opened. | | `onClose` | () => void | - | This function will be invoked when tooltip is closed. It will also be called when the user attempts to close the tooltip via Escape key or backdrop press. | | `openDelay` | number | 0 | Duration in ms to wait till displaying the tooltip. | | `closeDelay` | number | 0 | Duration in ms to wait till hiding the tooltip. | | `placement` | "bottom" | "top" | "right" | "left" | "top left" | "top right" | "bottom left" | "bottom right" | "right top" | "right bottom" | "left top" | "left bottom" | bottom left | Tooltip placement | | `children` | any | - | The content to display inside the tooltip. | | `closeOnClick` | boolean | true | Whether tooltip should be closed on Trigger click. | | `trigger` | () => any | - | Function that returns a React Element. This element will be used as a Trigger for the tooltip. | | `offset` | number | 10 | Distance between the trigger and the tooltip. | | `crossOffset` | number | - | The additional offset applied along the cross axis between the element and its trigger element. | | `shouldOverlapWithTrigger` | boolean | false | Determines whether tooltip content should overlap with the trigger. | | `shouldFlip` | boolean | true | Whether the element should flip its orientation (e.g. top to bottom or left to right) when there is insufficient room for it to render completely. | | `closeOnOverlayClick` | boolean | true | Closes tooltip when clicked outside. | #### TooltipText Contains all text related layout style props and actions. It inherits all the properties of React Native's Text component. #### TooltipContent Contains all backdrop related layout style props and actions. It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component. ### Accessibility We have outlined the various features that ensure the Tooltip component is accessible to all users, including those with disabilities. These features help ensure that your application is inclusive and meets accessibility standards. It adheres to the [ WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/). ### Examples #### Tooltip with Heading A tooltip component with an avatar is a user interface element that displays a small pop-up box of additional information when the user hovers over or interacts with an avatar or an icon. ```jsx function App(){ return ( { return ( + 3 ) }} > View all members of this channel
Includes John, Sarah, Mike, Emily and David
Sandeep Srivastva Arjun Kapoor Ritik Sharma
); }` ``` #### Tooltip with Icon A tooltip component with an icon is a user interface element that provides contextual information or explanatory text when the user hovers over or interacts with an icon. ```jsx function App(){ return ( { return ( ) }} > New message N ); }` ``` #### Inline Text Tooltip anchored to inline text elements using long press interaction ```jsx function Example() { return ( This is an example of inline text with a tooltip. You can get more information by{' '} { return ( long pressing ); }} > This tooltip anchors to the exact touch point on the text {' '} on the highlighted word to see the tooltip appear at the correct position. ); }` ``` --- ## VStack URL: /ui/docs/components/vstack/index # VStack Use the gluestack-ui VStack component to arrange elements vertically with customizable spacing. Simplify layout design with VStack React Native for seamless UIs. This is an illustration of **VStack** component. ```jsx function Example() { return ( ) }` ``` ## Installation **CLI:** ### Run the following command: ```bash npx gluestack-ui add vstack ``` **Manual:** ### Step 1: Copy and paste the following code into index.tsx in your project. ```jsx type IVStackProps = React.ComponentProps & VariantProps; const VStack = React.forwardRef, IVStackProps>( function VStack({ className, space, reversed, ...props }, ref) { return ( ); } ); VStack.displayName = 'VStack'; export { VStack }; ``` > 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 IVStackProps = React.ComponentProps<'div'> & VariantProps; const VStack = React.forwardRef, IVStackProps>( function VStack({ className, space, reversed, ...props }, ref) { return (
); } ); VStack.displayName = 'VStack'; export { VStack }; ``` ### Step 3: Copy and paste the following code into styles.tsx in your project. ```jsx 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 vstackStyle = tva({ base: `flex-col ${baseStyle}`, 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', }, reversed: { true: 'flex-col-reverse', }, }, }); ``` ### 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. #### VStack

{`Renders a

on web and a View on native.`}

| Platform | Output | | --- | --- | | `{`Web`}` | {`
`} | | `Native` | {``} | ### Accessibility The accessibility of a VStack is primarily determined by the accessibility information of the components it contains. When you pass an accessible component inside a VStack, its accessibility attributes, such as labels and hints, will be utilized by assistive technologies like screen readers. In essence, the VStack acts as a container that inherits and propagates the accessibility attributes of its child views. ### Props #### VStack | Prop | Type | Default | Description | | --- | --- | --- | --- | | `space` | string | - | It sets the space between children. By default there is no space between the VStack items. | | `reversed` | boolean | false | When true, it places the VStack items in reverse direction. | ### 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. #### VStack Reversed A VStack component with the reversed prop reverses the order of vertically stacked elements, enabling customized vertical layouts and visual arrangements of content within a user interface. ```jsx function Example() { return ( ); }` ``` --- ## Contributing to `gluestack-ui` URL: /ui/docs/guides/more/contribution-guide/index # Contributing to `gluestack-ui` Thank you for your interest in contributing to `gluestack-ui`! 🚀 Your efforts help us build a more robust and versatile library for the community. This comprehensive guide will walk you through everything you need to know about contributing to gluestack-ui, from initial setup to advanced development workflows. We're thrilled to have you on this journey with us. Together, we can accelerate universal app development! 🎉 ## Table of Contents 1. [Getting Started](#getting-started) 2. [Project Architecture](#project-architecture) 3. [Generated Files System](#generated-files-system) 4. [Development Workflow](#development-workflow) 5. [Local Package Development](#local-package-development) 6. [Creating Your First Component](#creating-your-first-component) 7. [Documentation Guidelines](#documentation-guidelines) 8. [Testing Your Changes](#testing-your-changes) 9. [Contribution Guidelines](#contribution-guidelines) 10. [Troubleshooting](#troubleshooting) ## Getting Started ### Initial Setup 1. **Fork and Clone** ```bash # Fork gluestack-ui from GitHub first, then: git clone git@github.com:${YOUR_USERNAME}/gluestack-ui.git cd gluestack-ui ``` 2. **Add Remote** ```bash git remote add origin git@github.com:/gluestack-ui.git ``` 3. **Install Dependencies** ```bash yarn ``` 4. **Initial Sync** (Copy files from `src/` to `apps/`) ```bash yarn sync ``` 5. **Start Development Watcher** ```bash yarn dev ``` ### Prerequisites Before contributing, ensure you understand: - How the source-to-destination system works: - **Source files** are maintained in the `src/` directory (single source of truth) - **Mapper scripts** automatically copy and sync files to destination projects in `apps/` - **Generated files** in destination projects are gitignored to prevent accidental commits - All UI components in `src/components/ui/` are automatically synced to all apps - Documentation components and sidebar config are synced only to the website app - Use `yarn dev` to watch for changes and auto-sync, or `yarn sync` for one-time sync - **Never edit generated files** in `apps/*/components/ui/` - always edit source files in `src/` - Project architecture and component organization - Development workflow and testing approach ## Project Architecture ### High-Level Structure ``` gluestack-ui/ ├── src/ # Source files (single source of truth) ├── packages/ # Published packages ├── apps/ # Example applications & documentation ├── scripts/ # Build & development scripts ``` ### Key Directories #### Source (`src/`) - **`components/ui/`** - Core UI components (Button, Input, Modal, etc.) - **`docs-components/`** - Documentation-specific components - **`sidebar.json`** - Documentation sidebar configuration #### Applications (`apps/`) - **`website/`** - Documentation website (gluestack-ui.com) - **`kitchen-sink/`** - Component showcase & testing - **`starter-kit-next/`** - Next.js starter template - **`starter-kit-expo/`** - Expo starter template #### Packages (`packages/`) - **`gluestack-ui/`** - Main CLI package for component installation - **`create-gluestack/`** - Project initialization tool - **`gluestack-utils/`** - Shared utility functions - **`gluestack-core/`** - Creator and Aria related code for components - **`ui-next-adapter/`** - Next.js adapter for Gluestack-ui component library with React Native Web support ## Generated Files System This repository uses a **source-to-destination** file generation system where: - **Source files** are maintained in the `src/` directory at the root level - **Mapper scripts** in `scripts/` automatically copy and sync files to destination projects in `apps/` - **Generated files** in destination projects are gitignored to prevent accidental commits ### File Structure ``` / ├── src/ # Source files │ ├── components/ui/ # UI components source │ ├── docs-components/ # Documentation components source │ └── sidebar.json # Sidebar configuration source | ├── packages/ | └── create-gluestack # CLI for gluestack's create command │ └── gluestack-ui # CLI for gluestack's add, init, upgrade command │ └── gluestack-utils # Utils related files │ └── gluestack-core # Components creator and aria │ └── ui-next-adapter # gluestack/ui-next-adapter for NextJs | ├── scripts/ # Mapper scripts │ └── mappers/ # Individual app mappers | └── apps/ # Destination projects ├── website/ # Generated files are gitignored ├── starter-kit-next/ ├── starter-kit-expo/ └── kitchen-sink/ # Generated files are gitignored ``` ### Generated Files by Destination #### All Apps The following files are generated in **all** destination apps: - `components/ui/` - UI components copied from `src/components/ui/` #### Website App Only Additional files generated only for the website app: - `components/docs-components/` - Documentation components copied from `src/docs-components/` - `sidebar.json` - Sidebar configuration copied from `src/sidebar.json` - `app/ui/docs` - Docs or examples copied from `src/docs` or `src/components/ui/${component_name}/docs` ### Why This System? #### ✅ Benefits - **Single source of truth** - All components maintained in one place - **Consistency** - Same components across all apps - **No duplication** - Avoid copy-paste errors - **Easy updates** - Change once, update everywhere - **Clean git history** - Only source changes are committed #### ⚠️ Important Notes - **Never edit generated files directly** - Changes will be overwritten - **Always edit source files** in `src/` directory - **Generated files are local only** - Not committed to git - **Run mappers after pulling** to sync local generated files ## Development Workflow ### Making Changes #### For UI Components 1. **Edit source files** in `src/components/ui/` - Never edit in `apps/*/components/ui/` 2. **Test changes** in the `kitchen-sink` app or `website` 3. **Verify** across different applications 4. **Commit only source files** - Generated files are automatically ignored #### For Documentation 1. **Edit content** in appropriate `src/docs/` directories 2. **Update sidebar** in `src/sidebar.json` if adding any new component 3. **Test** on the website app 4. **Review** documentation flow and navigation ### Mapper Scripts The mapper system uses: - `scripts/dev.ts` - Main script that watches for file changes - `scripts/mappers/` - Individual mappers for each destination app - File watching and debouncing to handle rapid changes - Event-based processing (add, change, delete) ### Development Commands ```bash # Watch all mappers and sync changes yarn dev # Run specific mapper only yarn dev:website # One-time sync without watching yarn sync # Run specific mapper with sync yarn sync:starter-kit-next # Build local package using yalc and create its link and watch for file changes yarn link:create # Link local packages in apps yarn link:apps # Initialize a new component for contribution yarn create:component # Cleanup of local packages in apps yarn unlink:apps ``` ### Gitignore Configuration Each destination app has been configured to ignore generated files: #### Website App (`apps/website/.gitignore`) ```gitignore # Generated files from src/ - DO NOT COMMIT # These files are automatically generated by the mapper scripts components/ui/ components/docs-components/ sidebar.json ``` #### Other Apps (`apps/*/\.gitignore`) ```gitignore # Generated files from src/ - DO NOT COMMIT # These files are automatically generated by the mapper scripts components/ui/ ``` ## Local Package Development When contributing to gluestack-ui packages (`gluestack-utils`, `gluestack-core`, etc.), you'll need to set up local package linking to test your changes in the apps before publishing. ### Package Linking System This project uses [yalc](https://github.com/wclr/yalc) for local package development, which provides a better alternative to `npm link` with more reliable dependency resolution. ### Setting Up Local Package Development #### Prerequisites Ensure yalc is installed globally: ```bash npm install -g yalc ``` #### Step 1: Link Packages for Development To start developing packages locally: ```bash # Link all packages and set up watch mode yarn link:create # This command does the following: # 1. Links gluestack-utils package (builds, publishes to yalc, starts watch mode) # 2. Links gluestack-core package (builds, publishes to yalc, starts watch mode) ``` #### Step 2: Link Packages to Apps Link the locally published packages to your apps: ```bash # Link packages to all apps (website + kitchen-sink) yarn link:apps # Or link to specific apps: yarn link:apps-website # Link to website app only yarn link:apps-kitchen-sink # Link to kitchen-sink app only ``` #### Step 3: Start Development Now you can start your normal development workflow: ```bash # In one terminal - watch for file changes and mapping yarn dev # In another terminal - start your app of choice cd apps/website && yarn dev # or cd apps/kitchen-sink && yarn dev ``` ### Local Package Development Workflow #### Making Changes to Packages 1. **Edit package source files** in `packages/gluestack-utils/` or `packages/gluestack-core/` 2. **Changes are automatically rebuilt** and republished to yalc (watch mode) 3. **Apps automatically pick up changes** through yalc linking 4. **Test your changes** in the linked apps #### Package Structure ``` packages/ ├── gluestack-utils/ # Utility functions, hooks, and common code │ ├── src/ │ │ ├── hooks/ # Reusable hooks │ │ ├── aria/ # Accessibility utilities │ │ ├── common/ # Common utility functions │ │ └── nativewind-utils/ # NativeWind specific utilities │ └── package.json │ ├── gluestack-core/ # Core component factories and creators │ ├── src/ │ │ ├── accordion/ # Component-specific core logic │ │ ├── button/ │ │ └── ... │ └── package.json │ ├── gluestack-ui/ # CLI package for component installation └── ui-next-adapter/ # Next.js adapter for React Native Web ``` ### Working with Individual Packages #### For gluestack-utils When adding new utilities, hooks, or common functionality: ```bash # Navigate to the package cd packages/gluestack-utils # Install dependencies yarn # Start development mode (if not already linked) yarn dev # Make your changes in src/ # Changes will be automatically rebuilt and published to yalc ``` #### For gluestack-core When adding new component creators or core functionality: ```bash # Navigate to the package cd packages/gluestack-core # Install dependencies yarn # Start development mode (if not already linked) yarn dev # Make your changes in src/ # Changes will be automatically rebuilt and published to yalc ``` ### Common Package Development Tasks #### Adding a New Utility Function 1. **Create your utility** in `packages/gluestack-utils/src/` 2. **Export it** from the appropriate index file 3. **Test it** in a linked app 4. **Add tests** if applicable #### Adding a New Component Core/Creator 1. **Create component folder** in `packages/gluestack-core/src/` 2. **Implement the core functionality** 3. **Export from main index** 4. **Use in UI components** in `src/components/ui/` #### Testing Package Changes ```bash # Ensure packages are linked to apps yarn link:apps # Start apps to test changes cd apps/kitchen-sink && yarn dev cd apps/website && yarn dev ``` ### Cleaning Up After Development When you're done with local package development: ```bash # Unlink packages from apps yarn unlink:apps # This removes yalc links and cleans up node_modules # Apps will revert to using published package versions ``` ### Package Development Commands Reference ```bash # Setup commands yarn link:create # Link and watch all packages yarn link:create-utils # Link and watch gluestack-utils only yarn link:create-core # Link and watch gluestack-core only # App linking commands yarn link:apps # Link packages to all apps yarn link:apps-website # Link packages to website app yarn link:apps-kitchen-sink # Link packages to kitchen-sink app # Cleanup commands yarn unlink:apps # Unlink packages from all apps yarn unlink:apps-website # Unlink packages from website app yarn unlink:apps-kitchen-sink # Unlink packages from kitchen-sink app # Component development commands yarn create:component # Initialize a new component for contribution ``` ### Troubleshooting Package Development #### Changes Not Reflecting in Apps 1. **Check if packages are properly linked**: ```bash cd apps/website && yalc check ``` 2. **Verify package watch mode is running**: ```bash # Should see build output when you make changes ``` 3. **Re-link if needed**: ```bash yarn unlink:apps && yarn link:apps ``` #### Package Build Errors 1. **Check TypeScript compilation**: ```bash cd packages/gluestack-utils && yarn build ``` 2. **Verify dependencies are installed**: ```bash yarn ``` #### Yalc Issues 1. **Clear yalc cache**: ```bash yalc clean ``` 2. **Reset and re-link**: ```bash yarn unlink:apps yarn link:create yarn link:apps ``` ### Best Practices for Package Development - **Always test package changes** in multiple apps before submitting - **Keep package interfaces stable** - avoid breaking changes - **Use semantic versioning** for package releases - **Document new APIs** thoroughly - **Add tests** for new functionality - **Clean up after development** to avoid confusion ## Creating Your First Component > Make sure you have set up your gluestack-ui development environment and have `yarn dev` running at root to watch for file changes. ### Prerequisites for Component Creation 1. **Development Environment Setup** - Follow the setup instructions above - Verify all apps are properly synced with `yarn sync` - Ensure `yarn dev` is running to watch for file changes ### Setting Up Local Package Development Before creating your first component, you'll need to set up local package development to test your changes effectively: #### Step 1: Link Packages for Development ```bash # Link all packages and set up watch mode yarn link:create # This command: # - Builds gluestack-utils and gluestack-core packages # - Publishes them to yalc for local development # - Starts watch mode to automatically rebuild on changes ``` #### Step 2: Link Packages to Apps ```bash # Link packages to all apps (website + kitchen-sink) yarn link:apps # Or link to specific apps: yarn link:apps-website # Link to website app only yarn link:apps-kitchen-sink # Link to kitchen-sink app only ``` #### Step 3: Verify Setup After linking, you can verify the setup: ```bash # Check if packages are properly linked cd apps/website && yalc check cd apps/kitchen-sink && yalc check ``` ### Using the Component Creation Script The `yarn create:component` command helps you initialize a new component with the proper structure: ```bash # Initialize a new component for contribution yarn create:component # This will prompt you for: # - Component name (in kebab-case) # - Component type (basic, complex, etc.) # - Whether to include examples and documentation ``` #### What the Script Creates The `create:component` script automatically generates: 1. **Component folder structure** in `src/components/ui//` 2. **Basic component template** with proper TypeScript types 3. **Example files** in `examples/` directory 4. **Documentation template** in `docs/` directory 5. **Export statements** for the component ### Component Development Workflow #### Step 1: Create Your Component ```bash # Create a new component yarn create:component # Follow the prompts to set up your component ``` #### Step 2: Develop Your Component 1. **Edit your component** in `src/components/ui//` 2. **Create your component's creator & aria (if any)** in `packages/gluestack-core` 3. **Add examples** in the `examples/` directory 4. **Write documentation** in the `docs/` directory 5. **Update your component in sidebar.json file** in `src/sidebar.json` 6. **Test changes** in real-time as they sync to apps #### Step 3: Test Your Component ```bash # Start the kitchen-sink app to test your component cd apps/kitchen-sink && yarn dev # Or test in the website app for documentation cd apps/website && yarn dev ``` #### Step 4: Clean Up After Development When you're done developing: ```bash # Unlink packages from apps yarn unlink:apps # This removes yalc links and cleans up node_modules # Apps will revert to using published package versions ``` ### Troubleshooting Component Development #### Changes Not Reflecting If your component changes aren't showing up in the apps: ```bash # Check if packages are properly linked cd apps/website && yalc check # Re-link if needed yarn unlink:apps && yarn link:apps ``` #### Package Build Errors ```bash # Check if packages are building correctly cd packages/gluestack-utils && yarn build cd packages/gluestack-core && yarn build ``` #### Yalc Issues ```bash # Clear yalc cache if needed yalc clean # Reset and re-link yarn unlink:apps yarn link:create yarn link:apps ``` ### Step 1: Create the Core Component #### Component Structure Create your component in the source directory following this structure: ``` src/components/ui// ├── index.tsx # Main component file (copy pasteable) ├── examples/ # Examples of component ├── docs/ # Docs of the component ``` #### Create Component Steps 1. **Create component folder** inside `src/components/ui/` (use kebab-case). 2. **Create composable component file** inside `src/components/ui//index.tsx`. 3. **Export** all the composable components from this file. Make sure the components are exported as named-exports. 4. **Create creator functions** inside (`packages/gluestack-utils//creator/`): - All creator/factory functions for unstyled components should go here - Use this for creating base component factories that can be styled 5. **Create ARIA hooks** inside (`packages/gluestack-utils//aria/`): - All accessibility-related code and ARIA hooks should go here - Include keyboard navigation, screen reader support, and other a11y features 6. **Add component examples** inside (`src/components/ui//examples/`): - Create usage examples showing different variants and use cases 7. **Add component documentation** inside (`src/components/ui//docs/`): - Include comprehensive documentation for the component 8. **Export from main index** (`src/components/ui/index.tsx`): ```tsx // Add your component export export * from './'; ``` ### Step 2: Component Guidelines #### Code Standards - **Use TypeScript** for all component files - **Follow naming conventions**: PascalCase for components, kebab-case for directories - **Include accessibility features** where appropriate - **Write meaningful prop types** and documentation - **Support className prop** for styling flexibility #### Styling Guidelines - **Use NativeWind/Tailwind** classes via className prop - **Import from react-native directly** (View, Text, etc.) - **Don't import styled components** from NativeWind - **Support theming** through CSS variables when needed ### Step 3: Add Component Dependencies Update dependencies in the appropriate configuration files: **Starter-Kits** - Update starter-kit-\* templates if needed. **gluestack-ui package** - Update `packages/gluestack-ui/src/dependencies.ts` if needed. ## Updating Starter-Kits (Templates) for Shipment When your changes are ready to ship and you need to update the starter-kit templates, follow this comprehensive process to ensure all templates are properly updated and tested. ### Understanding Starter-Kit Templates Starter-kit templates are located in `packages/gluestack-ui/templates/` and are used by the CLI to initialize new projects. These templates include: - **Next.js templates** (`templates/nextjs/`) - For Next.js projects - **Expo templates** (`templates/expo/`) - For Expo/React Native projects - **React Native CLI templates** (`templates/react-native-cli/`) - For React Native CLI projects - **Common templates** (`templates/common/`) - Shared configuration files ### When to Update Starter-Kits Update starter-kit templates when: - ✅ Adding new components that require template-specific configuration - ✅ Updating dependencies that affect project initialization - ✅ Modifying configuration files (tailwind.config.js, babel.config.js, etc.) - ✅ Adding new project types or framework support - ✅ Updating provider components or essential dependencies - ✅ Changing initialization logic or file structure ### Step-by-Step Update Process #### Step 1: Identify Required Changes 1. **Review your component changes** and determine if they affect: - Dependencies in `packages/gluestack-ui/src/dependencies.ts` - Template configuration files - Provider components or initialization logic - Project-specific setup requirements 2. **Check template usage** in the initialization code: ```bash # Review how templates are used grep -r "templates" packages/gluestack-ui/src/ ``` #### Step 2: Update Dependencies Configuration If your component requires new dependencies: 1. **Update `packages/gluestack-ui/src/dependencies.ts`**: ```typescript // Add new dependencies to the appropriate project type const projectBasedDependencies: Dependencies = { nextjs: { dependencies: { // Add your new dependency here 'your-new-package': '^1.0.0', // ... existing dependencies }, }, expo: { dependencies: { // Add your new dependency here 'your-new-package': '^1.0.0', // ... existing dependencies }, }, // ... other project types }; ``` 2. **Test dependency resolution**: ```bash cd packages/gluestack-ui yarn build ``` #### Step 3: Update Template Files 1. **For configuration changes** (tailwind, babel, etc.): ```bash # Update the appropriate template file # Example: updating tailwind.config.js cp your-updated-tailwind.config.js packages/gluestack-ui/templates/tailwind.config.js ``` 2. **For project-specific templates**: ```bash # Update Next.js templates # Update Expo templates # Update React Native CLI templates # Each project type may need different configurations ``` 3. **For provider components**: ```bash # Update provider templates if needed # Check packages/gluestack-ui/src/util/init/index.ts for provider logic ``` #### Step 4: Update Initialization Logic If your changes affect the initialization process: 1. **Review `packages/gluestack-ui/src/util/init/index.ts`**: - Check if new project types need support - Update provider installation logic - Modify configuration generation 2. **Update configuration helpers**: ```bash # Update project-specific config helpers packages/gluestack-ui/src/util/config/ ``` #### Step 5: Test Template Updates 1. **Test with local CLI**: ```bash # Build the CLI package cd packages/gluestack-ui yarn build # Test initialization in a new directory mkdir test-init && cd test-init npm init -y npx ../gluestack-ui/dist/index.js init --projectType nextjs ``` 2. **Test all project types**: ```bash # Test Next.js initialization npx ../gluestack-ui/dist/index.js init --projectType nextjs # Test Expo initialization npx ../gluestack-ui/dist/index.js init --projectType expo # Test React Native CLI initialization npx ../gluestack-ui/dist/index.js init --projectType react-native-cli ``` 3. **Verify generated files**: - Check that all dependencies are properly installed - Verify configuration files are correctly generated - Test component functionality in the initialized project #### Step 6: Update Documentation 1. **Update CLI documentation** if new options or project types are added 2. **Update template documentation** for any new configuration options 3. **Update contributing guide** if the process changes ### Template-Specific Considerations #### Next.js Templates - **Version-specific templates**: `templates/nextjs/next15/` for Next.js 15+ - **Configuration files**: `next.config.js`, `postcss.config.js` - **Provider setup**: Web-specific provider components - **Dependencies**: React Native Web, DOM helpers #### Expo Templates - **Configuration files**: `metro.config.js`, `babel.config.js` - **Provider setup**: Native-specific provider components - **Dependencies**: React Native Safe Area Context, Expo HTML Elements #### React Native CLI Templates - **Configuration files**: `metro.config.js`, `babel.config.js` - **Provider setup**: Native-specific provider components - **Dependencies**: React Native Reanimated, React Native Safe Area Context ### Testing Checklist Before shipping template updates: - [ ] All project types initialize successfully - [ ] Dependencies are correctly installed - [ ] Configuration files are properly generated - [ ] Provider components work as expected - [ ] Components function correctly in initialized projects - [ ] No breaking changes to existing functionality - [ ] Documentation is updated - [ ] CLI builds without errors ### Common Template Update Scenarios #### Adding a New Component with Dependencies 1. **Update dependencies.ts** with new package requirements 2. **Test initialization** with the new component 3. **Verify dependencies** are installed correctly 4. **Update documentation** if needed #### Updating Configuration Files 1. **Update template files** in `packages/gluestack-ui/templates/` 2. **Test initialization** to ensure configs are applied 3. **Verify functionality** in initialized projects #### Adding New Project Type Support 1. **Add project type** to dependencies configuration 2. **Create template files** for the new project type 3. **Update initialization logic** to handle the new type 4. **Test thoroughly** with the new project type ### Troubleshooting Template Updates #### Initialization Fails 1. **Check dependency versions** in `dependencies.ts` 2. **Verify template files** exist and are correct 3. **Test with minimal setup** to isolate issues 4. **Check CLI build** for compilation errors #### Generated Files Incorrect 1. **Review template copying logic** in init/index.ts 2. **Check file paths** and template structure 3. **Verify file permissions** and access rights 4. **Test template file content** manually #### Dependencies Not Installed 1. **Check dependencies.ts** configuration 2. **Verify package manager** detection logic 3. **Test installation process** manually 4. **Check for version conflicts** ### Best Practices - **Always test** template updates with multiple project types - **Maintain backward compatibility** when possible - **Document breaking changes** clearly - **Use semantic versioning** for template updates - **Test in clean environments** to avoid local state issues - **Keep templates minimal** and focused on essential setup - **Update examples** and documentation with template changes ### Advanced Features #### Accessibility Support If your component needs accessibility features: 1. **Import accessibility props** from react-native 2. **Add ARIA attributes** where appropriate 3. **Support screen readers** with proper labels 4. **Test with accessibility tools** #### Animation Support For animated components: 1. **Use react-native-reanimated** for animations 2. **Provide animation configuration props** 3. **Ensure performance** on lower-end devices 4. **Document animation behavior** ## Documentation Guidelines ### Documentation Structure Create Component documentation in this directory: ``` src/components/ui// ├── examples/ # Usage examples │ ├── basic/ | |- meta.json | |- template.handlebars |── docs/ # Main docs | |- index.mdx ``` ### Documentation Best Practices - **Include comprehensive examples** showing different variants and use cases - **Document all props** with types and descriptions - **Provide accessibility guidelines** for component usage - **Include common patterns** and best practices - **Update sidebar.json** when adding new component documentation ## Testing Your Changes ### Using Kitchen Sink App The `kitchen-sink` app is the primary testing environment: - Contains all components with various configurations - Hot reloads when source files change - Best for component development and testing #### Testing Components in Kitchen Sink 1. **Navigate to Kitchen Sink**: ```bash cd apps/kitchen-sink ``` 2. **Start Development Server** ```bash yarn dev ``` ### Using Website App The `website` app for documentation testing: - Full documentation experience - Component examples and API documentation - Best for documentation changes Test documentation rendering: ```bash # Run website for documentation testing cd apps/website yarn dev ``` Visit the documentation page to verify: - Component examples render correctly - API documentation is accurate - Examples are interactive ### Using Starter Kits Test starter templates to ensure: - Components work in fresh projects - Installation process is smooth - No missing dependencies ### Component Testing Checklist Before submitting your component: - [ ] Component renders correctly in Kitchen Sink app - [ ] Documentation displays properly on Website app - [ ] Component works in starter templates - [ ] TypeScript types are properly defined - [ ] Accessibility features are implemented - [ ] Examples cover common use cases - [ ] Props are well documented - [ ] Cross-platform compatibility (web and native) - [ ] Performance tested on lower-end devices ## Contribution Guidelines ### Before Contributing 1. **Understand the project structure** (this document) 2. **Set up your development environment** 3. **Run the mapper scripts** to ensure everything works locally 4. **Explore the codebase** using the kitchen-sink and website apps ### Code Style & Standards - Follow existing code patterns and conventions - Use TypeScript for type safety - Write meaningful commit messages - Test your changes across multiple applications - Update documentation when adding new features ### Pull Request Process The process of proposing a change to `gluestack/gluestack-ui` can be summarized as follows: 1. **Create a feature branch** from `main` 2. **Make your changes** in source files only 3. **Test thoroughly** using kitchen-sink and website apps 4. **Update documentation** if needed 5. **Push changes** to your fork 6. **Create a pull request** to the `gluestack/gluestack-ui` repository 7. **Review and address comments** on your pull request If all goes well, your pull request will be merged. If it is not merged, maintainers will do their best to explain the reason why. ### File Identification #### Generated Files (DO NOT EDIT) - Any file in `apps/*/components/ui/` - `apps/website/components/docs-components/` - `apps/website/sidebar.json` #### Source Files (EDIT THESE) - Any file in `src/components/ui/` - Any file in `packages/gluestack-utils/` - Any file in `packages/gluestack-core/` - Any file in `src/docs-components/` - `src/sidebar.json` ### Common Pitfalls to Avoid - ❌ Editing generated files in `apps/*/components/ui/` - ❌ Committing generated files - ❌ Not testing in multiple applications - ❌ Forgetting to update documentation - ❌ Making changes without understanding the mapper system ## Troubleshooting ### Missing Generated Files? ```bash yarn sync ``` ### Changes Not Reflecting? Check if `yarn dev` is running and watching for changes. ### Git Trying to Commit Generated Files? Check `.gitignore` configuration in the respective app directory. ### Mapper Scripts Not Working? 1. Ensure you're in the root directory 2. Check that dependencies are installed 3. Verify file permissions on scripts ## Getting Help If you have any questions or need guidance, feel free to reach out—collaboration is key! - **Issues**: Open a GitHub issue for bugs or feature requests - **Discussions**: Use GitHub Discussions for questions - **Documentation**: Check the website for comprehensive guides - **Code**: Refer to existing components for patterns and examples ## Maintenance The gitignore rules are maintained in each destination app. If you add new generated file patterns, update the respective `.gitignore` files and this documentation. For questions or issues with the generated files system, refer to the mapper scripts in `scripts/mappers/` or contact the development team. --- Happy contributing, **The `gluestack-ui` Team** ❤️ --- ## Discord FAQs URL: /ui/docs/guides/more/discord-faqs/index # Discord FAQs --- ## FAQs URL: /ui/docs/guides/more/faqs/index # FAQs ## Migration and Compatibility - **How do I migrate from gluestack v1 to v2?** - We have a detailed migration guide with codemod. - **Will gluestack v2 be compatible with my current project?** - Yes, you need to follow a few steps to get your project up and running. - **What are the major differences between gluestack v1 and v2?** <> | gluestack-v1 | gluestack-v2 | | --- | --- | | Bundled Library | Copy-Pastable components | | gluestack-style as styling library | NativeWind as a styling library | | Utility prop support | Tailwind CSS className support | | No RSC support | Partial RSC support | ## New Features and Improvements - **What are the new features in gluestack v2?** - Tailwind CSS className support. - Partial RSC support. - NativeWind as a styling library. - Copy-Pastable components. - **How does gluestack v2 improve performance compared to v1?** - Coming soon... - **Are there any new components or APIs in gluestack v2?** - Yes, we have Skeleton, Table, Bottomsheet, Grid, and many more to come. ## Support and Community - **What kind of support is available for gluestack v1?** - We will provide maintenance and support for gluestack-ui v1 for a limited time to ensure a smooth transition. - **How can I provide feedback or report bugs?** - You can create a GitHub issue on our [repo](https://github.com/gluestack/gluestack-ui). ## Technical Questions - **Are there any known issues with gluestack v2?** - You can check out the [troubleshooting](https://gluestack.io/ui/docs/guides/more/troubleshooting#known-issues) page for this. - **Are there any deprecated features from v1 that are not available in gluestack v2?** - Descendant Styling. - Utility Props. - Property Resolver. - Passing Prop. ## Adoption and Integration - **How do I integrate gluestack v2 with other libraries and frameworks?** - It works out of the box. You can check out our integration of Gorhom Bottom Sheet for more reference. - **What are the best practices for using gluestack v2 in production?** - **Purge Unused CSS**: Enable PurgeCSS to remove unused styles in production. - **Use CDN for Tailwind**: Serve Tailwind CSS via a CDN for faster load times. - **Optimize Build Process**: Use tools like PostCSS and Autoprefixer for better performance. - **Minimize Custom CSS**: Stick to Tailwind's utility classes to keep your CSS file small. - **Don't Include Unused Utilities**: Avoid adding unused utilities in your configuration to keep the CSS file lightweight. - **Don't Ignore Responsive Design**: Ensure your design is responsive to avoid performance issues on different devices. - **Don't Overload with Plugins**: Use only necessary plugins to avoid bloating the CSS file. - **Don't Skip Testing**: Regularly test your site's performance and make adjustments as needed. --- ## Releases URL: /ui/docs/guides/more/releases/index # Releases To view releases, please visit [GitHub releases](https://github.com/gluestack/gluestack-ui/releases). --- ## Roadmap URL: /ui/docs/guides/more/roadmap/index # Roadmap The gluestack-ui roadmap outlines our vision, priorities, and upcoming features for the framework. It helps the community stay aligned on what we're building, what's in progress, and what's planned for the future. ## Shipped ### Components - **BottomSheet** - Mobile-first interaction pattern - **Date/Time Picker** - Essential form component with high community demand - **Image-Viewer** - Image viewer component with zoom and pan support - **LiquidGlass** - Liquid glass effect component ### Animations - **Component-level animation hooks** (enter/exit animations) - **Smooth transitions** across all interactive components ### Framework - **NativeWind v5** (Tailwind CSS v4, Expo & React Native CLI) - **UniWind** (Tailwind CSS v4, Expo-only) ## Upcoming ### Developer Experience - **Interactive Playgrounds** - Live component examples with seamless code editing - **VS Code Snippets** - Enhanced development workflow - **Utility Props** - NativeWind support improvements ### Performance - **Performance Benchmarking** ### Framework - **Next.js support** - Pending NativeWind v5 web support --- ## Troubleshooting URL: /ui/docs/guides/more/troubleshooting/index # Troubleshooting If you encounter any issues while using nativewind, please refer to the nativewind [troubleshooting guide](https://www.nativewind.dev/v4/guides/troubleshooting). ## Common Issues ### 1. "dark:" Variant Not Working as Expected If you are using `dark:` and experiencing unexpected behavior, ensure that you have followed the documentation for [dark mode configuration](/home/theme-configuration/dark-mode) correctly. ### 2. Toast Inside Modal Not Functioning When using `Toast` inside a `Modal`, ensure that you have added the `OverlayProvider` from `@gluestack-ui/overlay` to the root of your application. ```jsx const App = () => { return ( ); }; ``` ### 3. Flashing Issue in Next.js If encountering flashing issues in Next.js: - Refer to the [installation documentation](https://gluestack.io/ui/nativewind/docs/home/getting-started/installation), click on manual, and ensure that step 4 is correctly configured. ### 4. TailwindCSS Classnames Not Overriding in react-native-web To ensure Tailwind styles override with higher specificity, add `important: 'html'` to your `tailwind.config.js`. ```jsx // tailwind.config.js module.exports = { ... important: 'html', ... } ``` ## Known Issues - `placeholder` does not work with CSS tokens. - `dark:` does not function with "class" as a strategy in native devices. ## Still Facing Issues? If you continue to experience issues, please create an issue on [GitHub](https://github.com/gluestack/gluestack-ui). --- ## Upgrade to gluestack-ui v2 with Codemod URL: /ui/docs/guides/more/upgrade-to-v2/index # Upgrade to gluestack-ui v2 with Codemod This guide provides detailed steps to upgrade from gluestack-ui v1 to gluestack-ui v2. With codemod you can automate the process of code transformations, making it easier and faster to migrate your project to gluestack-ui v2. By following this guide, you'll ensure a smooth transition with minimal manual adjustments. ## gluestack-ui v1 Usage Scenarios: gluestack-ui v1 users can be divided into three scenarios: - **Scenario 1** : Users who imported the gluestack-ui configuration from `@gluestack-ui/config`. - **Scenario 2** : Users who exported the gluestack-ui configuration and made modifications to it (`gluestack-ui.config.ts`). - **Scenario 3** : Users who exported components as well. At this time, we fully support migration for users in **Scenario 1 and Scenario 2** using codemod. ### Overview of Steps to Follow (for Scenario 1 and Scenario 2 Users) 1. **Initialize gluestack-ui v2**: `{`npx gluestack-ui@latest init`}` 2. **Setup Tailwind CSS**: `import "@/global.css"` 3. **Add All Components**: `{`npx gluestack-ui@latest add --all`}` 4. **Code Migration**: `npx @gluestack-ui/v2-codemod@latest ` ## Step 1 : Initialize gluestack-ui v2 **Run the below command to setup the project**: ```bash npx gluestack-ui@latest init ``` To refer more about the gluestack-ui v2 installation, check [this](https://gluestack.io/ui/docs/home/getting-started/installation). > Installation using gluestack-ui CLI in Expo projects supports for Expo SDK 50 and above only. For Expo SDK < 49, please refer to the manual installation guide [here](https://gluestack.io/ui/docs/home/getting-started/installation). ## Step 2 : Setup Tailwind CSS Import `global.css` / `globals.css` where Tailwind directives are defined. ```jsx export default function App() { return ( Simple Text ); } //After: //descendant style will remain as it is. export default function App() { return ( Simple Text ) } //Manual Changes: //Update the descendant styling: //descendant style will remain as it is. //color:'white' --> text-white export default function App() { return ( Simple Text ) } ``` ## Examples of some property combinations : **Applying styles in different color modes (light/dark)** : **Before**: ```jsx export default function App() { return ( Simple Text ); } ``` **After**: ```jsx export default function App() { return Simple Text; } ``` **Applying styles for different media queries (sm/md/lg) using @** : **Before**: ```jsx export default function App() { return ( Simple Box ); } ``` **After**: ```jsx export default function App() { return ( Simple Box ); } ``` **Applying styles for different media queries (sm/md/lg) using $** : **Before**: ```jsx export default function App() { return ( Simple Box ); } ``` **After**: ```jsx export default function App() { return ( Simple Box ); } ``` **Applying basic layout stylings** : **Before**: ```jsx export default function App() { return ( Simple Box ); } ``` **After**: ```jsx export default function App() { return Simple Box; } ``` **Applying basic layout stylings in different color modes** : **Before**: ```jsx export default function App() { return ( Simple Box ); } ``` **After**: ```jsx //By default if you don't mention any mode it's take light mode. export default function App() { return Simple Box; } ``` **Applying styles for different action states (hover/active etc..,)** : **Before**: ```jsx export default function App() { return ( Simple ); } ``` **After**: ```jsx //If not mentioned any mode explicitly then it's gonna take light mode only. export default function App() { return ( Simple ); } ``` **Applying styles for different platforms (mobile/web/android/ios)** : **Before**: ```jsx export default function App() { return ( gluestack-ui ); } ``` **After**: ```jsx export default function App() { return gluestack-ui; } ``` **Applying styles for different color modes at different media queries** : **Before**: ```jsx export default function App() { return (
); } ``` **After**: ```jsx export default function App() { return (
); } ``` **Descendant Styling (this type of styling is not supported by NativeWind)** : **Before**: ```jsx export default function App() { return ( gluestack-ui ); } ``` **After**: ```jsx // we need to remove the descendant styling from parent element and add that // styling to all the child elements export default function App() { return ( gluestack-ui ); } ``` --- ## Upgrade to gluestack-ui v3 URL: /ui/docs/guides/more/upgrade-to-v3/index # Upgrade to gluestack-ui v3 ## Why gluestack-ui v3? ### Evolution to v3 **gluestack-ui v3** represents a major evolution in our approach to universal component development. Building on the foundation of v2, v3 introduces a revolutionary source-to-destination architecture that ensures consistency across all platforms while maintaining maximum flexibility. #### What's New in v3 - **Source-to-Destination Architecture** *(contribution change)*: All components are maintained in a single source of truth (`src/` directory) and automatically synced across all project templates and examples - **Enhanced Package System** *(contribution change)*: Modular packages (`gluestack-core`, `gluestack-utils`, `ui-next-adapter`) for better maintainability and extensibility - **Improved Developer Experience** *(contribution change)*: Streamlined development workflow with automated file synchronization and hot reloading - **Advanced Component Creation** *(contribution change)*: New CLI tools and scripts for rapid component development and contribution - **Better Documentation Integration** *(contribution change)*: Seamless integration between component source code and documentation - **Enhanced Testing Infrastructure** *(contribution change)*: Comprehensive testing across multiple starter templates and applications ## Migration from Previous Versions ### From v2 to v3 #### What's Changed (Minimal Breaking Changes): **gluestack-ui v3** maintains API compatibility with v2 while introducing significant architectural improvements: - **Package structure changes** to @gluestack-ui/core and @gluestack-ui/utils - **Import path updates** for creator function - **ARIA dependencies** moved from react-native-aria to @gluestack-ui/core - **Enhanced Next.js adapter** - **Better compatibility** with Expo SDK 53 and Next.js 15 #### What Stays the Same: - **Component APIs** remain exactly the same - **Styling system** unchanged - **Theming configurations** continue to work - **Copy-paste philosophy** maintained ### Migration Benefits - **Seamless Upgrade**: Simple upgrade process with `npx gluestack-ui@latest upgrade` command. - **Minimal code changes** (only import paths) - **Enhanced stability** and bug fixes - **Future-ready foundation** - **Improved developer experience** (contribution change) - **Community Support**: Active community and comprehensive migration resources We've prepared a comprehensive migration guide to help you transition smoothly from v2 to v3, including automated migration tools and detailed instructions. # Manual Migration to gluestack-ui v3 If you have a simple Next.js or Expo project using Gluestack, you can upgrade directly by running: ```bash npx gluestack-ui@latest upgrade ``` For monorepos, please follow the manual installation guide instead. This guide provides detailed manual steps to migrate from legacy gluestack-ui packages to the new gluestack-ui v3 ecosystem. Follow these steps carefully to ensure a smooth transition with minimal issues. We’ll be releasing an official Gluestack monorepo template soon to make the process even easier. ## Migration Overview The migration process involves several key steps: 1. **Backup and Preparation** - Secure your codebase 2. **Package Management** - Remove old packages and install new ones 3. **Configuration Updates** - Update Tailwind and Next.js configurations 4. **Code Transformation** - Update import statements and component usage 5. **Clean Installation** - Ensure dependency integrity ## Step 1: Backup and Preparation ### Check Git Status Before proceeding, ensure your project has no uncommitted changes: ```bash git status ``` If you have uncommitted changes, either commit them or stash them: ```bash # Commit changes git add . git commit -m "Pre-migration backup" # Or stash changes git stash ``` ### Identify Legacy Packages Check your `package.json` to identify old gluestack packages that need to be removed. Look for packages starting with: - `@gluestack-ui` - `gluestack` - `@gluestack` ## Step 2: Remove Legacy Packages ### Detect Old Packages First, list all the legacy packages in your `package.json` and remove them: Remove `react-native-aria` **npm:** ```bash # Remove old packages npm uninstall react-native-aria ``` **yarn:** ```bash # Remove old packages yarn remove @gluestack-ui/themed @gluestack-ui/components @gluestack-ui/config @gluestack/ui-next-adapter ``` **pnpm:** ```bash # Remove old packages pnpm remove @gluestack-ui/themed @gluestack-ui/components @gluestack-ui/config @gluestack/ui-next-adapter ``` **bun:** ```bash # Remove old packages bun remove @gluestack-ui/themed @gluestack-ui/components @gluestack-ui/config @gluestack/ui-next-adapter ``` **Note**: Replace the package names above with the actual legacy packages found in your `package.json`. ## Step 3: Update Configuration Files ### Update Tailwind Configuration Remove the old gluestack plugin from your `tailwind.config.ts` or `tailwind.config.js`: **Before:** ```typescript import gluestackPlugin from '@gluestack-ui/nativewind-utils/tailwind-plugin'; export default { // ... other config plugins: [gluestackPlugin, /* other plugins */], } ``` **After:** ### Update Next.js Configuration Update your Next.js configuration files (`next.config.ts`, `next.config.js`, or `next.config.mjs`): **Before:** ```typescript import { withGluestackUI } from '@gluestack/ui-next-adapter'; const nextConfig = { // ... your config }; export default withGluestackUI(nextConfig); ``` **After:** ```typescript import { withGluestackUI } from '@gluestack/ui-next-adapter'; const nextConfig = { // ... your config }; export default withGluestackUI(nextConfig); ``` ### Update Registry File (Next.js 15) If you have an `app/registry.tsx` file, update the flush import: **Before:** ```typescript import { flush } from '@gluestack-ui/nativewind-utils/flush'; ``` **After:** ```typescript import { flush } from '@gluestack-ui/utils/nativewind-utils'; ``` ## Step 4: Install New Packages Install the new gluestack-ui v3 packages: **npm:** ```bash npm install @gluestack-ui/core@latest @gluestack-ui/utils@latest react-native-svg@15.12.0 @gluestack/ui-next-adapter@latest ``` **yarn:** ```bash yarn add @gluestack-ui/core@latest @gluestack-ui/utils@latest react-native-svg@15.12.0 @gluestack/ui-next-adapter@latest ``` **pnpm:** ```bash pnpm i @gluestack-ui/core@latest @gluestack-ui/utils@latest react-native-svg@15.12.0 @gluestack/ui-next-adapter@latest ``` **bun:** ```bash bun add @gluestack-ui/core@latest @gluestack-ui/utils@latest react-native-svg@15.12.0 @gluestack/ui-next-adapter@latest ``` ## Step 5: Clean Installation To ensure all dependencies are properly resolved, perform a clean installation: **npm:** ```bash # Remove node_modules and package-lock.json rm -rf node_modules package-lock.json # Reinstall dependencies npm install ``` **yarn:** ```bash # Remove node_modules and yarn.lock rm -rf node_modules yarn.lock # Reinstall dependencies yarn install ``` **pnpm:** ```bash # Remove node_modules and pnpm-lock.yaml rm -rf node_modules pnpm-lock.yaml # Reinstall dependencies pnpm install ``` **bun:** ```bash # Remove node_modules and bun.lockb rm -rf node_modules bun.lockb # Reinstall dependencies bun install ``` ## Step 6: Update Import Statements ### Transform Component Imports Update all import statements in your `components/ui` folder and throughout your codebase: **Before:** ```typescript import { tva } from '@gluestack-ui/utils/nativewind-utils'; ``` ## Step 7: Update Component Usage Patterns ### Import Path Transformation Rules Use these patterns to transform your imports: | Old Import Pattern | New Import Pattern | | --- | --- | | `{`from '@gluestack-ui/[component]'`}` | `{`from '@gluestack-ui/core/[component]/creator'`}` | | `{`from '@gluestack-ui/nativewind-utils/[utility]'`}` | `{`from '@gluestack-ui/utils/nativewind-utils'`}` | | `{`from '@gluestack/ui-next-adapter'`}` | `{`from '@gluestack/ui-next-adapter'`}` | ## Step 8: Recursive File Processing ### Process All Component Files You'll need to update imports in all TypeScript/JavaScript files recursively. Here's a systematic approach: 1. **Start with the main components directory**: `components/ui/` 2. **Process subdirectories**: Look for nested component folders 3. **Update file extensions**: Process `.ts`, `.tsx`, `.js`, `.jsx` files 4. **Check other directories**: Look for components in `src/`, `app/`, `screens/`, etc. ### File Processing Checklist For each file, verify: - All `@gluestack-ui/*` imports are updated - All `@gluestack/*` imports are updated - No legacy package imports remain - File compiles without import errors ## Step 9: Verification and Testing ### Verify Migration Success After completing the migration: #### Step1: Check for compilation errors: ```bash npm run build ``` #### Step2: Run your development server: ```bash npm run dev ``` #### Step3: Test core functionality: Ensure your components render and behave correctly ### Common Issues and Solutions #### Import Resolution Issues If you encounter import errors: - Verify package installation: Check that all new packages are in `package.json` - Clear cache: Delete `node_modules` and reinstall - Check import paths: Ensure they follow the new pattern exactly #### Build Errors If builds fail: - Check TypeScript configuration compatibility - Verify all configuration files are updated - Ensure no legacy imports remain #### Runtime Issues If components don't work at runtime: - Check browser console for errors - Verify CSS/styling is loading correctly - Test component functionality individually ## Step 10: Post-Migration Cleanup ### Final Verification Steps #### Step1: Search for legacy references: ```bash # Search for any remaining old imports grep -r "@gluestack-ui" src/ components/ app/ grep -r "@gluestack/" src/ components/ app/ ``` #### Step2: Commit your changes**: ```bash git add . git commit -m "Migrate to gluestack-ui v3 packages" ``` 3. **Test thoroughly**: Run your test suite and manually test key features ## Troubleshooting ### Package Manager Issues If you encounter dependency resolution issues: #### Step1: Clear package manager cache: ```bash npm cache clean --force # for npm yarn cache clean # for yarn pnpm store prune # for pnpm bun pm cache rm # for bun ``` #### Step2: Delete lock files and reinstall: ```bash rm -rf node_modules package-lock.json npm install ``` ### Configuration Issues If configurations aren't working: - Double-check file names and extensions - Ensure proper syntax in config files - Verify import statements in config files ### Component Issues If components aren't working: - Check component documentation for API changes - Verify styling and theming setup - Test components individually ## Migration Checklist Use this checklist to track your migration progress: - **Preparation** - Committed or stashed all changes - Identified all legacy packages - Created project backup - **Package Management** - Removed all legacy gluestack packages - Installed new gluestack packages - Performed clean installation - **Configuration Updates** - Updated Tailwind configuration - Updated Next.js configuration - Updated registry file (if applicable) - **Code Updates** - Updated all component imports - Updated utility imports - Processed all subdirectories - Verified no legacy imports remain - **Testing** - Project builds successfully - Development server runs - Components render correctly - Core functionality works - **Finalization** - Committed changes - Updated documentation - Tested thoroughly ## Next Steps After successful migration: 1. **Update your team**: Inform team members about the migration 2. **Update documentation**: Revise any project-specific documentation 3. **Monitor for issues**: Keep an eye on the application for any unexpected behavior 4. **Stay updated**: Follow gluestack-ui releases for future updates ## Support If you encounter issues during migration: - Check the [gluestack-ui documentation](https://gluestack.io/ui/docs) - Review common issues in this guide - Seek help from the gluestack-ui community Remember to test thoroughly in a development environment before deploying to production! --- ## Upgrade to gluestack-ui v4 URL: /ui/docs/guides/more/upgrade-to-v4/index # Upgrade to gluestack-ui v4 ## Why gluestack-ui v4? ### Evolution to v4 **gluestack-ui v4** introduces significant improvements to the animation system, performance optimizations, and enhanced compatibility with the latest React Native ecosystem. Building on the solid foundation of v3, v4 brings a modern animation engine and better developer experience. #### What's New in v4 - **shadcn-Inspired Color System**: Modern color token system with semantic naming (primary, secondary, accent, muted, etc.) - **New Animation Engine**: Migration from `@legendapp/motion` to `react-native-reanimated` (~4.2.1) for most animated components - **Enhanced Performance**: Better performance with the new animation system and worklets support - **Tailwind v4 Support**: Full compatibility with Tailwind CSS v4 (via Uniwind) - **Modern Dependencies**: Updated to latest React Native ecosystem packages - **Improved Type Safety**: Enhanced TypeScript support with better type definitions - **Better Web Support**: Enhanced Next.js adapter with improved SSR capabilities ## Migration from v3 to v4 ### What's Changed: **gluestack-ui v4** introduces important dependency, configuration, and theming changes: - **Color Token System**: ⚠️ **Breaking Change** - Migrated to shadcn-like color tokens - New color naming convention (e.g., `primary`, `secondary`, `accent`, `muted`, etc.) - Updates required in `tailwind.config.js` and `gluestack-ui-provider/config.ts` - All component styles updated to use new tokens - **Animation System**: Most components now use `react-native-reanimated` instead of `@legendapp/motion` - **Component Styles**: ⚠️ **Breaking Change** - All components have updated styles - Components use new color tokens - Animation library changes in some components - **Must re-copy components** from CLI using `npx gluestack-ui add ` - **New Dependencies**: Added `react-native-worklets`, `react-native-reanimated@~4.2.1`, updated other packages - **Babel Configuration**: Required babel plugin for worklets support (Expo/RN CLI) - **Package Versions**: Updated to v4 alpha packages - **Next.js Adapter**: Updated `@gluestack/ui-next-adapter` to v4 ### What Stays the Same: - **Component APIs** remain exactly the same - **Import patterns** are unchanged (v3 and v4 use identical imports) - **Copy-paste philosophy** maintained - **Overall theming approach** stays the same (only token names changed) ### Migration Benefits - **Automatic CLI Upgrade**: Simple upgrade process with `npx gluestack-ui@alpha upgrade` command - **Modern Color System**: shadcn-like color tokens for better theming consistency - **Semantic Color Naming**: More intuitive color names (primary, secondary, accent, muted, etc.) - **Better Performance**: New animation engine with react-native-reanimated - **Enhanced Stability**: Bug fixes and improved reliability - **Future-Ready**: Foundation for upcoming features and Tailwind v4 support - **Improved Animation Capabilities**: Smoother animations with worklets - **No Import Changes**: v3 and v4 use identical import patterns (though components must be re-copied) # Automated Upgrade (Recommended) The easiest way to upgrade is using our automated CLI command: ```bash npx gluestack-ui@alpha upgrade ``` This command will: - Auto-detect your current version (v2 or v3) - Upgrade to v4 automatically - Install all required dependencies - Update configuration files (babel.config.js for Expo/RN CLI) - Handle platform-specific packages (Next.js, Expo, React Native CLI) > **Important:** After running the automated upgrade, you still need to: > 1. Update color tokens in `tailwind.config.js` and `gluestack-ui-provider/config.ts` (see Step 4 below) > 2. Re-copy all components using `npx gluestack-ui add ` (see Step 5 below) For monorepos or custom setups, please follow the manual installation guide below. # Manual Migration to gluestack-ui v4 This guide provides detailed manual steps to migrate from gluestack-ui v3 to v4. Follow these steps carefully to ensure a smooth transition. ## Migration Overview The migration process involves several key steps: 1. **Backup and Preparation** - Secure your codebase 2. **Package Management** - Install new v4 packages 3. **Configuration Updates** - Update Babel and Next.js configurations 4. **Color Token Migration** - Update to shadcn-like color system 5. **Re-copy Components** - Get updated component styles 6. **Clean Installation** - Ensure dependency integrity 7. **Verification** - Test your application ### Understanding the Color Token Changes v4 introduces a new color token system inspired by shadcn/ui. This provides better semantic naming and more consistent theming: **New Color Tokens:** - `background` / `foreground` - Base application colors - `card` / `card-foreground` - Card surfaces - `popover` / `popover-foreground` - Popover/dropdown surfaces - `primary` / `primary-foreground` - Primary brand color - `secondary` / `secondary-foreground` - Secondary brand color - `muted` / `muted-foreground` - Muted/subtle content - `accent` / `accent-foreground` - Accent highlights - `destructive` / `destructive-foreground` - Error/danger states - `border` - Border colors - `input` - Input field borders - `ring` - Focus ring colors These tokens use HSL color space with CSS variables for easy theming and dark mode support. ## Step 1: Backup and Preparation ### Check Git Status Before proceeding, ensure your project has no uncommitted changes: ```bash git status ``` If you have uncommitted changes, either commit them or stash them: ```bash # Commit changes git add . git commit -m "Pre-migration to v4 backup" # Or stash changes git stash ``` ### Verify Current Version Check your `package.json` to confirm you're on v3: ```json { "dependencies": { "@gluestack-ui/core": "^3.0.10", "@gluestack-ui/utils": "^3.0.11" } } ``` ## Step 2: Install v4 Packages ### Install New Packages Install the new gluestack-ui v4 packages and required dependencies: **Next.js:** ```bash npm install @gluestack-ui/core@alpha @gluestack-ui/utils@alpha react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 @legendapp/motion@^2.4.0 tailwind-variants@^0.1.20 react-native-svg@^15.12.0 nativewind@^4.1.23 @gluestack/ui-next-adapter@alpha react-native-web@^0.20.0 react-native-safe-area-context@^5.6.1 react-aria@^3.41.1 react-stately@^3.39.0 dom-helpers@^5.2.1 ``` Or with yarn: ```bash yarn add @gluestack-ui/core@alpha @gluestack-ui/utils@alpha react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 @legendapp/motion@^2.4.0 tailwind-variants@^0.1.20 react-native-svg@^15.12.0 nativewind@^4.1.23 @gluestack/ui-next-adapter@alpha react-native-web@^0.20.0 react-native-safe-area-context@^5.6.1 react-aria@^3.41.1 react-stately@^3.39.0 dom-helpers@^5.2.1 ``` **Expo:** ```bash npm install @gluestack-ui/core@alpha @gluestack-ui/utils@alpha react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 @legendapp/motion@^2.4.0 tailwind-variants@^0.1.20 react-native-svg@^15.12.0 nativewind@^4.1.23 react-native-safe-area-context@^5.6.1 react-aria@^3.41.1 @expo/html-elements@^0.12.5 react-stately@^3.39.0 ``` Or with yarn: ```bash yarn add @gluestack-ui/core@alpha @gluestack-ui/utils@alpha react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 @legendapp/motion@^2.4.0 tailwind-variants@^0.1.20 react-native-svg@^15.12.0 nativewind@^4.1.23 react-native-safe-area-context@^5.6.1 react-aria@^3.41.1 @expo/html-elements@^0.12.5 react-stately@^3.39.0 ``` **React Native CLI:** ```bash npm install @gluestack-ui/core@alpha @gluestack-ui/utils@alpha react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 @legendapp/motion@^2.4.0 tailwind-variants@^0.1.20 react-native-svg@^15.12.0 nativewind@^4.1.23 react-native-safe-area-context@^5.6.1 react-aria@^3.41.1 @expo/html-elements@^0.12.5 react-stately@^3.39.0 ``` Or with yarn: ```bash yarn add @gluestack-ui/core@alpha @gluestack-ui/utils@alpha react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 @legendapp/motion@^2.4.0 tailwind-variants@^0.1.20 react-native-svg@^15.12.0 nativewind@^4.1.23 react-native-safe-area-context@^5.6.1 react-aria@^3.41.1 @expo/html-elements@^0.12.5 react-stately@^3.39.0 ``` > **Note:** Use `--legacy-peer-deps` flag if you encounter peer dependency resolution issues. ### New Dependencies Explained | Package | Purpose | Required For | | --- | --- | --- | | `react-native-reanimated@~4.2.1` | Primary animation engine for v4 | All Projects | | `react-native-worklets@^0.7.1` | Required by react-native-reanimated | All Projects | | `@legendapp/motion@^2.4.0` | Used by some components (Tooltip, ActionSheet, Select) | All Projects | | `tailwind-variants@^0.1.20` | Enhanced variant system | All Projects | | `nativewind@^4.1.23` | Updated styling engine | All Projects | ## Step 3: Update Configuration Files ### Step 3.1: Update Babel Config (Expo/React Native CLI Only) For Expo and React Native CLI projects, you **must** add the `react-native-worklets/plugin` to your `babel.config.js`: **Before:** ```javascript module.exports = function (api) { api.cache(true); return { presets: ['babel-preset-expo'], plugins: [ // your existing plugins ], }; }; ``` **After:** ```javascript module.exports = function (api) { api.cache(true); return { presets: ['babel-preset-expo'], plugins: [ // your existing plugins 'react-native-worklets/plugin', // Add this line ], }; }; ``` > **Important:** The `react-native-worklets/plugin` **must** be included for react-native-reanimated to work properly. ### Step 3.2: Update Next.js Configuration (Next.js Only) Ensure your `@gluestack/ui-next-adapter` is updated to v4. The configuration syntax remains the same: ```typescript import { withGluestackUI } from '@gluestack/ui-next-adapter'; const nextConfig = { // ... your config }; export default withGluestackUI(nextConfig); ``` ## Step 4: Update Color Tokens (Breaking Change) ### Step 4.1: Update Tailwind Config ⚠️ **Important:** v4 introduces a new shadcn-like color token system. You need to update your `tailwind.config.js` with the new color definitions. Replace your existing color configuration with the new v4 color tokens: ```javascript /** @type {import('tailwindcss').Config} */ module.exports = { // ... other config theme: { extend: { colors: { // New v4 color tokens (shadcn-like) foreground: 'rgb(var(--foreground)/)', card: { DEFAULT: 'rgb(var(--card) / )', foreground: 'rgb(var(--card-foreground) / )', }, popover: { DEFAULT: 'rgb(var(--popover) / )', foreground: 'rgb(var(--popover-foreground) / )', }, muted: { DEFAULT: 'rgb(var(--muted) / )', foreground: 'rgb(var(--muted-foreground) / )', }, destructive: { DEFAULT: 'rgb(var(--destructive) / )', }, border: 'rgb(var(--border)/)', input: 'rgb(var(--input)/)', ring: 'rgb(var(--ring) / )', white: 'rgb(255 255 255)', primary: { DEFAULT: 'rgb(var(--primary)/)', foreground: 'rgb(var(--primary-foreground)/)', }, secondary: { DEFAULT: 'rgb(var(--secondary)/)', foreground: 'rgb(var(--secondary-foreground)/)', }, typography: { white: '#FFFFFF', gray: '#D4D4D4', black: '#181718', }, background: { DEFAULT: 'rgb(var(--background)/)', }, accent: { DEFAULT: 'rgb(var(--accent)/)', foreground: 'rgb(var(--accent-foreground)/)', }, }, }, }, }; ``` ### Step 4.2: Update GluestackUIProvider Config Update your `components/ui/gluestack-ui-provider/config.ts` (or wherever your provider config is located) with the new color token definitions: ```typescript 'use client'; export const config = { light: vars({ '--primary': '23 23 23', '--primary-foreground': '250 250 250', '--card': '255 255 255', '--secondary': '245 245 245', '--secondary-foreground': '23 23 23', '--background': '255 255 255', '--popover': '255 255 255', '--popover-foreground': '10 10 10', '--muted': '245 245 245', '--muted-foreground': '115 115 115', '--destructive': '231 0 11', '--foreground': '10 10 10', '--border': '229 229 229', '--input': '229 229 229', '--ring': '212 212 212', '--accent': '247 247 247', '--accent-foreground': '52 52 52', }), dark: vars({ '--primary-foreground': '23 23 23', '--primary': '255 245 245', '--card': '23 23 23', '--secondary': '38 38 38', '--secondary-foreground': '250 250 250', '--background': '10 10 10', '--popover': '23 23 23', '--popover-foreground': '250 250 250', '--muted': '38 38 38', '--muted-foreground': '161 161 161', '--destructive': '255 100 103', '--foreground': '250 250 250', '--border': '46 46 46', '--input': '46 46 46', '--accent': '38 38 38', '--accent-foreground': '250 250 250', '--ring': '115 115 115', }), }; ``` > **Note:** These are the default color tokens. You can customize them to match your brand colors. ## Step 5: Re-copy All Components ⚠️ **Critical Step:** Due to the color token changes and animation library updates, you **must re-copy all components** from the CLI. ### Re-copy Components One by One For each component you're using, run: ```bash # Example: Re-copy Button component npx gluestack-ui add button # Re-copy all components you're using npx gluestack-ui add accordion npx gluestack-ui add alert npx gluestack-ui add avatar # ... etc ``` When prompted to overwrite existing components, select **Yes** to replace them with the v4 versions. > **Important:** The CLI will automatically: > - Update component styles to use new color tokens > - Replace `@legendapp/motion` with `react-native-reanimated` where applicable > - Ensure components use the correct animation imports ### Backup Custom Changes If you've customized any component styles: 1. **Before re-copying**: Backup your customizations ```bash cp -r components/ui components/ui-backup ``` 2. **After re-copying**: Manually re-apply your customizations to the new component files ## Step 7: Clean Installation To ensure all dependencies are properly resolved, perform a clean installation: **npm:** ```bash # Remove node_modules and package-lock.json rm -rf node_modules package-lock.json # Reinstall dependencies npm install ``` **yarn:** ```bash # Remove node_modules and yarn.lock rm -rf node_modules yarn.lock # Reinstall dependencies yarn install ``` **pnpm:** ```bash # Remove node_modules and pnpm-lock.yaml rm -rf node_modules pnpm-lock.yaml # Reinstall dependencies pnpm install ``` **bun:** ```bash # Remove node_modules and bun.lockb rm -rf node_modules bun.lockb # Reinstall dependencies bun install ``` ## Step 8: No Import Changes Required! 🎉 Great news! **v3 and v4 use identical import patterns**, so you don't need to update any import statements: ```typescript // These imports work in both v3 and v4! ``` > **However:** While imports don't change, you still need to re-copy components (Step 5) due to internal style and animation changes. ## Step 9: Verification and Testing ### Verify Migration Success After completing the migration: #### 1. Check for compilation errors: ```bash npm run build ``` #### 2. Run your development server: **Next.js:** ```bash npm run dev ``` **Expo:** ```bash npx expo start ``` **React Native CLI:** ```bash # iOS npm run ios # Android npm run android ``` #### 3. Test animated components: Test components that use the new animation system: - Accordion - Drawer - Modal - Menu - Popover - Toast #### 4. Test your application functionality: Ensure all your components render and behave correctly. ## Common Issues and Solutions ### Babel Plugin Not Working **Issue:** Animated components not working in Expo/React Native. **Solution:** Ensure `react-native-worklets/plugin` is added to `babel.config.js` and restart your development server: ```bash # Clear cache and restart npx expo start --clear # For React Native CLI npx react-native start --reset-cache ``` ### Metro Bundler Issues **Issue:** Metro bundler errors after upgrade. **Solution:** Clear Metro cache and restart: ```bash # Expo npx expo start --clear # React Native CLI npx react-native start --reset-cache ``` ### Build Errors **Issue:** Build fails with dependency resolution errors. **Solution:** 1. Use `--legacy-peer-deps` flag: ```bash npm install --legacy-peer-deps ``` 2. Or try clearing cache: ```bash npm cache clean --force rm -rf node_modules package-lock.json npm install ``` ### Animation Performance Issues **Issue:** Animations are laggy or not smooth. **Solution:** Ensure Hermes is enabled (it's required for react-native-reanimated): For React Native CLI, check `android/app/build.gradle`: ```gradle project.ext.react = [ enableHermes: true, ] ``` For Expo, Hermes is enabled by default in SDK 50+. ## Migration Checklist Use this checklist to track your migration progress: - **Preparation** - [ ] Committed or stashed all changes - [ ] Verified current version is v3 - [ ] Created project backup - **Package Management** - [ ] Installed v4 packages - [ ] Installed react-native-reanimated and worklets - [ ] Updated Next.js adapter (if applicable) - **Configuration Updates** - [ ] Updated babel.config.js (Expo/RN CLI) - [ ] Verified Next.js configuration (Next.js) - [ ] Updated tailwind.config.js with new color tokens - [ ] Updated gluestack-ui-provider/config.ts with new color tokens - **Component Updates** - [ ] Backed up custom component changes - [ ] Re-copied all components using CLI - [ ] Re-applied custom modifications (if any) - [ ] Verified component styles look correct - **Clean Installation** - [ ] Performed clean installation - [ ] Cleared Metro/build cache - **Testing** - [ ] Project builds successfully - [ ] Development server runs - [ ] Animated components work correctly - [ ] Color theming works in light mode - [ ] Color theming works in dark mode - [ ] All functionality tested - **Finalization** - [ ] Committed changes - [ ] Updated documentation - [ ] Deployed to staging for testing ## Platform-Specific Notes ### Next.js Projects - Ensure `@gluestack/ui-next-adapter@alpha` is installed - No babel configuration changes needed - SSR/RSC continues to work as before ### Expo Projects - Works with Expo SDK 50+ - Hermes is enabled by default - Make sure to add worklets plugin to babel.config.js - Clear cache with `npx expo start --clear` ### React Native CLI Projects - Enable Hermes in build configuration - Add worklets plugin to babel.config.js - Clear cache with `npx react-native start --reset-cache` ## Animation System Changes ### Components Using react-native-reanimated (New in v4) These components now use `react-native-reanimated` for better performance: - Accordion - Drawer - Modal - Menu - Popover - Toast ### Components Still Using @legendapp/motion These components continue to use `@legendapp/motion`: - Tooltip - ActionSheet - Select > **Note:** Both animation libraries are automatically installed and configured by the CLI. ## Next Steps After successful migration: 1. **Test thoroughly**: Test all components in your application 2. **Monitor performance**: Check if animations perform better with the new engine 3. **Update documentation**: Revise any project-specific documentation 4. **Stay updated**: Follow gluestack-ui releases for future updates ## Support If you encounter issues during migration: - Check the [gluestack-ui documentation](https://gluestack.io/ui/docs) - Review common issues in this guide - Join our [Discord community](https://discord.gg/gluestack) for help - Report bugs on [GitHub](https://github.com/gluestack/gluestack-ui/issues) Remember to test thoroughly in a development environment before deploying to production! --- > Looking to upgrade to NativeWind v5 or UniWind? See the [Upgrade to v5 guide](/ui/docs/guides/more/upgrade-to-v5). --- ## Upgrade to gluestack-ui v5 URL: /ui/docs/guides/more/upgrade-to-v5/index # Upgrade to gluestack-ui v5 gluestack-ui v5 moves to **Tailwind CSS v4** (CSS-first configuration). You can choose between two styling engines: - **NativeWind v5** — Expo and React Native CLI projects - **UniWind** — Expo-only alternative with native style processing Both engines eliminate `tailwind.config.js` in favor of defining theme tokens directly in `global.css`. ## Automated Upgrade (Recommended) ```bash npx gluestack-ui@alpha upgrade ``` The CLI auto-detects your current version and asks which engine to upgrade to: ``` ◆ Which styling engine would you like to upgrade to? │ ● NativeWind v5 (Tailwind CSS v4) │ ○ UniWind (Tailwind CSS v4, Expo-only) │ ○ Stay on current version ``` > **Note:** The upgrade command is not supported for Next.js projects. --- # NativeWind v5 Upgrade ## What Changed | Area | Before (NativeWind v4) | After (NativeWind v5) | | --- | --- | --- | | Tailwind version | `tailwindcss@^3.x` | `tailwindcss@^4.2.0` | | NativeWind version | `nativewind@^4.1.23` | `nativewind@^5.0.0-preview.2` | | New package | — | `react-native-css@^3.0.4` | | PostCSS config | — | `postcss.config.js` with `@tailwindcss/postcss` | | Theme tokens | `tailwind.config.js` | `global.css` via `@layer theme` | | metro.config.js | `withNativewind` (same) | `withNativewind` from `nativewind/metro` | | Type declarations | `nativewind-env.d.ts` | `react-native-css-env.d.ts` | | lightningcss | — | Must pin to `1.30.1` | ## Automated CLI Upgrade ```bash npx gluestack-ui@alpha upgrade ``` Select **NativeWind v5 (Tailwind CSS v4)**. The CLI will: 1. Pin `lightningcss@1.30.1` in `package.json` overrides/resolutions 2. Replace `nativewind@^4.x` with `nativewind@^5.0.0-preview.2`, add `react-native-css` 3. Upgrade `tailwindcss` to v4, add `@tailwindcss/postcss` 4. Rewrite `global.css` with Tailwind v4 imports and theme tokens 5. Create `postcss.config.js` 6. Add `withNativewind` to `metro.config.js` (preserves existing options like `inlineRem`) 7. Prompt about `tailwind.config.js` — never auto-deleted (may contain custom tokens) 8. Create `react-native-css-env.d.ts` > **After upgrading:** Commit your changes, then re-add components to get NativeWind v5 versions: > ```bash > npx gluestack-ui@alpha add button accordion modal # all components you use > ``` ## Manual Migration to NativeWind v5 ### Step 1: Pin lightningcss Add to `package.json` **before** installing other packages: ```json { "overrides": { "lightningcss": "1.30.1" }, "resolutions": { "lightningcss": "1.30.1" } } ``` ### Step 2: Install packages **npm:** ```bash npm install nativewind@^5.0.0-preview.2 react-native-css@^3.0.4 @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 npm install --save-dev tailwindcss@^4.2.0 @tailwindcss/postcss@^4.2.0 ``` **yarn:** ```bash yarn add nativewind@^5.0.0-preview.2 react-native-css@^3.0.4 @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 yarn add --dev tailwindcss@^4.2.0 @tailwindcss/postcss@^4.2.0 ``` ### Step 3: Create `postcss.config.js` ```js export default { plugins: { "@tailwindcss/postcss": {}, }, }; ``` ### Step 4: Update `metro.config.js` Ensure `withNativewind` is imported from `nativewind/metro`: ```js const { getDefaultConfig } = require("expo/metro-config"); const { withNativewind } = require("nativewind/metro"); const config = getDefaultConfig(__dirname); module.exports = withNativewind(config); ``` ### Step 5: Rewrite `global.css` Replace the contents with Tailwind v4 format. Theme tokens move here from `tailwind.config.js`: ```css @import "tailwindcss/theme.css" layer(theme); @import "tailwindcss/preflight.css" layer(base); @import "tailwindcss/utilities.css"; @import "nativewind/theme"; @layer theme { :root { --primary: 23 23 23; --primary-foreground: 250 250 250; --card: 255 255 255; --secondary: 245 245 245; --secondary-foreground: 23 23 23; --background: 255 255 255; --popover: 255 255 255; --popover-foreground: 10 10 10; --muted: 245 245 245; --muted-foreground: 115 115 115; --destructive: 231 0 11; --foreground: 10 10 10; --border: 229 229 229; --input: 229 229 229; --ring: 212 212 212; --accent: 247 247 247; --accent-foreground: 52 52 52; } @media (prefers-color-scheme: dark) { :root { --primary: 255 245 245; --primary-foreground: 23 23 23; --card: 23 23 23; --secondary: 38 38 38; --secondary-foreground: 250 250 250; --background: 10 10 10; --popover: 23 23 23; --popover-foreground: 250 250 250; --muted: 38 38 38; --muted-foreground: 161 161 161; --destructive: 255 100 103; --foreground: 250 250 250; --border: 46 46 46; --input: 46 46 46; --ring: 115 115 115; --accent: 38 38 38; --accent-foreground: 250 250 250; } } /* Web: class-based toggle overrides media query */ :root.dark { --primary: 255 245 245; --primary-foreground: 23 23 23; --card: 23 23 23; --secondary: 38 38 38; --secondary-foreground: 250 250 250; --background: 10 10 10; --popover: 23 23 23; --popover-foreground: 250 250 250; --muted: 38 38 38; --muted-foreground: 161 161 161; --destructive: 255 100 103; --foreground: 250 250 250; --border: 46 46 46; --input: 46 46 46; --ring: 115 115 115; --accent: 38 38 38; --accent-foreground: 250 250 250; } :root.light { --primary: 23 23 23; --primary-foreground: 250 250 250; --card: 255 255 255; --secondary: 245 245 245; --secondary-foreground: 23 23 23; --background: 255 255 255; --popover: 255 255 255; --popover-foreground: 10 10 10; --muted: 245 245 245; --muted-foreground: 115 115 115; --destructive: 231 0 11; --foreground: 10 10 10; --border: 229 229 229; --input: 229 229 229; --ring: 212 212 212; --accent: 247 247 247; --accent-foreground: 52 52 52; } } @theme inline { --color-primary: rgb(var(--primary)); --color-primary-foreground: rgb(var(--primary-foreground)); --color-card: rgb(var(--card)); --color-secondary: rgb(var(--secondary)); --color-secondary-foreground: rgb(var(--secondary-foreground)); --color-background: rgb(var(--background)); --color-popover: rgb(var(--popover)); --color-popover-foreground: rgb(var(--popover-foreground)); --color-muted: rgb(var(--muted)); --color-muted-foreground: rgb(var(--muted-foreground)); --color-destructive: rgb(var(--destructive)); --color-foreground: rgb(var(--foreground)); --color-border: rgb(var(--border)); --color-input: rgb(var(--input)); --color-ring: rgb(var(--ring)); --color-accent: rgb(var(--accent)); --color-accent-foreground: rgb(var(--accent-foreground)); } ``` ### Step 6: Handle `tailwind.config.js` Tailwind v4 is CSS-first — `tailwind.config.js` is no longer needed. Before deleting it, migrate any custom tokens (fonts, shadows, breakpoints, etc.) to `global.css`. ### Step 7: Create `react-native-css-env.d.ts` ```ts /// ``` ### Step 8: Re-add components ```bash npx gluestack-ui@alpha add button accordion modal # all components you use ``` When prompted to overwrite, select **Yes** to get NativeWind v5 versions. --- # UniWind Upgrade UniWind is an Expo-only Tailwind v4 styling engine. It processes styles natively on device without a PostCSS step and uses class-based theme toggling via `.dark` / `.light` selectors. > **UniWind is Expo-only.** It does not support Next.js or bare React Native CLI. ## What Changed | Area | Before (NativeWind v4) | After (UniWind) | | --- | --- | --- | | Styling engine | `nativewind@^4.1.23` | `uniwind@^1.3.0` | | Tailwind version | `tailwindcss@^3.x` | `tailwindcss@^4.1.18` | | metro.config.js | `withNativewind` | `withUniwindConfig` from `uniwind/metro` | | Theme tokens | `tailwind.config.js` | `global.css` with `.light {}` / `.dark {}` | | babel.config.js | `nativewind/babel` plugin | Remove `nativewind/babel` | | Type declarations | `nativewind-env.d.ts` | `uniwind-types.d.ts` | ## Automated CLI Upgrade ```bash npx gluestack-ui@alpha upgrade ``` Select **UniWind (Tailwind CSS v4, Expo-only)**. The CLI will: 1. Replace `nativewind` with `uniwind`, upgrade `tailwindcss` to v4 2. Rewrite `global.css` with UniWind `.light {}` / `.dark {}` theme format 3. Update `metro.config.js` to use `withUniwindConfig` 4. Remove `nativewind/babel` from `babel.config.js` 5. Delete `tailwind.config.js` 6. Replace `nativewind-env.d.ts` with `uniwind-types.d.ts` > **After upgrading:** Commit your changes, then re-add components: > ```bash > npx gluestack-ui@alpha add button accordion modal # all components you use > ``` ## Manual Migration to UniWind ### Step 1: Install packages **npm:** ```bash npm install uniwind@^1.3.0 @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 npm install --save-dev tailwindcss@^4.1.18 npm uninstall nativewind ``` **yarn:** ```bash yarn add uniwind@^1.3.0 @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 yarn add --dev tailwindcss@^4.1.18 yarn remove nativewind ``` ### Step 2: Update `metro.config.js` ```js const { getDefaultConfig } = require("expo/metro-config"); const { withUniwindConfig } = require("uniwind/metro"); const config = getDefaultConfig(__dirname); module.exports = withUniwindConfig(config, { cssEntryFile: "./global.css", themes: ["light", "dark"], }); ``` ### Step 3: Rewrite `global.css` UniWind uses top-level `.light {}` and `.dark {}` class selectors (not nested inside `:root`): ```css @import "tailwindcss"; @layer theme { .light { --primary: 23 23 23; --primary-foreground: 250 250 250; --card: 255 255 255; --secondary: 245 245 245; --secondary-foreground: 23 23 23; --background: 255 255 255; --popover: 255 255 255; --popover-foreground: 10 10 10; --muted: 245 245 245; --muted-foreground: 115 115 115; --destructive: 231 0 11; --foreground: 10 10 10; --border: 229 229 229; --input: 229 229 229; --ring: 212 212 212; --accent: 247 247 247; --accent-foreground: 52 52 52; } @media (prefers-color-scheme: light) { :root:not(:where(.light, .light *, .dark, .dark *)) { --primary: 23 23 23; --primary-foreground: 250 250 250; --card: 255 255 255; --secondary: 245 245 245; --secondary-foreground: 23 23 23; --background: 255 255 255; --popover: 255 255 255; --popover-foreground: 10 10 10; --muted: 245 245 245; --muted-foreground: 115 115 115; --destructive: 231 0 11; --foreground: 10 10 10; --border: 229 229 229; --input: 229 229 229; --ring: 212 212 212; --accent: 247 247 247; --accent-foreground: 52 52 52; } } .dark { --primary: 255 245 245; --primary-foreground: 23 23 23; --card: 23 23 23; --secondary: 38 38 38; --secondary-foreground: 250 250 250; --background: 10 10 10; --popover: 23 23 23; --popover-foreground: 250 250 250; --muted: 38 38 38; --muted-foreground: 161 161 161; --destructive: 255 100 103; --foreground: 250 250 250; --border: 46 46 46; --input: 46 46 46; --ring: 115 115 115; --accent: 38 38 38; --accent-foreground: 250 250 250; } @media (prefers-color-scheme: dark) { :root:not(:where(.light, .light *, .dark, .dark *)) { --primary: 255 245 245; --primary-foreground: 23 23 23; --card: 23 23 23; --secondary: 38 38 38; --secondary-foreground: 250 250 250; --background: 10 10 10; --popover: 23 23 23; --popover-foreground: 250 250 250; --muted: 38 38 38; --muted-foreground: 161 161 161; --destructive: 255 100 103; --foreground: 250 250 250; --border: 46 46 46; --input: 46 46 46; --ring: 115 115 115; --accent: 38 38 38; --accent-foreground: 250 250 250; } } } @theme inline { --color-primary: rgb(var(--primary)); --color-primary-foreground: rgb(var(--primary-foreground)); --color-card: rgb(var(--card)); --color-secondary: rgb(var(--secondary)); --color-secondary-foreground: rgb(var(--secondary-foreground)); --color-background: rgb(var(--background)); --color-popover: rgb(var(--popover)); --color-popover-foreground: rgb(var(--popover-foreground)); --color-muted: rgb(var(--muted)); --color-muted-foreground: rgb(var(--muted-foreground)); --color-destructive: rgb(var(--destructive)); --color-foreground: rgb(var(--foreground)); --color-border: rgb(var(--border)); --color-input: rgb(var(--input)); --color-ring: rgb(var(--ring)); --color-accent: rgb(var(--accent)); --color-accent-foreground: rgb(var(--accent-foreground)); } ``` > **Why `.dark {}` at the top level?** UniWind's CSS visitor transforms `&:where(.dark, .dark *)` selectors into `.dark {}` — this must be at the top level of `@layer theme`, not nested inside `:root {}`. Nesting would create `:root .dark {}` (descendant selector) which would not match `html.dark`. ### Step 4: Remove `nativewind/babel` from `babel.config.js` Remove the `nativewind/babel` entry from plugins and presets: ```js // Before module.exports = { presets: ['babel-preset-expo'], plugins: ['nativewind/babel'], // ← remove this }; // After module.exports = { presets: ['babel-preset-expo'], plugins: [], }; ``` ### Step 5: Update type declarations Remove `nativewind-env.d.ts` and create `uniwind-types.d.ts`: ```ts /// ``` ### Step 6: Delete `tailwind.config.js` Tailwind v4 is CSS-first — `tailwind.config.js` is not used by UniWind. Migrate any custom tokens to `global.css` first. ### Step 7: Re-add components ```bash npx gluestack-ui@alpha add button accordion modal # all components you use ``` When prompted to overwrite, select **Yes** to get UniWind versions. --- ## Support If you encounter issues during migration: - Check the [gluestack-ui documentation](https://gluestack.io/ui/docs) - Join our [Discord community](https://discord.gg/gluestack) for help - Report bugs on [GitHub](https://github.com/gluestack/gluestack-ui/issues) --- ## LinearGradient URL: /ui/docs/guides/recipes/linear-gradient/index # LinearGradient Learn how to use LinearGradient components in React Native for stunning UI effects. Create smooth color transitions with Linear Gradient for beautiful app designs. This is an illustration of **Linear Gradient** component in `App.tsx` file. ```jsx export default function App() { const [colorMode, setColorMode] = (useState < 'dark') | ('light' > 'light'); return (
Stay up to date Subscribe to our newsletter for the latest news and product updates. Subscribe
); } ``` ## Installation **Expo Linear Gradient:** ### Step 1: Install the following dependencies: ```bash npx expo install expo-linear-gradient ``` ### Step 2: Copy and paste the following code into your project. ```jsx 'use client'; const StyledExpoLinearGradient = styled(ExpoLinearGradient, { className: 'style' }); const linearGradientStyle = tva({ base: '', }); export const LinearGradient = React.forwardRef( ({ className, ...props }: any, ref?: any) => { return ( ); } ); ``` ### Step 3: Update the import paths to match your project setup. **React Native Linear Gradient:** ### Step 1: Install the following dependencies: ```bash npm i react-native-linear-gradient ``` ### Step 2: Copy and paste the following code into your project. ```jsx 'use client'; //@ts-ignore const StyledRNLinearGradient = styled(RNLinearGradient, { className: 'style' }); const linearGradientStyle = tva({ base: '', }); export const LinearGradient = React.forwardRef( ({ className, ...props }: any, ref?: any) => { return ( ); } ); ``` ### 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 () => ; ``` --- ## Learn about gluestack from YouTube guru notJust.dev URL: /ui/docs/guides/tutorials/building-ecommerce-app/index # Learn about gluestack from YouTube guru [notJust.dev](https://www.youtube.com/@notjustdev) Vadim, popularly known as "not-just-dev" on YouTube, offers insightful and practical videos that explore a wide range of developer tools and frameworks, including gluestack-ui. --- ## Accessibility URL: /ui/docs/home/core-concepts/accessibility/index # Accessibility Accessibility is an important aspect of web and native development, and at gluestack-ui, we strive to make our components accessible to all users. In this section, we will discuss the accessibility features of our components. ## WAI-ARIA Guidelines gluestack-ui follows the WAI-ARIA Authoring Practices guidelines to ensure our components are accessible to users of assistive technologies. The WAI-ARIA Authoring Practices document provides guidance on how to create accessible widgets, navigation, and behaviours using WAI-ARIA roles, states, and properties. ## Browser and Assistive Technology Testing We test our components in a wide selection of modern browsers and commonly used assistive technologies to ensure accessibility. Our testing includes screen readers such as NVDA and JAWS, as well as keyboard navigation and other assistive technologies. ## Accessible Component Props gluestack-ui components come with built-in accessibility features such as keyboard navigation and support for screen readers. We provide additional accessibility props for our components, such as aria-label, aria-labelledby, aria-describedby, role, tabIndex, and more. ## Keyboard Accessibility All components are fully navigable and operable using a keyboard, providing an optimal experience for users who cannot or prefer not to use a mouse or touch screen. ## Focus Management We also provide focus management for our components to ensure a smooth and intuitive experience for keyboard users. When a component gains focus, we ensure that it is visually highlighted and that the focus order is consistent with the DOM order. ## Screen Reader Accessibility All components include appropriate ARIA roles, states, and properties to ensure proper communication with screen readers and other assistive technologies. We are committed to making our components accessible to all users, and we will continue to improve our accessibility features as we receive feedback from the community. --- ## Universal URL: /ui/docs/home/core-concepts/universal/index # Universal All `gluestack-ui` components are universal out of the box, eliminating the need for separate development for both web and mobile platforms, which is the traditional approach. With `gluestack-ui`, developers can now craft beautiful interfaces with a single set of code that seamlessly operates across all devices, removing the need for the tedious and time-consuming process of crafting and maintaining two separate codebase for distinct platforms. You can also use `gluestack-ui` to create styles specific to a particular platform such as Web, iOS or Android. using `web:`, `ios:` and `android:` platform modifiers respectively. ```jsx function App { return ( ) } ``` ## Component Showcase Discover how gluestack-ui delivers a smooth and consistent user experience across various device types. You can view live example [here](https://ui-kitchensink.gluestack.io/). --- ## CLI URL: /ui/docs/home/getting-started/cli/index # CLI The gluestack-ui CLI is a tool that simplifies integrating the gluestack-ui into your project. It provides commands for initialising and adding components to your project. > Installation using gluestack-ui CLI in Expo projects supports for Expo SDK 50 and above. For Expo SDK < 49, please refer to the manual installation guide [here](https://gluestack.io/ui/docs/home/getting-started/installation). - Currently we are working on support for Windows OS. For now, we recommend using the manual installation guide to avoid any issues. ## Init Use init command to set up your project. The init command adds the necessary `gluestack-ui.config.json` file, includes the `GluestackUIProvider` and essential components (`icon`, `overlay`, `toast`), installs dependencies, and configures above files for your project. - `metro.config.js` - `babel.config.js` - `next.config.js` ( for Next.js) - `tailwind.config.js` - `global.css` (for Tailwind CSS) - `ts.config.json` - entry file (for your project) ```bash npx gluestack-ui init ``` > Note: If you wish to make changes to your project manually, you can refer to the [installation](/ui/docs/home/getting-started/installation) guide. ### Monorepo Example Here's a complete workflow for setting up gluestack-ui in a monorepo: ```bash # Navigate to your monorepo root cd my-monorepo # Initialize gluestack-ui in monorepo mode npx gluestack-ui init --monorepo --path packages/ui # The CLI will: # 1. Prompt you to select a styling engine (NativeWind v5, UniWind, etc.) # 2. Create GluestackUIProvider in packages/ui # 3. Generate gluestack-ui.config.json at repo root: # { # "app": { # "components": "packages/ui" # } # } # 4. Install dependencies and update config files # Later, add components npx gluestack-ui add button --monorepo # Or add all components npx gluestack-ui add --all --monorepo ``` For non-interactive environments (CI/CD): ```bash npx gluestack-ui init --monorepo --path packages/ui --nativewind-v5 -y npx gluestack-ui add button --monorepo -y ``` ### Options: - `--path `: Specifies the directory path where you want to add the GluestackUIProvider component. By default, it creates the component in the `components/ui` directory within your project's root. - `--monorepo`: When used in monorepo repositories, this flag switches the init flow to monorepo mode. In monorepo mode the CLI will treat the project as a shared component library (projectType: library) and will: - Prompt to select or enter the components path where GluestackUIProvider and components should be written (e.g. `packages/ui`, `packages/components`, `apps/ui`). - Generate a top-level `gluestack-ui.config.json` that points `app.components` to the chosen components path (relative to repo root). - Add provider and core components into the chosen components path and update configs (tailwind, metro, tsconfig) appropriately. - `--use-`: This flag specifies the package manager to use when initializing gluestack-ui. Supported options are npm (default), yarn, bun, and pnpm. Replace `` with your preferred package manager. - `--nativewind`: Use NativeWind v4 (Tailwind CSS v3) as the styling engine. Useful for projects that need to stay on Tailwind v3. - `--nativewind-v5`: Use NativeWind v5 (Tailwind CSS v4) as the styling engine. Note: NativeWind v5 is not yet web-compatible in monorepo mode; the CLI will fall back to NativeWind v4 in monorepo setups. - `--uniwind`: Use UniWind (Tailwind CSS v4, Expo-only) as the styling engine. Note: UniWind is not supported for monorepo Next.js setups. In monorepo mode the CLI will fall back to NativeWind v4. - `-y, --yes`: Answer yes to all prompts. Use this for non-interactive environments like CI/CD pipelines. ```bash Usage: npx gluestack-ui init [options] \n \n initialize your gluestack-ui and install dependencies \n \n Options: \n --path Path to add GluestackUIProvider, defaults to components/ui in the current directory. \n --monorepo Run init in monorepo/library mode and interactively choose components path. \n --use-npm Use npm as the package manager (default) \n --use-yarn Use yarn as the package manager \n --use-bun Use bun as the package manager \n --use-pnpm Use pnpm as the package manager \n --nativewind Use NativeWind v4 (Tailwind CSS v3) \n --nativewind-v5 Use NativeWind v5 (Tailwind CSS v4) \n --uniwind Use UniWind (Tailwind CSS v4, Expo-only) \n -y, --yes Answer yes to all prompts (non-interactive mode) ``` ## Add Use the `add` command to add components and their dependencies to your project: ```bash npx gluestack-ui add [component] ``` ### Options: - `--all`: This flag can be used to add all available components from gluestack-ui to your project. - `--path `: Specifies the directory path where you want to add the component. By default, it will add the component to the same directory where the GluestackUIProvider is located. - `--monorepo`: When adding components into a monorepo, use --monorepo to enable monorepo-aware flows. With --monorepo the CLI will: - Prompt you to select or enter the components path (e.g. `packages/ui`) if not provided via `--path`. - Validate and write `gluestack-ui.config.json` at the repo root pointing to the selected components path. - Ensure components are added into the chosen path rather than the repo root. - `--use-`: Specifies the package manager to use. Supported options are npm (default), yarn, bun, and pnpm. Replace `` with your preferred package manager. - `-y, --yes`: Answer yes to all prompts. Use this for non-interactive environments like CI/CD pipelines. ```bash Usage: npx gluestack-ui add [component] [options] \n \n add component to your project \n \n Options: \n --all Adds all existing components from gluestack-ui \n --path Path to add components, defaults to the directory where GluestackUIProvider is located. \n --monorepo Run add in monorepo mode and interactively choose components path. \n --use-npm Set npm as the project's package manager, default \n --use-yarn Set yarn as the project's package manager \n --use-bun Set bun as the project's package manager \n --use-pnpm Set pnpm as the project's package manager, currently not supported in react-native-cli projects. \n -y, --yes Answer yes to all prompts (non-interactive mode) ``` ## Upgrade Use the `upgrade` command to migrate your project's styling engine. ```bash npx gluestack-ui upgrade ``` The upgrade command auto-detects your current gluestack-ui version and presents a prompt asking which styling engine you want to upgrade to: ``` ◆ Which styling engine would you like to upgrade to? │ ● NativeWind v5 (Tailwind CSS v4) │ ○ UniWind (Tailwind CSS v4, Expo-only) │ ○ Stay on current version ``` ### NativeWind v5 upgrade path Migrates from NativeWind v4 (Tailwind v3) to NativeWind v5 (Tailwind v4). The command will automatically: 1. Pin `lightningcss@1.30.1` in `package.json` overrides/resolutions 2. Replace `nativewind@^4.x` with `nativewind@^5.0.0-preview.2` and add `react-native-css` 3. Upgrade `tailwindcss` to v4 and add `@tailwindcss/postcss` 4. Rewrite `global.css` to Tailwind v4 format (with theme tokens) 5. Create `postcss.config.js` 6. Add `withNativewind` to `metro.config.js` (preserves existing options) 7. Prompt whether to delete `tailwind.config.js` (never auto-deleted — may contain custom tokens) 8. Create `react-native-css-env.d.ts` > **After upgrading:** Re-add your components using `npx gluestack-ui@alpha add ` to get the NativeWind v5 versions. Commit your changes first — re-adding will overwrite existing component files. ### UniWind upgrade path Migrates from NativeWind v4 to UniWind (Tailwind v4, Expo-only). The command will automatically: 1. Replace `nativewind` with `uniwind` 2. Upgrade `tailwindcss` to v4 3. Rewrite `global.css` to UniWind / Tailwind v4 format 4. Update `metro.config.js` to use `withUniwindConfig` 5. Remove `nativewind/babel` entries from `babel.config.js` 6. Remove `tailwind.config.js` (Tailwind v4 is CSS-first) 7. Remove `nativewind-env.d.ts` and create `uniwind-types.d.ts` > **After upgrading:** Re-add your components using `npx gluestack-ui@alpha add ` to get the UniWind versions. Commit your changes first. > **Note:** The upgrade command is not supported for Next.js projects. ## gluestack-ui.config.json Configuration for your project. The `gluestack-ui.config.json` file holds the configuration for your project. It helps the CLI understand how your project is set up and add components accordingly. > Note : The gluestack-ui.config.json file is optional. It is only required if you use the CLI to add components to your project. If you prefer to manually copy and paste components, this file is not needed. Manual creation of gluestack-ui.config.json file is not necessary. It will be automatically generated upon executing the following command: ```bash npx gluestack-ui init ``` ### Monorepo Configuration When using the `--monorepo` flag with `init` or `add` commands, the CLI generates a minimal configuration file at the repository root containing only the `app.components` field: ```json { "app": { "components": "packages/ui" } } ``` This tells the CLI where to find and add components in your monorepo. Other configuration fields like `tailwind.config`, `tailwind.css`, and `app.entry` are not automatically generated in monorepo mode and should be added manually if needed. ## tailwind ### tailwind.config Path to where your `tailwind.config.js` file is located. ```bash { "tailwind": { "config": "tailwind.config.js", } } ``` ### tailwind.css Path to the CSS file (`global.css`) where Tailwind styles are likely imported into your project. ```bash { "tailwind": { "css": "global.css" } } ``` ## app ### app.entry Path to your project's entry file ```bash { "app": { "entry": "app/_layout.tsx", } } ``` ### app.components Specifies the directory path where the `GluestackUIProvider` component is located. ```bash { "app": { "components": "components/ui", } } ``` --- ## Figma UI Kit URL: /ui/docs/home/getting-started/figma-ui-kit/index # Figma UI Kit The [Figma UI Kit](https://www.figma.com/community/file/1577667149474894602) provides a collection of ready-to-use UI components from the gluestack-ui library. So you can directly use these components in Figma and design your app. The developers won't have a chance to say this is not possible! ## What is included? Discover the comprehensive features of our latest Figma UI Kit, designed to enhance your design workflow with consistency, efficiency, and customization. Here's a breakdown of what you can expect: - **Color Tokens**: Color tokens in Figma provide a systematic way to manage and use colors in your design projects. They ensure consistency and simplify updates by defining a centralized set of color values reusable throughout the project. Our kit includes an extended color palette added as styles, semantic colors as variable collections, and a set of fixed colors that remain unchanged across modes. - **Text Tokens**: Text tokens in Figma enable you to define and manage consistent text properties like font, size, line spacing, and color. Our Figma UI Kit features two main foundation components, **Text** and **Heading**, with various sizes and font weights. You can easily customize these styles and typography properties to align with your brand. - **Shadow Tokens**: Shadow tokens, also known as effect styles, allow you to define and manage effects such as drop shadows, strokes, blurs, and images. Our kit includes two primary shadows, **Hard** and **Soft**, covering different light source directions and intensities. - **Components**: Components in Figma are reusable design elements that ensure consistent and efficient designs. They can represent UI elements, icons, buttons, or any visual elements you want to reuse. Our Figma UI Kit includes around 23 components with instances mapped in compound components, enabling you to design screens effortlessly with common primitive components. - **Additional Examples**: We've added more components and their use cases in the latest version. These examples demonstrate how components can be customized to meet user needs. ## Incorporated Features - The Figma kit includes all the latest Figma features, making it an excellent starting point for any design system. The gluestack kit leverages the following advanced features to enhance your design process and streamline your workflow. ### AutoLayout Support Figma's AutoLayout feature allows for the creation of responsive and flexible designs by automatically adjusting the layout and spacing of elements within frames. This ensures that designs adapt seamlessly to various screen sizes and content changes. All components in our file are equipped with AutoLayout, enabling consistent design across mobile, tablet, and desktop devices. ### Variable Support The latest variable feature in Figma simplifies the process of switching between light and dark modes. With just one click, you can toggle between these modes, ensuring that designs are optimized for different viewing preferences. ## Taking It One Step Further We like to elevate our offerings, and with the v2 release, users will benefit from the following enhancements: - **Detailed Documentation**: Each component comes with a spec sheet, providing comprehensive information on structuring, properties, dos and don'ts, and use cases. This documentation is invaluable for anyone seeking an in-depth understanding of our design process. - **gluestack Plugin**: Customization in Figma can often be a manual and time-consuming task. To streamline this, we've introduced the [gluestack plugin](https://www.figma.com/community/plugin/1304000704678516266/gluestack), now available in the Figma Community. This plugin simplifies the process of linking text styles and updating font families, making customization more efficient and less tedious. --- ## @gluestack-ui/utils URL: /ui/docs/home/getting-started/gluestack-ui-nativewind-utils/index # @gluestack-ui/utils [@gluestack-ui/utils](https://www.npmjs.com/package/@gluestack-ui/utils) provides a collection of utility functions for seamless integration of gluestack-ui and nativewind. ### tva (Tailwind Variant Authority) The `tva` function serves as a wrapper around [Tailwind Variant](https://www.tailwind-variants.org/), extending its functionality. In addition to the default Tailwind variant, it introduces support for `parentVariant` and `parentCompoundVariant`. ### VariantProps Utility The `VariantProps` utility allows for easy extraction of variants from a component. ### flush This function facilitates server-side rendering (SSR) by flushing out any styles generated during the process. This ensures that these styles are properly added to the HTML document. ### withStates `withStates` is a Higher Order Component (HOC) designed to incorporate state-based styling into gluestack-ui components on native devices. ### withStyleContext `withStyleContext` is a HOC that creates a React context with a defined scope. ### withStyleContextAndStates This single HOC combines the functionality of both `withStyleContext` and `withStates`. ### useStyleContext `useStyleContext` is a hook used to consume the context created by `withStyleContext` and `withStyleContextAndStates`. --- ## Installation URL: /ui/docs/home/getting-started/installation/index # Installation **CLI:** The fastest way to get started. The CLI detects your project type, prompts you to choose a styling engine, and automatically configures `GluestackUIProvider`, `metro.config.js`, `babel.config.js`, `global.css`, and your app entry file. ### Prerequisites | Package | Supported Versions | | --- | --- | | `expo` | >= 50 | | `react-native` | >= 0.72.5 | | `node` | > 16 | > **Note:** gluestack-ui v5 does not currently support Next.js. Next.js support will be available as soon as NativeWind v5 adds web support. For Next.js projects, refer to the [v4 documentation](https://v4.gluestack.io). ### Step 1: Initialize Run the following command at the root of your project: ```bash npx gluestack-ui init ``` The CLI prompts you to select a styling engine: - **NativeWind v5** — Expo & React Native CLI, powered by Tailwind CSS v4 + PostCSS - **UniWind** — Expo-only, powered by Tailwind CSS v4 with no PostCSS build step ### Step 2: Add components ```bash npx gluestack-ui add button ``` See the [CLI guide](/ui/docs/home/getting-started/cli) for all available commands and options. **Manual:** Uses **NativeWind v5** with **Tailwind CSS v4** (CSS-first). Theme tokens live in `global.css` — no `tailwind.config.js` needed. ### Step 1: Pin `lightningcss` in `package.json` Add this **before** installing packages to avoid build errors caused by version conflicts: ```json { "overrides": { "lightningcss": "1.30.1" }, "resolutions": { "lightningcss": "1.30.1" } } ``` ### Step 2: Install dependencies ```bash npm i @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 nativewind@^5.0.0-preview.2 react-native-css@^3.0.4 react-native-safe-area-context@^5.6.1 react-aria@^3.45.0 @expo/html-elements@^0.12.5 tailwind-variants@^0.1.20 @legendapp/motion@^2.4.0 react-native-svg@^15.15.3 react-stately@^3.39.0 react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 ``` ```bash npm i --save-dev tailwindcss@^4.2.0 @tailwindcss/postcss@^4.2.0 ``` **yarn:** ```bash yarn add @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 nativewind@^5.0.0-preview.2 react-native-css@^3.0.4 react-native-safe-area-context@^5.6.1 react-aria@^3.45.0 @expo/html-elements@^0.12.5 tailwind-variants@^0.1.20 @legendapp/motion@^2.4.0 react-native-svg@^15.15.3 react-stately@^3.39.0 react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 ``` ```bash yarn add --dev tailwindcss@^4.2.0 @tailwindcss/postcss@^4.2.0 ``` > Use `--legacy-peer-deps` if you encounter peer dependency resolution issues. ### Step 3: Configure `babel.config.js` > NativeWind v5 uses PostCSS for style processing — do **not** add `nativewind/babel`. ```js module.exports = function (api) { api.cache(true); return { presets: ['babel-preset-expo'], plugins: ['react-native-worklets/plugin'], }; }; ``` ### Step 4: Create `postcss.config.js` ```js export default { plugins: { '@tailwindcss/postcss': {}, }, }; ``` ### Step 5: Configure `metro.config.js` ```js const { getDefaultConfig } = require('expo/metro-config'); const { withNativewind } = require('nativewind/metro'); const config = getDefaultConfig(__dirname); module.exports = withNativewind(config); ``` ### Step 6: Create `global.css` Theme tokens are defined here instead of `tailwind.config.js`: ```css @import "tailwindcss/theme.css" layer(theme); @import "tailwindcss/preflight.css" layer(base); @import "tailwindcss/utilities.css"; @import "nativewind/theme"; @layer theme { :root { --primary: 23 23 23; --primary-foreground: 250 250 250; --card: 255 255 255; --secondary: 245 245 245; --secondary-foreground: 23 23 23; --background: 255 255 255; --popover: 255 255 255; --popover-foreground: 10 10 10; --muted: 245 245 245; --muted-foreground: 115 115 115; --destructive: 231 0 11; --foreground: 10 10 10; --border: 229 229 229; --input: 229 229 229; --ring: 212 212 212; --accent: 247 247 247; --accent-foreground: 52 52 52; } @media (prefers-color-scheme: dark) { :root { --primary: 255 245 245; --primary-foreground: 23 23 23; --card: 23 23 23; --secondary: 38 38 38; --secondary-foreground: 250 250 250; --background: 10 10 10; --popover: 23 23 23; --popover-foreground: 250 250 250; --muted: 38 38 38; --muted-foreground: 161 161 161; --destructive: 255 100 103; --foreground: 250 250 250; --border: 46 46 46; --input: 46 46 46; --ring: 115 115 115; --accent: 38 38 38; --accent-foreground: 250 250 250; } } /* Web: class-based toggle overrides system preference */ :root.dark { --primary: 255 245 245; --primary-foreground: 23 23 23; --card: 23 23 23; --secondary: 38 38 38; --secondary-foreground: 250 250 250; --background: 10 10 10; --popover: 23 23 23; --popover-foreground: 250 250 250; --muted: 38 38 38; --muted-foreground: 161 161 161; --destructive: 255 100 103; --foreground: 250 250 250; --border: 46 46 46; --input: 46 46 46; --ring: 115 115 115; --accent: 38 38 38; --accent-foreground: 250 250 250; } :root.light { --primary: 23 23 23; --primary-foreground: 250 250 250; --card: 255 255 255; --secondary: 245 245 245; --secondary-foreground: 23 23 23; --background: 255 255 255; --popover: 255 255 255; --popover-foreground: 10 10 10; --muted: 245 245 245; --muted-foreground: 115 115 115; --destructive: 231 0 11; --foreground: 10 10 10; --border: 229 229 229; --input: 229 229 229; --ring: 212 212 212; --accent: 247 247 247; --accent-foreground: 52 52 52; } } @theme inline { --color-primary: rgb(var(--primary)); --color-primary-foreground: rgb(var(--primary-foreground)); --color-card: rgb(var(--card)); --color-secondary: rgb(var(--secondary)); --color-secondary-foreground: rgb(var(--secondary-foreground)); --color-background: rgb(var(--background)); --color-popover: rgb(var(--popover)); --color-popover-foreground: rgb(var(--popover-foreground)); --color-muted: rgb(var(--muted)); --color-muted-foreground: rgb(var(--muted-foreground)); --color-destructive: rgb(var(--destructive)); --color-foreground: rgb(var(--foreground)); --color-border: rgb(var(--border)); --color-input: rgb(var(--input)); --color-ring: rgb(var(--ring)); --color-accent: rgb(var(--accent)); --color-accent-foreground: rgb(var(--accent-foreground)); } ``` ### Step 7: Create `react-native-css-env.d.ts` ```ts /// ``` ### Step 8: Add path alias in `tsconfig.json` ```json { "compilerOptions": { "paths": { "@/*": ["./*"] } } } ``` ### Step 9: Copy `GluestackUIProvider` Copy the contents of the into `components/ui/gluestack-ui-provider/` in your project. ### Step 10: Wrap your app Import `global.css` and wrap your root layout with `GluestackUIProvider`: ```tsx // app/_layout.tsx export default function RootLayout({ children }: { children: React.ReactNode }) { return {children}; } ``` ### Step 11: Add components ```bash npx gluestack-ui add button ``` Uses **UniWind** with **Tailwind CSS v4** (Expo-only). Styles are processed on-device — no PostCSS build step required. Theme tokens live in `global.css` via `.light {}` / `.dark {}` class selectors — no `tailwind.config.js` needed. ### Step 1: Install dependencies **npm:** ```bash npm i @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 uniwind@^1.4.0 react-native-safe-area-context@^5.6.1 react-aria@^3.45.0 @expo/html-elements@^0.12.5 tailwind-variants@^0.1.20 @legendapp/motion@^2.5.3 react-native-svg@^15.13.0 react-stately@^3.39.0 react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 ``` ```bash npm i --save-dev tailwindcss@^4.1.18 ``` **yarn:** ```bash yarn add @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 uniwind@^1.4.0 react-native-safe-area-context@^5.6.1 react-aria@^3.45.0 @expo/html-elements@^0.12.5 tailwind-variants@^0.1.20 @legendapp/motion@^2.5.3 react-native-svg@^15.13.0 react-stately@^3.39.0 react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 ``` ```bash yarn add --dev tailwindcss@^4.1.18 ``` > Use `--legacy-peer-deps` if you encounter peer dependency resolution issues. ### Step 2: Configure `babel.config.js` > UniWind does **not** use `nativewind/babel`. ```js module.exports = function (api) { api.cache(true); return { presets: ['babel-preset-expo'], plugins: [ ['module-resolver', { root: ['./'], alias: { '@': './' } }], 'react-native-worklets/plugin', ], }; }; ``` ### Step 3: Configure `metro.config.js` ```js const { getDefaultConfig } = require('expo/metro-config'); const { withUniwindConfig } = require('uniwind/metro'); const config = getDefaultConfig(__dirname); module.exports = withUniwindConfig(config, { cssEntryFile: './global.css', themes: ['light', 'dark'], }); ``` ### Step 4: Create `global.css` UniWind requires `.light {}` and `.dark {}` at the **top level** of `@layer theme` — not nested inside `:root`: ```css @import "tailwindcss"; @layer theme { .light { --primary: 23 23 23; --primary-foreground: 250 250 250; --card: 255 255 255; --secondary: 245 245 245; --secondary-foreground: 23 23 23; --background: 255 255 255; --popover: 255 255 255; --popover-foreground: 10 10 10; --muted: 245 245 245; --muted-foreground: 115 115 115; --destructive: 231 0 11; --foreground: 10 10 10; --border: 229 229 229; --input: 229 229 229; --ring: 212 212 212; --accent: 247 247 247; --accent-foreground: 52 52 52; } @media (prefers-color-scheme: light) { :root:not(:where(.light, .light *, .dark, .dark *)) { --primary: 23 23 23; --primary-foreground: 250 250 250; --card: 255 255 255; --secondary: 245 245 245; --secondary-foreground: 23 23 23; --background: 255 255 255; --popover: 255 255 255; --popover-foreground: 10 10 10; --muted: 245 245 245; --muted-foreground: 115 115 115; --destructive: 231 0 11; --foreground: 10 10 10; --border: 229 229 229; --input: 229 229 229; --ring: 212 212 212; --accent: 247 247 247; --accent-foreground: 52 52 52; } } .dark { --primary: 255 245 245; --primary-foreground: 23 23 23; --card: 23 23 23; --secondary: 38 38 38; --secondary-foreground: 250 250 250; --background: 10 10 10; --popover: 23 23 23; --popover-foreground: 250 250 250; --muted: 38 38 38; --muted-foreground: 161 161 161; --destructive: 255 100 103; --foreground: 250 250 250; --border: 46 46 46; --input: 46 46 46; --ring: 115 115 115; --accent: 38 38 38; --accent-foreground: 250 250 250; } @media (prefers-color-scheme: dark) { :root:not(:where(.light, .light *, .dark, .dark *)) { --primary: 255 245 245; --primary-foreground: 23 23 23; --card: 23 23 23; --secondary: 38 38 38; --secondary-foreground: 250 250 250; --background: 10 10 10; --popover: 23 23 23; --popover-foreground: 250 250 250; --muted: 38 38 38; --muted-foreground: 161 161 161; --destructive: 255 100 103; --foreground: 250 250 250; --border: 46 46 46; --input: 46 46 46; --ring: 115 115 115; --accent: 38 38 38; --accent-foreground: 250 250 250; } } } @theme inline { --color-primary: rgb(var(--primary)); --color-primary-foreground: rgb(var(--primary-foreground)); --color-card: rgb(var(--card)); --color-secondary: rgb(var(--secondary)); --color-secondary-foreground: rgb(var(--secondary-foreground)); --color-background: rgb(var(--background)); --color-popover: rgb(var(--popover)); --color-popover-foreground: rgb(var(--popover-foreground)); --color-muted: rgb(var(--muted)); --color-muted-foreground: rgb(var(--muted-foreground)); --color-destructive: rgb(var(--destructive)); --color-foreground: rgb(var(--foreground)); --color-border: rgb(var(--border)); --color-input: rgb(var(--input)); --color-ring: rgb(var(--ring)); --color-accent: rgb(var(--accent)); --color-accent-foreground: rgb(var(--accent-foreground)); } ``` ### Step 5: Create `uniwind-types.d.ts` ```ts /// ``` ### Step 6: Add path alias in `tsconfig.json` ```json { "compilerOptions": { "paths": { "@/*": ["./*"] } } } ``` ### Step 7: Copy `GluestackUIProvider` Copy the contents of the into `components/ui/gluestack-ui-provider/` in your project. ### Step 8: Wrap your app ```tsx // app/_layout.tsx export default function RootLayout({ children }: { children: React.ReactNode }) { return {children}; } ``` ### Step 9: Add components ```bash npx gluestack-ui add button ``` > gluestack-ui v5 does not currently support Next.js. Next.js support will be available as soon as NativeWind v5 adds web support. In the meantime, the steps below use **NativeWind v4** (Tailwind CSS v3). For the full v4 documentation see [v4.gluestack.io](https://v4.gluestack.io). ### Step 1: Install dependencies **npm:** ```bash npm i @gluestack-ui/core@alpha @gluestack-ui/utils@alpha nativewind@^4.1.23 @gluestack/ui-next-adapter@alpha react-native-web@^0.20.0 react-native-safe-area-context@^5.6.1 react-aria@^3.45.0 react-stately@^3.39.0 tailwind-variants@^0.1.20 @legendapp/motion@^2.4.0 react-native-svg@^15.12.0 react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 dom-helpers@^5.2.1 @expo/html-elements@^0.12.5 ``` ```bash npm i --save-dev tailwindcss@^3.4.17 autoprefixer@^10.4.21 postcss@^8.5.4 @types/react-native@0.72.8 @react-native/assets-registry@^0.79.3 ``` **yarn:** ```bash yarn add @gluestack-ui/core@alpha @gluestack-ui/utils@alpha nativewind@^4.1.23 @gluestack/ui-next-adapter@alpha react-native-web@^0.20.0 react-native-safe-area-context@^5.6.1 react-aria@^3.45.0 react-stately@^3.39.0 tailwind-variants@^0.1.20 @legendapp/motion@^2.4.0 react-native-svg@^15.12.0 react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 dom-helpers@^5.2.1 @expo/html-elements@^0.12.5 ``` ```bash yarn add --dev tailwindcss@^3.4.17 autoprefixer@^10.4.21 postcss@^8.5.4 @types/react-native@0.72.8 @react-native/assets-registry@^0.79.3 ``` > Use `--legacy-peer-deps` if you encounter peer dependency resolution issues. ### Step 2: Configure `tailwind.config.js` ```js /** @type {import('tailwindcss').Config} */ module.exports = { darkMode: process.env.DARK_MODE ? process.env.DARK_MODE : 'media', content: [ './app/**/*.{html,js,jsx,ts,tsx,mdx}', './components/**/*.{html,js,jsx,ts,tsx,mdx}', './utils/**/*.{html,js,jsx,ts,tsx,mdx}', ], presets: [require('nativewind/preset')], important: 'html', safelist: [ { pattern: /(bg|border|text|stroke|fill)-(foreground|card|popover|muted|destructive|border|input|ring|white|chart|sidebar|primary|secondary|typography|background|accent)(\/\d+)?$/, }, { pattern: /(bg|border|text|stroke|fill)-(card|popover|muted|destructive|primary|secondary|accent|sidebar)-(foreground)(\/\d+)?$/, }, ], theme: { extend: { colors: { foreground: 'rgb(var(--foreground)/)', card: { DEFAULT: 'rgb(var(--card) / )', foreground: 'rgb(var(--card-foreground) / )', }, popover: { DEFAULT: 'rgb(var(--popover) / )', foreground: 'rgb(var(--popover-foreground) / )', }, muted: { DEFAULT: 'rgb(var(--muted) / )', foreground: 'rgb(var(--muted-foreground) / )', }, destructive: { DEFAULT: 'rgb(var(--destructive) / )', }, border: 'rgb(var(--border)/)', input: 'rgb(var(--input)/)', ring: 'rgb(var(--ring) / )', white: 'rgb(255 255 255)', // Chart colors chart: { 1: 'rgb(var(--chart-1) / )', 2: 'rgb(var(--chart-2) / )', 3: 'rgb(var(--chart-3) / )', 4: 'rgb(var(--chart-4) / )', 5: 'rgb(var(--chart-5) / )', }, // Sidebar colors sidebar: { 'DEFAULT': 'rgb(var(--sidebar) / )', 'foreground': 'rgb(var(--sidebar-foreground) / )', 'primary': 'rgb(var(--sidebar-primary) / )', 'primary-foreground': 'rgb(var(--sidebar-primary-foreground) / )', 'accent': 'rgb(var(--sidebar-accent) / )', 'accent-foreground': 'rgb(var(--sidebar-accent-foreground) / )', 'border': 'rgb(var(--sidebar-border))', 'ring': 'rgb(var(--sidebar-ring) / )', }, primary: { DEFAULT: 'rgb(var(--primary)/)', foreground: 'rgb(var(--primary-foreground)/)', }, secondary: { DEFAULT: 'rgb(var(--secondary)/)', foreground: 'rgb(var(--secondary-foreground)/)', }, typography: { white: '#FFFFFF', gray: '#D4D4D4', black: '#181718', }, background: { DEFAULT: 'rgb(var(--background)/)', }, accent: { DEFAULT: 'rgb(var(--accent)/)', foreground: 'rgb(var(--accent-foreground)/)', }, }, fontFamily: { heading: undefined, body: 'var(--font-sans)', mono: 'var(--font-mono)', sans: 'var(--font-sans)', serif: 'var(--font-serif)', inter: ['var(--font-inter)'], georgia: ['Georgia'], melno: ['Melno'], andika: [ 'Andika_400Regular', 'Andika_400Regular_Italic', 'Andika_700Bold', 'Andika_700Bold_Italic', ], outfit: [ 'Outfit_400Regular', 'Outfit_500Medium', 'Outfit_600SemiBold', 'Outfit_700Bold', 'Outfit_800ExtraBold', 'Outfit_900Black', ], }, fontWeight: { extrablack: '950', }, fontSize: { '2xs': '10px', }, boxShadow: { 'hard-1': '-2px 2px 8px 0px rgba(38, 38, 38, 0.20)', 'hard-2': '0px 3px 10px 0px rgba(38, 38, 38, 0.20)', 'hard-3': '2px 2px 8px 0px rgba(38, 38, 38, 0.20)', 'hard-4': '0px -3px 10px 0px rgba(38, 38, 38, 0.20)', 'hard-5': '0px 2px 10px 0px rgba(38, 38, 38, 0.10)', 'soft-1': '0px 0px 10px rgba(38, 38, 38, 0.1)', 'soft-2': '0px 0px 20px rgba(38, 38, 38, 0.2)', 'soft-3': '0px 0px 30px rgba(38, 38, 38, 0.1)', 'soft-4': '0px 0px 40px rgba(38, 38, 38, 0.1)', }, }, }, }; ``` ### Step 3: Add path alias in `tsconfig.json` ```json { "compilerOptions": { "paths": { "@/*": ["./*"] } } } ``` ### Step 4: Copy `GluestackUIProvider` Copy the contents of the into `components/ui/gluestack-ui-provider/` in your project. ### Step 5: Update `next.config.js` ```js /** @type {import('next').NextConfig} */ const nextConfig = { eslint: { ignoreDuringBuilds: true, }, typescript: { ignoreBuildErrors: true, }, transpilePackages: [], }; export default withGluestackUI(nextConfig); ``` ### Step 6: Set up SSR **App Router:** Create `app/registry.tsx`: ```tsx 'use client'; export default function StyledJsxRegistry({ children, }: { children: React.ReactNode; }) { // Only create stylesheet once with lazy initial state // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state const [jsxStyleRegistry] = useState(() => createStyleRegistry()); const isServerInserted = useRef(false); useServerInsertedHTML(() => { AppRegistry.registerComponent('Main', () => 'main'); const { getStyleElement } = AppRegistry.getApplication('Main'); if (!isServerInserted.current) { isServerInserted.current = true; const styles = [ getStyleElement(), jsxStyleRegistry.styles(), flush(), ].filter(Boolean); // Remove any null/undefined styles jsxStyleRegistry.flush(); // Add unique keys to each style element return ( <> {styles.map((style, index) => ( {style} ))} ); } }); return {children}; } ``` Wrap `children` in `app/layout.tsx`: ```tsx "use client"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` **Page Router:** Add registry to `pages/_document.tsx`: ```tsx function Document() { return (
); } Document.getInitialProps = async ({ renderPage }: any) => { AppRegistry.registerComponent('Main', () => Main); const { getStyleElement } = AppRegistry.getApplication('Main'); const page = await renderPage(); const styles = [getStyleElement(), flush()]; return { ...page, styles: React.Children.toArray(styles) }; }; export default Document; ``` Wrap your app in `pages/_app.tsx`: ```tsx export default function App({ Component, pageProps }: any) { return ( ); } ``` ### Step 7: Add components ```bash npx gluestack-ui add button ``` Uses **NativeWind v5** with **Tailwind CSS v4** (CSS-first). Theme tokens live in `global.css` — no `tailwind.config.js` needed. > UniWind is not supported for React Native CLI projects. ### Step 1: Pin `lightningcss` in `package.json` Add this **before** installing packages to avoid build errors: ```json { "overrides": { "lightningcss": "1.30.1" }, "resolutions": { "lightningcss": "1.30.1" } } ``` ### Step 2: Install dependencies **npm:** ```bash npm i @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 nativewind@^5.0.0-preview.2 react-native-css@^3.0.4 react-native-safe-area-context@^5.6.1 react-aria@^3.45.0 @expo/html-elements@^0.12.5 tailwind-variants@^0.1.20 @legendapp/motion@^2.4.0 react-native-svg@^15.15.3 react-stately@^3.39.0 react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 ``` ```bash npm i --save-dev tailwindcss@^4.2.0 @tailwindcss/postcss@^4.2.0 ``` **yarn:** ```bash yarn add @gluestack-ui/core@^5.0.0-alpha.0 @gluestack-ui/utils@^5.0.1-alpha.0 nativewind@^5.0.0-preview.2 react-native-css@^3.0.4 react-native-safe-area-context@^5.6.1 react-aria@^3.45.0 @expo/html-elements@^0.12.5 tailwind-variants@^0.1.20 @legendapp/motion@^2.4.0 react-native-svg@^15.15.3 react-stately@^3.39.0 react-native-reanimated@~4.2.1 react-native-worklets@^0.7.1 ``` ```bash yarn add --dev tailwindcss@^4.2.0 @tailwindcss/postcss@^4.2.0 ``` > Use `--legacy-peer-deps` if you encounter peer dependency resolution issues. ### Step 3: Configure `babel.config.js` > NativeWind v5 uses PostCSS for style processing — do **not** add `nativewind/babel`. ```js module.exports = { presets: ['@react-native/babel-preset'], plugins: ['react-native-worklets/plugin'], }; ``` ### Step 4: Create `postcss.config.js` ```js export default { plugins: { '@tailwindcss/postcss': {}, }, }; ``` ### Step 5: Configure `metro.config.js` ```js const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); const { withNativewind } = require('nativewind/metro'); const config = mergeConfig(getDefaultConfig(__dirname), {}); module.exports = withNativewind(config); ``` ### Step 6: Create `global.css` ```css @import "tailwindcss/theme.css" layer(theme); @import "tailwindcss/preflight.css" layer(base); @import "tailwindcss/utilities.css"; @import "nativewind/theme"; @layer theme { :root { --primary: 23 23 23; --primary-foreground: 250 250 250; --card: 255 255 255; --secondary: 245 245 245; --secondary-foreground: 23 23 23; --background: 255 255 255; --popover: 255 255 255; --popover-foreground: 10 10 10; --muted: 245 245 245; --muted-foreground: 115 115 115; --destructive: 231 0 11; --foreground: 10 10 10; --border: 229 229 229; --input: 229 229 229; --ring: 212 212 212; --accent: 247 247 247; --accent-foreground: 52 52 52; } @media (prefers-color-scheme: dark) { :root { --primary: 255 245 245; --primary-foreground: 23 23 23; --card: 23 23 23; --secondary: 38 38 38; --secondary-foreground: 250 250 250; --background: 10 10 10; --popover: 23 23 23; --popover-foreground: 250 250 250; --muted: 38 38 38; --muted-foreground: 161 161 161; --destructive: 255 100 103; --foreground: 250 250 250; --border: 46 46 46; --input: 46 46 46; --ring: 115 115 115; --accent: 38 38 38; --accent-foreground: 250 250 250; } } } @theme inline { --color-primary: rgb(var(--primary)); --color-primary-foreground: rgb(var(--primary-foreground)); --color-card: rgb(var(--card)); --color-secondary: rgb(var(--secondary)); --color-secondary-foreground: rgb(var(--secondary-foreground)); --color-background: rgb(var(--background)); --color-popover: rgb(var(--popover)); --color-popover-foreground: rgb(var(--popover-foreground)); --color-muted: rgb(var(--muted)); --color-muted-foreground: rgb(var(--muted-foreground)); --color-destructive: rgb(var(--destructive)); --color-foreground: rgb(var(--foreground)); --color-border: rgb(var(--border)); --color-input: rgb(var(--input)); --color-ring: rgb(var(--ring)); --color-accent: rgb(var(--accent)); --color-accent-foreground: rgb(var(--accent-foreground)); } ``` ### Step 7: Create `react-native-css-env.d.ts` ```ts /// ``` ### Step 8: Add path alias in `tsconfig.json` ```json { "compilerOptions": { "paths": { "@/*": ["./*"] } } } ``` ### Step 9: Copy `GluestackUIProvider` Copy the contents of the into `components/ui/gluestack-ui-provider/` in your project. ### Step 10: Wrap your app ```tsx // App.tsx export default function App() { return ( {/* your app */} ); } ``` ### Step 11: Run pod install (iOS) ```bash npx pod-install ``` ### Step 12: Add components ```bash npx gluestack-ui add button ``` ## Common Issues **Expo app stuck in `tailwindcss(ios) rebuilding...` while running `expo start`** Your project directory name may contain spaces (e.g. `Expo App`). Rename it to remove spaces (e.g. `Expo-App`). **Peer dependency resolution errors** Use the `--legacy-peer-deps` flag: ```bash npm install --legacy-peer-deps ``` **NativeWind v5 build errors related to lightningcss** Ensure `lightningcss` is pinned to `1.30.1` in both `overrides` and `resolutions` in `package.json`, then run a clean install. **UniWind theme tokens not applying on web** Ensure `.light {}` and `.dark {}` are at the **top level** of `@layer theme` — not nested inside `:root {}`. Nesting them inside `:root {}` causes the selectors to be treated as descendants of `:root` rather than being applied to `:root` itself. --- ## Tooling Setup URL: /ui/docs/home/getting-started/tooling-setup/index # Tooling Setup ## IntelliSense setup (optional) If you are using VSCode and the [Tailwind CSS IntelliSense Extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss), you have to add the following to your `settings.json` file. ```json { "tailwindCSS.experimental.classRegex": [ ["tva\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] ] } ``` ## Prettier plugin setup (optional) If you are using [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) to sort your class names, you can add tv to the list of functions that should be sorted (on `.prettierrc`). ```ts module.exports = { plugins: ['prettier-plugin-tailwindcss'], tailwindFunctions: ['tva'], }; ``` --- ## VS Code Extensions URL: /ui/docs/home/getting-started/vscode-extensions/index # VS Code Extensions [gluestack VS Code Extension](https://marketplace.visualstudio.com/items?itemName=gluestack.gluestack-vscode) is specially crafted to streamline your development process when using the gluestack-ui v5. It provides a set of powerful features to accelerate your workflow and improve efficiency. These extensions provide gluestack snippets, which are shorthand for commonly used gluestack-ui components. All snippets start with the prefix `gs-` and are followed by the name of the desired component. gluestack VS Code Extensions ## Features ### Component Snippets Insert gluestack-ui components into your code with shorthand snippets. Each snippet starts with the prefix **`gs-`**, followed by the name of the desired component. This allows you to quickly generate component code without the need for manual typing. ### Additional Recipes Collection of predefined code templates, known as "recipes" to cover a wide range of scenarios and save you time by providing ready-to-use code structures. ### Automatic Imports Streamline your workflow with automated import statements. The extension intelligently handles the import process, ensuring that the necessary components and modules are included in your code. ### Dynamic Hooks & Variables Tailor your application's functionality specific requirements using dynamic hooks and variables. These features provide adaptability, allowing customization to suit the needs of your project. ## Getting Started To get started with the gluestack VS Code Extension, follow these steps: 1. Install the extension from the [Visual Studio Code Marketplace](https://marketplace.visualstudio.com/items?itemName=gluestack.gluestack-vscode). 2. Open a project in Visual Studio Code that uses `gluestack-ui v5`. 3. Start using the extension by accessing the snippets, recipes, and automatic import features described above. ## Example Usage ### Component Snippets To insert a gluestack-ui component, simply type **`gs-`** followed by the name of the desired component. This will give you suggestions. For example, to add a button, type **`gs-ButtonBasic`** and press **`Tab`** or **`Enter`** to generate the code. It will also have all the variants combinations with Buttons ```jsx ``` ### Additional Recipes Access predefined code templates by using the available recipes. For instance, to create a KeyboardAvoidingView with Actionsheet, type **`gs-ActionsheetWithKeyboardAvoidingView`** and press **`Tab`** to generate the code. ### Automatic Imports As you start using gluestack components, the extension will automatically handle the necessary imports for you. There's no need to manually add import statements; the extension does it for you. ### Dynamic Hooks & Variables Utilize dynamic hooks and variables to enhance the functionality of your gluestack application. These features empower you to adapt and customize your code to meet the specific requirements of your project. ## Changing gluestack-ui versions. - **Automatic Project Type Detection**: The extension automatically identifies your project type by analyzing your `tailwind.config` file. - **Manual Version Override**: You can manually select the snippet version you prefer. ### How to Manually Override Snippet Version? To manually override the snippet version: 1. Open the command palette by pressing `Command + Shift + P` (on Mac) or `Ctrl + Shift + P` (on Windows/Linux). 2. Type `Select gluestack-ui version` and press Enter. 3. Choose the desired version from the options: - `v1 - @gluestack-ui/themed` - `v2 - gluestack-ui with NativeWind` By following these steps, you can easily switch between different versions of gluestack-ui snippets to suit your project needs. --- ## Introduction URL: /ui/docs/home/overview/introduction/index # Introduction Customizable components and patterns for React, Next.js & React Native. **gluestack-ui** offers customizable, beautifully designed components for your projects. Unlike traditional libraries, it's not a pre-packaged dependency. Choose the components you need and copy-paste them directly into your React, Next.js & React Native projects. ## gluestack-ui philosophy ### Why gluestack-ui exists - **Problem:** Traditional UI libraries often force developers into rigid patterns, heavy dependencies, or platform-specific implementations. - **Solution:** gluestack-ui promotes a universal, modular, and copy-paste approach that lets developers own their UI components completely. - **Inspiration:** We started gluestack-ui v1 with a philosophy similar to React Aria & Radix UI, to keep the customization decoupled from component API and accessibility. By the time we announced v2, shadcn/ui had already taken over the web and today gluestack-ui and shadcn/ui are very similar, although gluestack-ui exists in the mobile and web world both! ### Goals & Non-Goals #### Our goals - **Universal Consistency:** One codebase, same behavior across React, Next.js, and React Native. - **Developer Ownership:** Copy-paste components, modify freely, avoid vendor lock-in. - **Performance & Accessibility:** Lightweight, optimized, and accessible components by default. - **Community-Driven:** Open architecture encouraging contributions and experimentation. #### Non-goals **We don't aim to** - Create another monolithic UI library with hundreds of pre-styled components - Provide opinionated design themes that limit creative freedom - Replace platform-specific optimizations with lowest-common-denominator solutions - Build a proprietary styling system that requires learning new APIs - Create dependencies that become bottlenecks for your project's evolution ### How gluestack-ui works - **Source-to-Destination Architecture:** Maintain a single source of truth for components that automatically syncs across projects. - **Copy-Paste Components:** Grab only what you need—no heavy dependencies required. - **Theming & Tailwind Integration:** Flexible styling system that works for web & mobile. - **TypeScript & RSC Ready:** Modern architecture for type safety and server-rendered apps. ### Design 101 - **Atomic, Composable Components:** Each component is small, reusable, and composable into more complex UIs. - **Compound Components:** Components with sub-components (e.g., `` ``Click Me`` `` ), allowing more flexible layouts. - **Accessibility by Default:** ARIA support baked in, keyboard navigable. - **Minimal Runtime Overhead:** Lightweight runtime ensures high performance. ### Code 101 - **Copy-Paste Philosophy:** No “magical imports.” You copy the component directly into your codebase. - **Fully Customizable:** You can override styles, props, and behavior easily. - **Single Source of Truth:** All component logic lives in src/ for maintainability and easy updates. - **Integration Ready:** Works out-of-the-box with Tailwind, NativeWind, and Next.js RSC. This philosophy guides every decision in gluestack-ui's development, from API design to documentation structure, ensuring that developers have the tools they need to build exceptional user experiences without compromise. {/* */} ## Future Considerations gluestack-ui is committed to continuously expanding its library of components to meet the needs of developers. We plan to add more components to the library, such as date-time picker, bottom sheet, and more. Apart from that, we are actively working on improving the performance of the styling library using techniques such as bundler-plugins, tree flattening, and more. ## Community ### Discord Get involved with the community, ask questions, and share tips, join our Discord. [Join our Discord](https://discord.gg/95qQ84nf6f) ### Twitter To receive updates on new primitives, announcements, blog posts, and tips on using the library. [Follow gluestack-ui on Twitter](https://twitter.com/gluestack) ### GitHub To report bugs, request features, or contribute to the library, check out the gluestack-ui GitHub repository. ### Stackoverflow Receive firsthand assistance from our community of developers. [Visit Stackoverflow](https://stackoverflow.com/questions/tagged/gluestack) ### LinkedIn Stay updated about our company and collaborate on enterprise-level projects. [Follow us on LinkedIn](https://www.linkedin.com/company/gluestackio/) ## All Components Explore 30+ components for React, Next.js & React Native [See All](/ui/docs/components/all-components). ## Contributing gluestack-ui v5 is built by the community, for the community. We welcome contributions of all kinds: - **Component Development**: Help build new components or improve existing ones - **Documentation**: Improve guides, examples, and API documentation - **Bug Reports**: Help us identify and fix issues - **Feature Requests**: Suggest new features and improvements Ready to build amazing universal apps? Let's get started! 🚀 > Note: Upgrade to v5 [Click here](/ui/docs/guides/more/upgrade-to-v5). --- ## Getting Started URL: /ui/docs/home/overview/quick-start/index # Getting Started --- ## Performance Benchmarks URL: /ui/docs/home/performance/benchmarks/index # Performance Benchmarks `gluestack-ui` harnesses the power of [NativeWind](https://www.nativewind.dev/v4/overview), a universal and highly performant styling library, to style the components. # React Native We performed the [benchmarks](https://github.com/gluestack/gluestack-ui-benchmarks) with `React Native`, `gluestack-ui v1` and `gluestack-ui v2`. **In each case we have rendered 1000 components with styling of each library. Results are average of 5 mounts.** > **All the benchmarks were measured in production environment on iPhone 15.** ## Simple Component The time it takes to render a simple themed component. We've developed a Box component using View with some default styles. ## Component with variants The time needed to render a component with different variations. We've developed a Box component using View with default styles and multiple variants. > Styled Components doesn't have built-in support for variants. We've incorporated prop-based variants into Styled Components. ## Component with default theme and inline styles A component with default themes, variations, inline styles, and state styles. We've designed a Button component using Pressable with default style, variations, state style, and inline styles. > React Native doesn't provide out of the box support for state styling. ## Layout using HStack, VStack, Image and Text Creating a layout with HStack, VStack, Image, and Text to display a list of 28 items. We've utilized components from the following libraries for this purpose. > Note: This test was taken from Tamagui. # Web We are working on dedicated benchmarks for the web. Stay tuned for more updates! In the meantime, we have built the landing page for v2 using gluestack-ui v2. You can check out the lighthouse score below: gluestack-ui v2 lighthouse score --- ## Customizing Theme URL: /ui/docs/home/theme-configuration/customizing-theme/index # Customizing Theme Customize your UI theme in gluestack-ui v5 using shadcn-inspired semantic color tokens. Define your theme in config.ts and apply it via GluestackUIProvider for consistent, accessible styling. ## Customizing Tokens gluestack-ui v5 uses a **semantic color token system** inspired by shadcn/ui. The tokens are defined separately for light and dark modes, and they automatically adapt when the mode changes. The color system uses semantic names (like `primary`, `secondary`, `background`) rather than numerical scales (50-900), making your theme more maintainable and meaningful. ### Understanding the Token Structure Color tokens are defined in RGB format **without the `rgb()` wrapper**, which allows Tailwind to apply opacity modifiers: ```jsx // Instead of: 'rgb(23, 23, 23)' // Use: '23 23 23' ``` This enables opacity syntax like `bg-primary/50` for 50% opacity. ### Customizing Colors To customize tokens, follow these steps: #### Step 1: Update color values in `gluestack-ui-provider/config.ts` ```jsx export const config = { light: vars({ // Base colors '--background': '255 255 255', // White background '--foreground': '10 10 10', // Almost black text // Primary brand color '--primary': '59 130 246', // Blue-500 '--primary-foreground': '255 255 255', // White text on primary // Secondary color '--secondary': '245 245 245', // Gray-100 '--secondary-foreground': '23 23 23', // Dark text on secondary // Accent color '--accent': '251 146 60', // Orange-400 '--accent-foreground': '255 255 255', // White text on accent // Muted elements '--muted': '245 245 245', // Gray-100 '--muted-foreground': '115 115 115', // Gray-500 // Destructive/error color '--destructive': '239 68 68', // Red-500 // Component backgrounds '--card': '255 255 255', // White cards '--popover': '255 255 255', // White popovers '--popover-foreground': '10 10 10', // Dark text in popovers // Borders and inputs '--border': '229 229 229', // Gray-200 '--input': '229 229 229', // Gray-200 '--ring': '59 130 246', // Blue-500 focus ring }), dark: vars({ // Base colors '--background': '10 10 10', // Almost black background '--foreground': '250 250 250', // Almost white text // Primary brand color (inverted for dark mode) '--primary': '147 197 253', // Blue-300 '--primary-foreground': '23 23 23', // Dark text on primary // Secondary color '--secondary': '38 38 38', // Gray-800 '--secondary-foreground': '250 250 250', // Light text on secondary // Accent color '--accent': '251 146 60', // Orange-400 '--accent-foreground': '255 255 255', // White text on accent // Muted elements '--muted': '38 38 38', // Gray-800 '--muted-foreground': '161 161 161', // Gray-400 // Destructive/error color '--destructive': '252 165 165', // Red-300 // Component backgrounds '--card': '23 23 23', // Dark cards '--popover': '23 23 23', // Dark popovers '--popover-foreground': '250 250 250', // Light text in popovers // Borders and inputs '--border': '46 46 46', // Gray-700 '--input': '46 46 46', // Gray-700 '--ring': '147 197 253', // Blue-300 focus ring }), }; ``` #### Step 2: Map tokens to Tailwind in `tailwind.config.js` Ensure your Tailwind config maps the CSS variables to Tailwind classes: ```jsx module.exports = { theme: { extend: { colors: { border: 'rgb(var(--border))', input: 'rgb(var(--input))', ring: 'rgb(var(--ring))', background: 'rgb(var(--background))', foreground: 'rgb(var(--foreground))', primary: { DEFAULT: 'rgb(var(--primary))', foreground: 'rgb(var(--primary-foreground))', }, secondary: { DEFAULT: 'rgb(var(--secondary))', foreground: 'rgb(var(--secondary-foreground))', }, destructive: { DEFAULT: 'rgb(var(--destructive))', }, muted: { DEFAULT: 'rgb(var(--muted))', foreground: 'rgb(var(--muted-foreground))', }, accent: { DEFAULT: 'rgb(var(--accent))', foreground: 'rgb(var(--accent-foreground))', }, popover: { DEFAULT: 'rgb(var(--popover))', foreground: 'rgb(var(--popover-foreground))', }, card: { DEFAULT: 'rgb(var(--card))', }, }, }, }, }; ``` ### Usage in Components Once configured, use the semantic tokens in your components: ```jsx // Primary button // Secondary button // Card with proper contrast Card content Muted description // Destructive action // With opacity Subtle primary background ``` ### Adding Custom Tokens You can add your own custom tokens following the same pattern: #### Step 1: Add to config.ts ```jsx export const config = { light: vars({ // ... existing tokens '--success': '34 197 94', // Green-500 '--success-foreground': '255 255 255', '--warning': '251 146 60', // Orange-400 '--warning-foreground': '255 255 255', }), dark: vars({ // ... existing tokens '--success': '134 239 172', // Green-300 '--success-foreground': '23 23 23', '--warning': '253 186 116', // Orange-300 '--warning-foreground': '23 23 23', }), }; ``` #### Step 2: Add to tailwind.config.js ```jsx colors: { // ... existing colors success: { DEFAULT: 'rgb(var(--success))', foreground: 'rgb(var(--success-foreground))', }, warning: { DEFAULT: 'rgb(var(--warning))', foreground: 'rgb(var(--warning-foreground))', }, } ``` ### Adding Custom Font Sizes You can also add custom typography tokens: #### Step 1: Add to config.ts ```jsx export const config = { light: vars({ // ... existing tokens '--font-size-custom': '80', // 80px }), dark: vars({ // ... existing tokens '--font-size-custom': '80', }), }; ``` #### Step 2: Add to tailwind.config.js ```jsx fontSize: { 'custom-heading-xl': 'var(--font-size-custom)', } ``` #### Step 3: Configure tva for the component For custom font sizes to work with `tva` (Tailwind Variants Authority), add configuration: ```jsx const config = { twMerge: true, twMergeConfig: { classGroups: { 'font-size': [ { text: ['custom-heading-xl'], }, ], }, }, }; export const textStyle = tva({ base: 'text-foreground', variants: { size: { 'custom-heading-xl': 'text-custom-heading-xl', }, }, }, config); ``` Alternatively, set a [global tailwind-variants config](https://www.tailwind-variants.org/docs/config#global-config) to affect all components. ### Best Practices 1. **Use semantic names** - Name tokens by purpose (`primary`, `secondary`) not appearance (`blue`, `gray`) 2. **Maintain contrast** - Always provide foreground tokens for colored backgrounds 3. **Test both modes** - Verify your colors work in both light and dark mode 4. **Use opacity sparingly** - The `/50` syntax is great for subtle backgrounds 5. **Follow the pattern** - Keep the RGB format without `rgb()` wrapper for Tailwind compatibility For a complete list of default tokens, see [Default Tokens](/ui/docs/home/theme-configuration/default-tokens). For extending Tailwind config (fonts, breakpoints, border radius, etc.), refer to the [Tailwind CSS documentation](https://tailwindcss.com/docs/theme#customizing-the-default-theme). --- ## Dark Mode URL: /ui/docs/home/theme-configuration/dark-mode/index # Dark Mode Customize the theme in gluestack-ui v5 with Tailwind dark mode, UI theme dark mode colors, and React Native light & dark mode for different color schemes and color mode support. ## Color Scheme gluestack-ui provides two ways of switching the color scheme or color mode: using CSS variables and using the `dark:` className support with `nativewind`. ### Using CSS Variables With CSS variables, you can configure multiple color schemes in the `config.ts` file. This file contains two predefined color schemes: `light` and `dark`. It utilizes the [vars](https://www.nativewind.dev/v4/api/vars) functionality from nativewind to switch token values when the color mode is changed. This approach results in less code and makes configuring tokens for different color schemes easier. ### Usage Let's look at an example where we define the `primary` token and switch it. 1. First, define the color token in your `tailwind.config.js` file and assign a variable value as shown below, following Tailwind's recommendation for [using CSS variables in Tailwind](https://tailwindcss.com/docs/customizing-colors#using-css-variables). ```js // tailwind.config.js module.exports = { theme: { extend: { colors: { primary: 'rgb(var(--color-primary)/)', }, }, }, }; ``` 2. Now, define the values of that CSS variable for the `light` and `dark` color schemes in the `config.ts` file as shown below. [Reference](https://www.nativewind.dev/v4/guides/themes). ```js // config.ts export const config = { light: vars({ '--color-primary': '51 51 51', }), dark: vars({ '--color-primary': '240 240 240', }), }; ``` 3. Pass the color mode to the `GluestackUIProvider` using the `mode` prop and use the tokens inside your code. `mode`prop accepts three values: `system`, `light` and `dark`. - `system`: It uses the system color mode. - `light`: It uses the light color mode. - `dark`: It uses the dark color mode. ```js // App.tsx export default function App() { const [colorMode, setColorMode] = useState<'light' | 'dark'>('light'); return ( ); } ``` ## Using Tailwind Dark Mode gluestack-ui also supports Tailwind's default `dark:` concept. Let's see how to use it with an example. ```js // App.tsx export default function App() { const [colorMode, setColorMode] = useState<'light' | 'dark'>('light'); return ( ); } ``` In the above example, we switch the background color of our Box component in dark mode using the `dark:` syntax. To use dark mode, we need to change the `darkMode` strategy to `"class"` for the web and `"media"` for native devices in the `tailwind.config.js` file. You can achieve this by setting the `DARK_MODE` environment variable as shown below. ```js // tailwind.config.js module.exports = { darkMode: process.env.DARK_MODE ? process.env.DARK_MODE : 'media', // rest of the config }; ``` After this, we need to update our scripts in the `package.json` file as shown below: ```json { "scripts": { "android": "DARK_MODE=media expo start --android", "ios": "DARK_MODE=media expo start --ios", "web": "DARK_MODE=class expo start --web" } } ``` For Next.js projects, you can directly set the `darkMode` strategy to `"class"` without needing to change the scripts. > Note: This is a temporary solution until we fix the issue with nativewind for the `darkMode:"class"` strategy. ## Persist Color Mode **Native:** Step 1: Install the following dependencies: ```bash npm i @react-native-async-storage/async-storage ``` We will use the `@react-native-async-storage/async-storage` package to store the color mode in the device's local storage. You can also use other storage solutions. Step 2: Create a new file `ThemeProvider.tsx` at `components/ui/ThemeProvider`. ```jsx // components/ui/ThemeProvider/ThemeProvider.tsx "use client"; type Theme = "light" | "dark"; interface ThemeContextType { theme: Theme; toggleTheme: () => void; } export const ThemeContext = createContext( undefined ); export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { const [theme, setTheme] = useState("light"); useEffect(() => { (async () => { const savedTheme = (await AsyncStorage.getItem("theme")) as | Theme | "light"; if (savedTheme) { setTheme(savedTheme); AsyncStorage.setItem("theme", savedTheme); } })(); }, []); const toggleTheme = () => { const newTheme = theme === "light" ? "dark" : "light"; setTheme(newTheme); AsyncStorage.setItem("theme", newTheme); }; return ( {children} ); }; export const useTheme = () => { const context = useContext(ThemeContext); if (context === undefined) { throw new Error("useTheme must be used within a ThemeProvider"); } return context; }; ``` Step 3: Update your app's root to utilize the `ThemeProvider` component. **Web:** Step 1: Install the following dependencies: ```bash npm i js-cookie @types/js-cookie ``` We have to use the `js-cookie` library to set and get cookies in the browser. Step 2: Create a new file `ThemeProvider.web.tsx` at `components/ui/ThemeProvider`. ```jsx // components/ui/ThemeProvider/ThemeProvider.web.tsx "use client"; type Theme = "light" | "dark"; interface ThemeContextType { theme: Theme; toggleTheme: () => void; } export const ThemeContext = createContext( undefined ); export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { const [theme, setTheme] = useState("light"); useEffect(() => { const savedTheme = Cookies.get("theme") as Theme | undefined; if (savedTheme) { setTheme(savedTheme); document.documentElement.classList.add(savedTheme); document.documentElement.style.colorScheme = savedTheme; } }, []); const toggleTheme = () => { const newTheme = theme === "light" ? "dark" : "light"; setTheme(newTheme); Cookies.set("theme", newTheme, { expires: 365 }); document.documentElement.classList.remove(theme); document.documentElement.classList.add(newTheme); document.documentElement.style.colorScheme = newTheme; }; return ( {children} ); }; export const useTheme = () => { const context = useContext(ThemeContext); if (context === undefined) { throw new Error("useTheme must be used within a ThemeProvider"); } return context; }; ``` Step 3: Update your app's root to utilize the `ThemeProvider` component. Please refer to the Tailwind CSS dark mode [documentation](https://tailwindcss.com/docs/dark-mode) for more information and core concepts of dark mode. --- ## Default Tokens URL: /ui/docs/home/theme-configuration/default-tokens/index # Default Tokens gluestack-ui v5 ships with shadcn-inspired default tokens, including colored tokens that provide access to theme values and flexibility to build and customize your own themed UI components. Theming in gluestack-ui is based on the [Styled System Theme Specification](https://github.com/system-ui/theme-specification) and follows the [shadcn/ui](https://ui.shadcn.com) design principles. ## Colors gluestack-ui v5 uses a **semantic color token system** inspired by shadcn/ui. Instead of using numerical color scales (50-900), we use purpose-driven color names that adapt automatically to light and dark modes. ### Semantic Color Tokens The color system is organized around **semantic tokens** that describe their purpose rather than their appearance: #### Base Colors - **`background`** - Default page background - **`foreground`** - Default text color #### Component Colors - **`card`** - Card backgrounds - **`popover`** - Popover/dropdown backgrounds - **`popover-foreground`** - Text color for popovers #### Semantic Colors with Foregrounds Each semantic color has a corresponding `-foreground` token for optimal contrast: - **`primary`** / **`primary-foreground`** - Primary actions and highlights - **`secondary`** / **`secondary-foreground`** - Secondary actions - **`accent`** / **`accent-foreground`** - Accent elements - **`muted`** / **`muted-foreground`** - Muted/subtle content - **`destructive`** - Destructive actions (errors, delete buttons) #### Form & Border Colors - **`border`** - Default border color - **`input`** - Input field borders - **`ring`** - Focus ring color ### Color Palette Below are the default color tokens provided in gluestack-ui v5. These automatically adapt between light and dark modes: ### How It Works The color tokens use **RGB values without the `rgb()` wrapper**, allowing for opacity modifications using Tailwind's opacity syntax: ```jsx // In config.ts export const config = { light: vars({ '--primary': '23 23 23', // Almost black in light mode '--primary-foreground': '250 250 250', // Almost white text }), dark: vars({ '--primary': '255 245 245', // Almost white in dark mode '--primary-foreground': '23 23 23', // Almost black text }), }; ``` Usage with opacity: ```jsx // Full opacity // 50% opacity ``` ### Benefits of This System 1. **Semantic naming** - Colors describe their purpose, not their shade 2. **Automatic dark mode** - Tokens adapt to light/dark mode automatically 3. **Guaranteed contrast** - Foreground tokens ensure readable text 4. **Design system consistency** - Follows industry-standard patterns To customize colors, update `gluestack-ui-provider/config.ts` and `tailwind.config.js`. For detailed instructions, see [Customizing Theme](/ui/docs/home/theme-configuration/customizing-theme). ## Typography To manage Typography options, update **theme** in `tailwind.config.js`. To add or update **Font Family**. Please refer [Tailwind CSS documentation](https://tailwindcss.com/docs/font-family). We have also added a new family, '**roboto**', in our `tailwind.config.js`. To add or update **font sizes**, please refer to the [Tailwind CSS documentation](https://tailwindcss.com/docs/font-size). We have added a new size, '**2xs**', in `tailwind.config.js` with a value of '**10px**'. To add or update **font weights**, please refer to the [Tailwind CSS documentation](https://tailwindcss.com/docs/font-weight). We have added a new weight, '**extrablack**', in `tailwind.config.js` with a value of '**950**'. To add or update **line heights**. Please refer [Tailwind CSS documentation](https://tailwindcss.com/docs/line-height). To add or update **letter spacing**. Please refer [Tailwind CSS documentation](https://tailwindcss.com/docs/letter-spacing). ## Breakpoints **gluestack-ui** comes with default Tailwind CSS **breakpoints**. Please refer [this](https://tailwindcss.com/docs/responsive-design) link for customization. ## Spacing **gluestack-ui** comes with default Tailwind CSS **spacing**. Please refer [this](https://tailwindcss.com/docs/customizing-spacing) link for customization. ## Radius **gluestack-ui** comes with default Tailwind CSS **border-radius**. Please refer [this](https://tailwindcss.com/docs/border-radius) link for customization. ## Shadows **gluestack-ui** comes with default Tailwind CSS Shadows and we provide two more types of shadows. Please refer [this](https://tailwindcss.com/docs/customizing-spacing) link for customization. --- ## useBreakpointValue URL: /ui/docs/hooks/use-break-point-value/index # useBreakpointValue Learn how to use the useBreakpointValue hook to manage breaking point values, breakpoint components in Expo, React & React Native for responsive design. This is an illustration of **useBreakPointValue** hook. ```jsx function App(){ const flexDir = useBreakpointValue(\{ default: "column", sm: "row", }); return ( Box 1 Box 2 Box 3 ); }` ``` To use this component in your project, include the following import statement in your file. ```ts ``` ```ts const flexDir = useBreakpointValue({ default: 'column', sm: 'row', }); ``` ### Hook Arguments This section provides a comprehensive reference list for the hook arguments, detailing descriptions, properties, types, and default behavior for easy project integration. #### useBreakPointValue | Name | Type | Default | | --- | --- | --- | | `options` | {`Record`} | - | ### Return Value The **useBreakPointValue** hook returns value, based on given break point value object. --- ## useMediaQuery URL: /ui/docs/hooks/use-media-query/index # useMediaQuery Implement responsive designs in React & React Native with the useMediaQuery hook and component. Learn how to use the React useMediaQuery hook today. **useMediaQuery** is a custom hook that helps detect matches between a single media query or multiple media queries. React Native does not natively support media queries, so useMediaQuery is still limited. ```jsx function App(){ const [isMobile, isTablet, isSmallScreen, isLargeScreen] = useMediaQuery([ { maxWidth: 480, }, { minWidth: 481, maxWidth: 768, }, { minWidth: 769, maxWidth: 1440, }, { minWidth: 1441, }, ]); return ( useMediaQuery Resize your browser windows to see changes. Small medium Large Extra Large ); }` ``` ## API Reference To use this component in your project, include the following import statement in your file. ```ts ``` ```ts const flexDir = useMediaQuery({ minWidth: 480, maxWidth: 1280, }); ``` ### Hook Arguments This section provides a comprehensive reference list for the hook arguments, detailing descriptions, properties, types, and default behavior for easy project integration. #### useBreakPointValue | Name | Type | Default | | --- | --- | --- | | `options` | {`{ [key: maxHeight | maxWidth | minHeight | minWidth | orientation] : value } \nArray<{ [key: maxHeight | maxWidth | minHeight | minWidth | orientation] : value }`} | - | ### Return Value The **useMediaQuery** hook returns an array of booleans, indicating whether the given query matches or queries match. ### Why an array? **useMediaQuery** accepts both an object and an array of object, but will always return an array. This way, you can combine multiple media queries which will be individually matched in a single call. The options to use are still limited to maxWidth, minWidth, maxHeight, minHeight, orientation. --- ## MCP Server URL: /ui/docs/mcp-server/mcp-server/index # MCP Server A server that generates complete, consistent codebases using `gluestack-ui v2` components. Check out this demo video: GitHub link for this [MCP Server](https://github.com/gluestack/mcp). ---