# Accordion (/docs/components/accordion)
Import [#import]
```jsx
import {Accordion,AccordionContent,AccordionItem,AccordionTrigger} from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const AccordionExample = () => {
return (
Page settings
Configure your page settings including layout, theme, and display
options.
Profile Information
Manage your profile details, avatar, and personal preferences.
);
};
```
Examples [#examples]
Dynamic Selection [#dynamic-selection]
Allow the change of the Selection type dynamically as single or multiple using the `type` prop.
Value Variants [#value-variants]
Individual accordion items can be set disabled and also as a default value
Props [#props]
Accordion [#accordion]
void",
description: "Event handler called when the expanded state changes.",
},
collapsible: {
type: "boolean",
description:
"When type is single, allows closing content when clicking trigger for opened item.",
default: "false",
},
disabled: {
type: "boolean",
description:
"When true, prevents the user from interacting with the accordion.",
default: "false",
},
dir: {
type: " 'ltr' | 'rtl' ",
description:
"The reading direction of the accordion when applicable. If omitted, assumes LTR (left-to-right) reading mode.",
default: "ltr",
},
css: {
type: "object",
description: "Custom CSS styles for the component",
},
}}
/>
AccordionItem [#accordionitem]
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
**Applicable**
\- Accordion
**Applicable**
\- AccordionItem/ AccordionContent/ AccordionTrigger
# AlertDialog (/docs/components/alert-dialog)
Import [#import]
```jsx
import {AlertDialog,AlertDialogAction,AlertDialogActions,AlertDialogCancel,AlertDialogContent,AlertDialogDescription,AlertDialogTitle,AlertDialogTrigger,Button} from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const AlertDialogExample = () => {
return (
Are you absolutely sure?
This action cannot be undone. This will permanently delete your
account and remove your data from our servers.
);
};
```
Props [#props]
AlertDialog [#alertdialog]
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
# Alert (/docs/components/alert)
Import [#import]
```jsx
import { Alert, AlertDescription } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
Password should have a minimum length of 6.
```
Examples [#examples]
Different status with variable sizes [#different-status-with-variable-sizes]
Props [#props]
Alert [#alert]
# Avatar (/docs/components/avatar)
Import [#import]
```jsx
import { Avatar } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
```
Examples [#examples]
Avatar group with different variants [#avatar-group-with-different-variants]
By default, the overflow label is shown as `+N` based on the extra avatar count. Once the count exceeds 99, it is capped at `+99`. If you need a different format or cap behavior, use `limitExceededFormatter` to customize it.
Props [#props]
Avatar [#avatar]
Avatar Group [#avatar-group]
React.ReactNode",
description:
"Provides a custom formatter for the overflow label using the extra avatar count.",
},
}}
/>
# Box (/docs/components/box)
Import [#import]
```jsx
import { Box } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
The quick brown fox jumps over the lazy dog.
```
Examples [#examples]
Box with styling options [#box-with-styling-options]
Props [#props]
Box [#box]
# Button (/docs/components/button)
Import [#import]
```jsx
import { Button } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
```
Examples [#examples]
Button variants and with icons [#button-variants-and-with-icons]
Button states [#button-states]
Props [#props]
Button [#button]
# CalendarRange (/docs/components/calendar-range)
Prerequisite [#prerequisite]
`CalendarRange` component requires additional dependency of `@internationalized/date` to be installed.
We will be working on removing this additional dependency in the future.
```bash
npm install --save @internationalized/date
```
Import [#import]
```jsx
import { CalendarRange } from "@sparrowengg/twigs-react";
import { parseDate } from "@internationalized/date";
```
Usage [#usage]
```jsx
function App() {
const [value, setValue] = React.useState({
start: parseDate("2025-08-04"),
end: parseDate("2025-08-15"),
});
return (
setValue(value as { start: CalendarDate; end: CalendarDate })}
value={value}
minValue={parseDate("2025-08-03")}
/>
);
}
```
Examples [#examples]
Compact calendar range and footer variants [#compact-calendar-range-and-footer-variants]
Props [#props]
CalendarRange [#calendarrange]
void",
description: "Callback function when date range is selected",
},
minValue: {
type: "DateValue",
description:
"Minimum selectable date value. Dates beyond this value will be disabled",
},
maxValue: {
type: "DateValue",
description:
"Maximum selectable date value. Dates beyond this value will be disabled",
},
isDisabled: {
type: "boolean",
description: "Disable the calendar component",
default: "false",
},
isDateUnavailable: {
type: "(date: DateValue) => boolean",
description:
"Function to determine if a date is unavailable for selection",
},
size: {
type: "'sm' | 'md' | 'lg'",
description: "Size of the calendar component",
default: "lg",
},
compact: {
type: "boolean",
description: "Display calendar in a compact single-month view",
default: "false",
},
showFooter: {
type: "boolean",
description: "Show footer with action button",
default: "true",
},
footerActionText: {
type: "string",
description: "Text to be displayed on footer button",
default: "'Apply'",
},
footerAction: {
type: "(state: RangeCalendarState) => void",
description: "Action to be performed on footer button click",
},
renderFooter: {
type: "(state: RangeCalendarState) => ReactNode",
description: "Custom footer component",
},
onDaySelect: {
type: "(date: DateValue, range: 'start' | 'end') => void",
description: "Callback function when day is selected",
},
onMonthSelect: {
type: "(date: DateValue, range: 'start' | 'end') => void",
description: "Callback function when month is selected",
},
onYearSelect: {
type: "(date: DateValue, range: 'start' | 'end') => void",
description: "Callback function when year is selected",
},
showTimePicker: {
type: "boolean",
description: "Show time picker for date and time selection",
default: "false",
},
showTimezonePicker: {
type: "boolean",
description: "Show timezone picker for timezone-aware selection",
default: "false",
},
containerCSS: {
type: "CSS Object",
description: "Custom CSS for the container element",
},
}}
/>
# Calendar (/docs/components/calendar)
Prerequisite [#prerequisite]
`Calendar` component requires additional dependency of `@internationalized/date` to be installed.
We will be working on removing this additional dependency in the future.
```bash
npm install --save @internationalized/date
```
Import [#import]
```jsx
import { Calendar } from "@sparrowengg/twigs-react";
import { parseDate } from "@internationalized/date";
```
Usage [#usage]
```jsx
function App() {
const [value, setValue] = React.useState(parseDate("2025-08-24"));
return (
);
}
```
Examples [#examples]
Calendar with customization [#calendar-with-customization]
Props [#props]
Calendar [#calendar]
void",
description: "Callback function when date is selected",
},
minValue: {
type: "DateValue",
description:
"Minimum selectable date value. Dates beyond this value will be disabled",
},
maxValue: {
type: "DateValue",
description:
"Maximum selectable date value. Dates beyond this value will be disabled",
},
isDisabled: {
type: "boolean",
description: "Disable the calendar component",
default: "false",
},
isReadOnly: {
type: "boolean",
description: "Make the calendar read-only",
default: "false",
},
isDateUnavailable: {
type: "(date: DateValue) => boolean",
description:
"Function to determine if a date is unavailable for selection",
},
autoFocus: {
type: "boolean",
description: "Whether to auto focus the calendar on mount",
default: "false",
},
focusedValue: {
type: "DateValue",
description: "Controlled focused date value",
},
defaultFocusedValue: {
type: "DateValue",
description: "Default focused date value (uncontrolled)",
},
onFocusChange: {
type: "(date: DateValue) => void",
description: "Callback when the focused date changes",
},
size: {
type: "'sm' | 'md' | 'lg'",
description: "Size of the calendar component",
default: "lg",
},
showFooter: {
type: "boolean",
description: "Show footer with action button",
default: "true",
},
footerActionText: {
type: "string",
description: "Text to be displayed on footer button",
default: "'Select'",
},
footerAction: {
type: "(state: CalendarState) => void",
description: "Action to be performed on footer button click",
},
renderFooter: {
type: "(state: CalendarState) => ReactNode",
description: "Custom footer component",
},
onDaySelect: {
type: "(date: DateValue) => void",
description: "Callback function when day is selected",
},
onMonthSelect: {
type: "(date: DateValue) => void",
description: "Callback function when month is selected",
},
onYearSelect: {
type: "(date: DateValue) => void",
description: "Callback function when year is selected",
},
showTimePicker: {
type: "boolean",
description: "Show time picker for date and time selection",
default: "false",
},
showTimezonePicker: {
type: "boolean",
description: "Show timezone picker for timezone-aware selection",
default: "false",
},
hourCycle: {
type: "12 | 24",
description: "Hour cycle - 12 for 12-hour format with AM/PM, 24 for 24-hour format",
default: "12",
},
containerCSS: {
type: "CSS Object",
description: "Custom CSS for the container element",
},
}}
/>
# Cascader (/docs/components/cascader)
Import [#import]
```jsx
import { Cascader } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
setValue(val)}
/>
```
We have stored the `test-data` seperately for the easy accessibility, all the example will use the same data.
Examples [#examples]
Cascader features [#cascader-features]
Props [#props]
Cascader [#cascader]
void",
description: "Callback fired when the value changes",
},
label: {
type: "string",
description: "Label text for the cascader input",
},
placeholder: {
type: "string",
description: "Placeholder text displayed when no value is selected",
},
creatable: {
type: "boolean",
description: "Allows users to create new options",
default: "false",
},
cancelButtonText: {
type: "string",
description: "Text for the cancel button",
default: "'Cancel'",
},
chooseButtonText: {
type: "string",
description: "Text for the choose button",
default: "'Choose'",
},
fetchOptions: {
type: "(data: { value: string; label: string } & Record) => Promise",
description: "Function to fetch options dynamically for a node",
},
fetchSearchOptions: {
type: "(searchString: string) => Promise",
description: "Function to fetch search results dynamically",
},
renderValue: {
type: "(value: CascaderItem | null, selectionPath: CascaderItem[]) => ReactNode",
description: "Custom renderer for the selected value display",
},
renderValueText: {
type: "(value: CascaderItem | null, selectionPath: CascaderItem[]) => ReactNode",
description: "Custom renderer for the value text",
},
renderBreadCrumb: {
type: "(data: CascaderItem | null, selectionPath: CascaderItem[]) => ReactNode",
description: "Custom renderer for the breadcrumb display",
},
inputAriaDescription: {
type: "string",
description: "ARIA description for the input field",
},
ariaLiveContent: {
type: "(data: { breadcrumb: string; label: string; totalItems: any; itemPosition: number; hasOptions: boolean; hasParent: boolean; } | null) => ReactNode",
description: "Custom ARIA live region content for accessibility",
},
searchLoadingIndicator: {
type: "ReactNode",
description: "Loading indicator shown during search",
default: "'Loading Results...'",
},
popoverPortal: {
type: "HTMLElement",
description: "Portal element for the popover",
},
css: {
type: "object",
description: "Custom CSS styles for the component",
},
}}
/>
# Checkbox (/docs/components/checkbox)
Import [#import]
```jsx
import { Checkbox } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const CheckboxExample = () => {
return (
Accept terms and conditions
);
};
```
Examples [#examples]
Indeterminate State [#indeterminate-state]
The checkbox supports an indeterminate state, useful for representing partial selections (e.g., "select all" scenarios). When set to `'indeterminate'`, the checkbox displays a horizontal line icon instead of a checkmark.
Props [#props]
Checkbox [#checkbox]
void",
description: "Event handler called when the checked state changes.",
},
children: {
type: "ReactNode",
description: "The content to display as the checkbox label.",
},
disabled: {
type: "boolean",
description: "When true, prevents the user from interacting with the checkbox.",
default: "false",
},
required: {
type: "boolean",
description: "When true, indicates that the user must check the checkbox before submitting the form.",
default: "false",
},
id: {
type: "string",
description: "The id of the checkbox element. Auto-generated if not provided.",
},
name: {
type: "string",
description: "The name of the checkbox. Useful for form submission.",
},
value: {
type: "string",
description: "The value given as data when submitted with a name.",
default: "on",
},
size: {
type: "'sm' | 'md'",
description: "The size of the checkbox.",
default: "'sm'",
},
containerRef: {
type: "React.Ref",
description: "Ref to the container div element wrapping the checkbox.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
# Chip (/docs/components/chip)
Import [#import]
```jsx
import { Chip } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const ChipExample = () => {
return (
PrimarySuccess
);
};
```
You can customize the chip by adding `icons`, `variant` and `color`
Examples [#examples]
Selectable Chip [#selectable-chip]
Interactive chips that can be selected and maintain an active state.
Props [#props]
Chip [#chip]
void",
description: "Callback function called when the close icon is clicked.",
},
leftElement: {
type: "ReactNode",
description: "React element to display on the left side of the chip content.",
},
rightElement: {
type: "ReactNode",
description: "React element to display on the right side of the chip content.",
},
selectable: {
type: "boolean",
description: "When true, makes the chip selectable with hover and active states.",
default: "false",
},
active: {
type: "boolean",
description: "The controlled active state of the chip when selectable.",
},
defaultActive: {
type: "boolean",
description: "The default active state when uncontrolled and selectable.",
default: "false",
},
onActiveStateChange: {
type: "(active: boolean) => void",
description: "Event handler called when the active state changes in selectable chips.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
# Circle Loader (/docs/components/circle-loader)
Import [#import]
```jsx
import { CircleLoader } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const CircleLoaderExample = () => {
return (
);
};
```
Props [#props]
CircleLoader [#circleloader]
# DatePicker (/docs/components/datepicker)
Prerequisite [#prerequisite]
`DatePicker` component requires additional dependency of `@internationalized/date` to be installed.We will be working on removing this additional dependency in the future.
```bash
npm install --save @internationalized/date
```
Import [#import]
```jsx
import { DatePicker } from "@sparrowengg/twigs-react";
import { parseDate } from "@internationalized/date";
```
Usage [#usage]
```jsx
const DatePickerExample = () => {
const [value, setValue] = React.useState(parseDate("2023-07-24"));
return (
setValue(value as CalendarDate)}
label="Enter date of birth"
/>
);
};
```
Examples [#examples]
Format Segments [#format-segments]
Customize the display format of the date picker using the `formatSegments` prop.
Props [#props]
DatePicker [#datepicker]
void",
description: "Callback function when date is selected.",
},
minValue: {
type: "DateValue",
description: "Minimum selectable date value. Dates beyond this value will be disabled.",
},
maxValue: {
type: "DateValue",
description: "Maximum selectable date value. Dates beyond this value will be disabled.",
},
isDisabled: {
type: "boolean",
description: "Disable the calendar component.",
default: "false",
},
isOpen: {
type: "boolean",
description: "Control the open state of the calendar popover.",
default: "false",
},
closeOnSelect: {
type: "boolean",
description: "Close datepicker on selecting a date.",
default: "false",
},
label: {
type: "string",
description: "Label text displayed above the date picker field.",
},
enablePortal: {
type: "boolean",
description: "Calendar popover will render in body instead of parent container.",
default: "false",
},
portalTarget: {
type: "Element",
description: "Render Calendar Popover in specific element.",
},
onDaySelect: {
type: "(date: DateValue) => void",
description: "Callback function when day is selected.",
},
onMonthSelect: {
type: "(date: DateValue) => void",
description: "Callback function when month is selected.",
},
onYearSelect: {
type: "(date: DateValue) => void",
description: "Callback function when year is selected.",
},
showFooter: {
type: "boolean",
description: "Show footer with action button.",
default: "true",
},
footerAction: {
type: "(state: CalendarState, setPopoverOpen: (isOpen: boolean) => void) => void",
description: "Action to be performed on footer button click.",
},
footerActionText: {
type: "string",
description: "Text to be displayed on footer button.",
default: "'Select'",
},
renderFooter: {
type: "(state: CalendarState, setPopoverOpen: (isOpen: boolean) => void) => ReactNode",
description: "Custom footer component.",
},
containerCSS: {
type: "CSS",
description: "Custom CSS for the container element.",
},
calendarContainerCSS: {
type: "CSS",
description: "Custom CSS for the calendar container element.",
},
popoverContentProps: {
type: "PopoverContentProps",
description: "Additional props to be passed to the popover content component.",
},
formatSegments: {
type: "(segments: DateSegment[]) => DateSegment[]",
description: "Custom format segments for the date picker display.",
},
showTimePicker: {
type: "boolean",
description: "Show time picker in the calendar.",
default: "false",
},
showTimezonePicker: {
type: "boolean",
description: "Show timezone picker in the calendar.",
default: "false",
},
hourCycle: {
type: "12 | 24",
description: "Hour cycle - 12 for 12-hour format with AM/PM, 24 for 24-hour format.",
default: "12",
},
}}
/>
# Dialog (/docs/components/dialog)
Import [#import]
```jsx
import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody,DialogFooter, DialogClose } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const DialogExample = () => {
return (
);
};
```
Props [#props]
Dialog [#dialog]
void",
description:
"Event handler called when the open state of the dialog changes.",
},
size: {
type: "'sm' | 'md' | 'lg' | 'xl' | 'full'",
description: "The size of the dialog.",
default: "'md'",
},
scrollBehavior: {
type: "'auto' | 'scroll' | 'hidden'",
description: "Control scroll behavior of the dialog content.",
default: "'auto'",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
DialogContent [#dialogcontent]
DialogTitle [#dialogtitle]
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
# DialogsManager (/docs/components/dialogs-manager)
Import [#import]
```jsx
import { DialogsManager, dialogs } from "@sparrowengg/twigs-react";
```
Usage [#usage]
Add `DialogsManager` to a top level component like `App` as shown below:
```jsx
import { DialogsManager } from "@sparrowengg/twigs-react";
function App() {
return (
);
}
```
And now you can call `dialogs.open` or `dialogs.push` to open new dialogs.
Methods [#methods]
dialogs.open(dialogName, options) [#dialogsopendialogname-options]
**Returns object with two properties:**
dialogs.push(dialogName, options) [#dialogspushdialogname-options]
The parameters and return types are same as that of `dialogs.open`, the only difference is that `push` doesn't close existing modals.
dialogs.close(dialogId) [#dialogsclosedialogid]
Used to close a particular dialog.
dialogs.closeAll() [#dialogscloseall]
Used to close all open dialogs.
Props [#props]
DialogsManager [#dialogsmanager]
void; [x: string]: any }) => ReactNode>",
description: "Custom dialogs that can be opened using dialogs methods.",
default: "{}",
},
}}
/>
Default Dialogs Props [#default-dialogs-props]
Alert [#alert]
) => void",
description: "Callback when action button is clicked.",
default: "undefined",
},
onClose: {
type: "() => void",
description: "Callback when dialog is closed.",
default: "undefined",
},
}}
/>
Confirm [#confirm]
) => void",
description: "Callback when confirm button is clicked.",
default: "undefined",
},
onCancel: {
type: "(e: React.MouseEvent) => void",
description: "Callback when cancel button is clicked.",
default: "undefined",
},
onClose: {
type: "() => void",
description: "Callback when dialog is closed.",
default: "undefined",
},
css: {
type: "CSS",
description: "Custom styles to be applied on the content.",
default: "{}",
},
}}
/>
Modal [#modal]
) => void",
description: "Callback when action button is clicked.",
default: "undefined",
},
onClose: {
type: "() => void",
description: "Callback when dialog is closed.",
default: "undefined",
},
size: {
type: "'sm' | 'md' | 'lg' | 'xl' | 'full'",
description: "The size of the modal dialog.",
default: "'md'",
},
}}
/>
Typesafe Options [#typesafe-options]
When creating custom dialogs, you can ensure type safety to `dialogs.open` and `dialogs.push` by overriding the `DialogOptionsOverride` interface as shown below:
```tsx
declare module "@sparrowengg/twigs-react" {
export interface DialogOptionsOverride {
deleteModal: {
title: string;
onDelete: () => void;
};
}
}
```
# Drawer (/docs/components/drawer)
Import [#import]
```jsx
import { Drawer, DrawerHeader, DrawerFooter, DrawerBody } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const DrawerExample = () => {
const [isDrawerOpen, setIsDrawerOpen] = React.useState(false);
return (
<>
setIsDrawerOpen(false)}
placement="right"
>
Edit Profile
{/* Your content here */}
{/* Footer content */}
>
);
};
```
Props [#props]
Drawer [#drawer]
You can alter the placement of the Modal by changing the prop to `top` ,`right` ,`left` and `bottom`
void",
description: "Callback function called when the drawer is closed.",
},
placement: {
type: "'top' | 'left' | 'right' | 'bottom'",
description: "The placement of the drawer on the screen.",
default: "'right'",
},
finalFocusRef: {
type: "React.RefObject",
description: "Reference to the element that should receive focus when the drawer closes.",
},
portalContainer: {
type: "HTMLElement",
description: "Custom container element for the drawer portal.",
},
children: {
type: "ReactNode",
description: "The content to be rendered inside the drawer.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
# Dropdown (/docs/components/dropdown)
Import [#import]
```jsx
import {DropdownMenu,DropdownMenuTrigger,DropdownMenuContent,DropdownMenuItem,DropdownMenuCheckboxItem,DropdownMenuRadioGroup,DropdownMenuRadioItem,DropdownMenuItemIndicator,DropdownMenuLabel,DropdownMenuSeparator,DropdownMenuSub,DropdownMenuSubTrigger,DropdownMenuSubContent} from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const DropdownExample = () => {
const [bookmarksChecked, setBookmarksChecked] = React.useState(true);
const [urlsChecked, setUrlsChecked] = React.useState(false);
const [person, setPerson] = React.useState("pedro");
return (
New TabNew WindowMore ToolsSave Page As…
);
};
```
Examples [#examples]
Advanced customization [#advanced-customization]
The dropdown component supports a wide range of features you can customize the component based on your own needs.
Props [#props]
DropdownMenu [#dropdownmenu]
void",
description: "Event handler called when the open state of the dropdown menu changes.",
},
modal: {
type: "boolean",
description: "When true, prevents the dropdown menu from closing when clicking outside of it.",
default: "true",
},
dir: {
type: "'ltr' | 'rtl'",
description: "The reading direction of the dropdown menu when applicable.",
},
size: {
type: "'sm' | 'md'",
description: "The size of the dropdown menu items and content.",
default: "'md'",
},
}}
/>
DropdownMenuTrigger [#dropdownmenutrigger]
DropdownMenuContent [#dropdownmenucontent]
>",
description: "The distance in pixels from the boundary.",
default: "0",
},
arrowPadding: {
type: "number",
description: "The padding between the arrow and the edges of the content.",
default: "0",
},
sticky: {
type: "'partial' | 'always'",
description: "The sticky behavior on the align axis.",
default: "'partial'",
},
hideWhenDetached: {
type: "boolean",
description: "Whether to hide the content when the trigger becomes occluded.",
default: "false",
},
loop: {
type: "boolean",
description: "Whether focus should loop inside the content.",
default: "false",
},
onCloseAutoFocus: {
type: "(event: Event) => void",
description: "Event handler called when focus moves to the trigger after closing.",
},
onEscapeKeyDown: {
type: "(event: KeyboardEvent) => void",
description: "Event handler called when the escape key is pressed.",
},
onPointerDownOutside: {
type: "(event: PointerDownOutsideEvent) => void",
description: "Event handler called when a pointer event occurs outside the content.",
},
onFocusOutside: {
type: "(event: FocusOutsideEvent) => void",
description: "Event handler called when focus moves outside the content.",
},
onInteractOutside: {
type: "(event: PointerDownOutsideEvent | FocusOutsideEvent) => void",
description: "Event handler called when an interaction outside the content occurs.",
},
forceMount: {
type: "boolean",
description: "Used to force mounting when more control is needed.",
},
container: {
type: "HTMLElement",
description: "The container inside which to render the content.",
},
showArrow: {
type: "boolean",
description: "Whether to show an arrow pointing to the trigger.",
default: "false",
},
}}
/>
DropdownMenuItem [#dropdownmenuitem]
void",
description: "Event handler called when the item is selected.",
},
textValue: {
type: "string",
description: "Optional text used for typeahead purposes.",
},
}}
/>
DropdownMenuCheckboxItem [#dropdownmenucheckboxitem]
void",
description: "Event handler called when the checked state changes.",
},
disabled: {
type: "boolean",
description: "When true, prevents the user from interacting with the item.",
default: "false",
},
textValue: {
type: "string",
description: "Optional text used for typeahead purposes.",
},
}}
/>
DropdownMenuRadioGroup [#dropdownmenuradiogroup]
void",
description: "Event handler called when the value changes.",
},
disabled: {
type: "boolean",
description: "When true, prevents the user from interacting with the group.",
default: "false",
},
}}
/>
DropdownMenuRadioItem [#dropdownmenuradioitem]
DropdownMenuSub [#dropdownmenusub]
void",
description: "Event handler called when the open state of the submenu changes.",
},
}}
/>
DropdownMenuSubTrigger [#dropdownmenusubtrigger]
DropdownMenuSubContent [#dropdownmenusubcontent]
>",
description: "The distance in pixels from the boundary.",
default: "0",
},
arrowPadding: {
type: "number",
description: "The padding between the arrow and the edges of the content.",
default: "0",
},
sticky: {
type: "'partial' | 'always'",
description: "The sticky behavior on the align axis.",
default: "'partial'",
},
hideWhenDetached: {
type: "boolean",
description: "Whether to hide the content when the trigger becomes occluded.",
default: "false",
},
loop: {
type: "boolean",
description: "Whether focus should loop inside the content.",
default: "false",
},
onCloseAutoFocus: {
type: "(event: Event) => void",
description: "Event handler called when focus moves to the trigger after closing.",
},
onEscapeKeyDown: {
type: "(event: KeyboardEvent) => void",
description: "Event handler called when the escape key is pressed.",
},
onPointerDownOutside: {
type: "(event: PointerDownOutsideEvent) => void",
description: "Event handler called when a pointer event occurs outside the content.",
},
onFocusOutside: {
type: "(event: FocusOutsideEvent) => void",
description: "Event handler called when focus moves outside the content.",
},
onInteractOutside: {
type: "(event: PointerDownOutsideEvent | FocusOutsideEvent) => void",
description: "Event handler called when an interaction outside the content occurs.",
},
forceMount: {
type: "boolean",
description: "Used to force mounting when more control is needed.",
},
container: {
type: "HTMLElement",
description: "The container inside which to render the content.",
},
}}
/>
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
**Applicable**
\- DropdownMenu
**Applicable**
\- DropdownMenuTrigger
**Applicable**
\- DropdownMenuContent/ DropdownMenuSubContent
**Applicable**
\- DropdownMenuItem/ DropdownMenuCheckboxItem/ DropdownMenuRadioItem/ DropdownMenuSubTrigger
**Applicable**
\- DropdownMenuSubTrigger
# Flex (/docs/components/flex)
Import [#import]
```jsx
import { Flex } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const FlexExample = () => {
return (
Item 1
Item 2
Item 3
);
};
```
Props [#props]
Flex [#flex]
# FormHelperText (/docs/components/form-helper-text)
Import [#import]
```jsx
import { FormHelperText } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const FormHelperTextExample = () => {
return (
This is a helpful message for the userThis is an error messagePassword should have a minimum length of 6.
);
};
```
Props [#props]
FormHelperText [#formhelpertext]
# FormInput (/docs/components/form-input)
Import [#import]
```jsx
import { FormInput } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const FormInputExample = () => {
return (
);
};
```
Examples [#examples]
Variants and advanced features [#variants-and-advanced-features]
Explore all available features and varieties of the FormInput component.
Props [#props]
FormInput [#forminput]
ReactNode",
description: "Custom function to render the character counter. Overrides the default counter when provided.",
},
size: {
type: "'sm' | 'md' | 'lg' | 'xl'",
description: "Controls the size of the input field, affecting height, padding, and font size.",
default: "'md'",
},
variant: {
type: "'default' | 'filled'",
description: "Controls the visual style variant of the input field.",
default: "'default'",
},
disabled: {
type: "boolean",
description: "When true, disables the input field and prevents user interaction.",
default: "false",
},
leftIcon: {
type: "ReactElement",
description: "Icon element displayed on the left side of the input field.",
},
rightIcon: {
type: "ReactElement",
description: "Icon element displayed on the right side of the input field.",
},
leftElement: {
type: "ReactElement",
description: "Custom element displayed on the left side of the input field.",
},
rightElement: {
type: "ReactElement",
description: "Custom element displayed on the right side of the input field.",
},
errorBorder: {
type: "boolean",
description: "When true, applies error border styling to the input field.",
default: "false",
},
maxLength: {
type: "number",
description: "Maximum number of characters allowed in the input field.",
},
onChange: {
type: "(e: React.ChangeEvent | React.FormEvent) => void",
description: "Event handler called when the input value changes.",
},
value: {
type: "string",
description: "The controlled value of the input field.",
},
defaultValue: {
type: "string",
description: "The default value of the input field when uncontrolled.",
},
placeholder: {
type: "string",
description: "Placeholder text displayed when the input field is empty.",
},
type: {
type: "string",
description: "The type of the input field (e.g., 'text', 'password', 'email').",
default: "'text'",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
as: {
type: "React.ElementType",
description: "Allows you to render the component as a different HTML element or custom component.",
},
}}
/>
# FormLabel (/docs/components/form-label)
Import [#import]
```jsx
import { FormLabel } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const FormLabelExample = () => {
return (
Small LabelMedium LabelRequired FieldLabel with InfoOptional}>
Label with Right Addon
);
};
```
Props [#props]
FormLabel [#formlabel]
",
description: "Ref for the container div that wraps the label and addons.",
},
as: {
type: "React.ElementType",
description: "The element type to render the label as. Inherits from styled component.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
# Grid (/docs/components/grid)
Import [#import]
```jsx
import { Grid } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const GridExample = () => {
return (
Grid Item 1
Grid Item 2
Grid Item 3
Grid Item 4
);
};
```
Props [#props]
Grid [#grid]
",
description: "Array containing [rowGap, columnGap] values for grid spacing. Numbers are converted to design tokens.",
default: "[3, 3]",
},
repeat: {
type: "'fit' | 'fill'",
description: "Controls how auto-generated columns behave when using width prop. 'fit' creates columns that fit content, 'fill' fills available space.",
default: "'fit'",
},
children: {
type: "ReactNode",
description: "Grid items to be rendered within the grid container.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
# Heading (/docs/components/heading)
Import [#import]
```jsx
import { Heading } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const HeadingExample = () => {
return (
Heading 1 - BoldHeading 2 - SemiboldHeading 3 - MediumHeading 4 - RegularHeading 5 as H1Heading 6 as H2
);
};
```
Changing the `size` of the heading does not change its html element type. It only changes the font size of the heading. If you want to change the html element type, you can use the `as` prop.
Props [#props]
Heading [#heading]
# HoverCard (/docs/components/hover-card)
Import [#import]
```jsx
import {HoverCard,HoverCardTrigger,HoverCardContent,HoverCardPortal,HoverCardArrow} from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const HoverCardExample = () => {
return (
Hover Card Title
This is a detailed description that appears when you hover over the trigger element.
);
};
```
Props [#props]
HoverCard [#hovercard]
void",
description: "Event handler called when the open state changes.",
},
openDelay: {
type: "number",
description: "The duration in milliseconds from when the mouse enters the trigger until the hover card opens.",
default: "700",
},
closeDelay: {
type: "number",
description: "The duration in milliseconds from when the mouse leaves the trigger until the hover card closes.",
default: "300",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
HoverCardTrigger [#hovercardtrigger]
HoverCardContent [#hovercardcontent]
HoverCardPortal [#hovercardportal]
Data Attributes [#data-attributes]
**Applicable**
\- HoverCardTrigger
**Applicable**
\- HoverCardContent
# IconButton (/docs/components/icon-button)
Import [#import]
```jsx
import { IconButton } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const IconButtonExample = () => {
return (
}
aria-label="Edit"
size="md"
color="primary"
variant="solid"
/>
);
};
```
Props [#props]
IconButton [#iconbutton]
# Image (/docs/components/image)
Import [#import]
```jsx
import { Image } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const ImageExample = () => {
return (
);
};
```
Props [#props]
Image [#image]
# Overview (/docs/components)
import { ComponentHome } from "@/component/component-home";
# Input (/docs/components/input)
Import [#import]
```jsx
import { Input } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const InputExample = () => {
return (
);
};
```
Examples [#examples]
With Icons and Advanced Features [#with-icons-and-advanced-features]
Comprehensive example showing all Input features including icons, different states, and interactive elements.
Props [#props]
Input [#input]
| React.FormEvent) => void",
description: "Event handler called when the input value changes.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
className: {
type: "string",
description: "Additional CSS class names to apply to the input element.",
},
}}
/>
The Input component also accepts all standard HTML input attributes such as `type`, `placeholder`, `value`, `defaultValue`, `name`, `id`, `required`, `readOnly`, `autoComplete`, `autoFocus`, `pattern`, `min`, `max`, `step`, and event handlers like `onFocus`, `onBlur`, `onKeyDown`, etc.
# LineLoader (/docs/components/line-loader)
Import [#import]
```jsx
import { LineLoader } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const LineLoaderExample = () => {
return (
);
};
```
Props [#props]
LineLoader [#lineloader]
",
description: "Ref to access the underlying container element.",
},
}}
/>
# Link (/docs/components/link)
Import [#import]
```jsx
import { Link } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const LinkExample = () => {
return (
Link
);
};
```
Props [#props]
Link [#link]
The Link component also accepts all standard HTML anchor attributes such as `download`, `hreflang`, `media`, `ping`, `referrerPolicy`, `type`, and event handlers like `onClick`, `onFocus`, `onBlur`, etc.
# Pagination (/docs/components/pagination)
Import [#import]
```jsx
import { Pagination } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const PaginationExample = () => {
const [activePage, setActivePage] = useState(1);
return (
setActivePage(page)}
/>
);
};
```
Examples [#examples]
Advanced Pagination [#advanced-pagination]
Comprehensive example showing different pagination configurations with various sizes, sibling counts, and page ranges.
Props [#props]
Pagination [#pagination]
void",
description: "Event handler called when the page changes. Receives the click event and the new page number.",
},
size: {
type: "'xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'",
description: "The size of the pagination buttons. Affects the overall size and spacing of the pagination component.",
default: "'sm'",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
# Popover (/docs/components/popover)
Import [#import]
```jsx
import {Popover,PopoverTrigger,PopoverContent,PopoverClose,PopoverArrow,PopoverPortal,PopoverAnchor} from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const PopoverExample = () => {
return (
} color="primary">
Settings
SettingsConfigure your preferencesUpdate your profile settingsManage notifications
);
};
```
Props [#props]
Popover [#popover]
void",
description: "Event handler called when the open state changes.",
},
modal: {
type: "boolean",
description: "When true, the popover will be modal, preventing interaction with elements outside of it.",
default: "false",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
PopoverTrigger [#popovertrigger]
PopoverContent [#popovercontent]
void",
description: "Event handler called when focus moves into the component after opening.",
},
onCloseAutoFocus: {
type: "(event: Event) => void",
description: "Event handler called when focus moves to the trigger after closing.",
},
onEscapeKeyDown: {
type: "(event: KeyboardEvent) => void",
description: "Event handler called when the escape key is pressed.",
},
onPointerDownOutside: {
type: "(event: PointerDownOutsideEvent) => void",
description: "Event handler called when a pointer event occurs outside the content.",
},
onFocusOutside: {
type: "(event: FocusOutsideEvent) => void",
description: "Event handler called when focus moves outside the content.",
},
onInteractOutside: {
type: "(event: PointerDownOutsideEvent | FocusOutsideEvent) => void",
description: "Event handler called when an interaction outside the content occurs.",
},
asChild: {
type: "boolean",
description: "Change the default rendered element for the one passed as a child, merging their props and behavior.",
default: "false",
},
}}
/>
PopoverClose [#popoverclose]
PopoverArrow [#popoverarrow]
PopoverPortal [#popoverportal]
PopoverAnchor [#popoveranchor]
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
**Applicable**
\- PopoverTrigger
**Applicable**
\- PopoverContent
# Radio (/docs/components/radio)
Import [#import]
```jsx
import { RadioGroup, Radio } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const RadioExample = () => {
const [selectedValue, setSelectedValue] = useState("option1");
return (
Choose your preference:
Small radio option
Medium radio option
Disabled radio option
Default selection:
Default option
Comfortable option
Compact option
);
};
```
Props [#props]
RadioGroup [#radiogroup]
void",
description: "Event handler called when the value changes.",
},
disabled: {
type: "boolean",
description: "When true, prevents the user from interacting with the radio group.",
default: "false",
},
required: {
type: "boolean",
description: "When true, indicates that the user must fill in a value before submitting a form.",
default: "false",
},
name: {
type: "string",
description: "The name of the group. Submitted with its owning form as part of a name/value pair.",
},
orientation: {
type: "'horizontal' | 'vertical'",
description: "The orientation of the radio group.",
},
dir: {
type: "'ltr' | 'rtl'",
description: "The reading direction of the radio group. If omitted, assumes LTR (left-to-right) reading mode.",
},
loop: {
type: "boolean",
description: "Whether focus should loop around when the end/start is reached.",
default: "true",
},
asChild: {
type: "boolean",
description: "Change the default rendered element for the one passed as a child, merging their props and behavior.",
default: "false",
},
}}
/>
Radio [#radio]
",
description: "Ref to access the container element wrapping the radio and label.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
**Applicable**
\- RadioGroup
**Applicable**
\- Radio
# Select (/docs/components/select)
Import [#import]
```jsx
import { Select } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const SelectExample = () => {
const [selectedValue, setSelectedValue] = useState(null);
return (
);
};
```
Examples [#examples]
Comprehensive Features [#comprehensive-features]
The select component supports a wide range of features including multi-select, async loading, grouped options, custom styling, and form integration.
Props [#props]
Select [#select]
void",
description: "Callback function called when the selection changes.",
},
placeholder: {
type: "string",
description: "Placeholder text displayed when no option is selected.",
},
label: {
type: "string",
description: "Label text displayed above the select input.",
},
size: {
type: "'sm' | 'md' | 'lg' | 'xl'",
description: "The size of the select input, affecting its height and padding.",
default: "'sm'",
},
variant: {
type: "'default' | 'filled'",
description: "The visual variant of the select input.",
default: "'default'",
},
isMulti: {
type: "boolean",
description: "When true, allows multiple options to be selected.",
default: "false",
},
isClearable: {
type: "boolean",
description: "When true, displays a clear button to remove the current selection.",
default: "false",
},
isSearchable: {
type: "boolean",
description: "When true, allows users to search/filter options by typing.",
default: "true",
},
isAsync: {
type: "boolean",
description: "When true, enables async loading of options via loadOptions prop.",
default: "false",
},
isCreatable: {
type: "boolean",
description: "When true, allows users to create new options that don't exist in the list.",
default: "false",
},
loadOptions: {
type: "(inputValue: string) => Promise",
description: "Function to load options asynchronously. Used when isAsync is true.",
},
isDisabled: {
type: "boolean",
description: "When true, prevents user interaction with the select.",
default: "false",
},
error: {
type: "string",
description: "Error message to display below the select input.",
},
helperText: {
type: "string",
description: "Helper text to display below the select input.",
},
requiredIndicator: {
type: "boolean",
description: "When true, displays a required indicator (*) next to the label.",
default: "false",
},
info: {
type: "string | ReactNode",
description: "Info content to display as a tooltip next to the label.",
},
labelRightAddon: {
type: "ReactNode",
description: "Additional content to display on the right side of the label.",
},
showSeparator: {
type: "boolean",
description: "When true, shows a separator line between the value and dropdown indicator.",
default: "false",
},
dropdownIndicatorIcon: {
type: "ReactElement",
description: "Custom icon to use as the dropdown indicator.",
},
dropdownIndicatorPosition: {
type: "'left' | 'right'",
description: "Position of the dropdown indicator icon.",
default: "'right'",
},
menuPortalTarget: {
type: "HTMLElement | null",
description: "DOM element to render the dropdown menu in. Useful for z-index issues.",
},
className: {
type: "string",
description: "Additional CSS class name to apply to the select container.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
# Separator (/docs/components/separator)
Import [#import]
```jsx
import { Separator } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const SeparatorExample = () => {
return (
HomeAboutContact
);
};
```
Props [#props]
Separator [#separator]
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
# Slider (/docs/components/slider)
Import [#import]
```jsx
import { Slider } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const SliderExample = () => {
const [value, setValue] = useState([50]);
return (
Basic Slider
Current value: {value[0]}
);
};
```
Examples [#examples]
variants and customization [#variants-and-customization]
The slider component supports a wide range of features including range selection, custom stepping, multiple sizes, label placement, and component customization.
When customizing slider components using the `components` prop, it's recommended to render each component as a separate function outside the main component. This approach provides better performance, cleaner code organization, and proper state handling.
Instead of inline component definitions, create dedicated functions like `RenderedThumb`, `RenderedRange`, and `RenderedTrack` to ensure optimal re-rendering behavior and maintainability.
Props [#props]
Slider [#slider]
void",
description: "Event handler called when the slider value changes.",
},
onValueCommit: {
type: "(value: number[]) => void",
description: "Event handler called when the slider value is committed (e.g., when dragging ends).",
},
min: {
type: "number",
description: "The minimum value of the slider range.",
default: "0",
},
max: {
type: "number",
description: "The maximum value of the slider range.",
default: "100",
},
step: {
type: "number",
description: "The step increment for the slider value.",
default: "1",
},
minStepsBetweenThumbs: {
type: "number",
description: "The minimum number of steps between thumbs in a range slider.",
default: "0",
},
disabled: {
type: "boolean",
description: "When true, prevents the user from interacting with the slider.",
default: "false",
},
orientation: {
type: "'horizontal' | 'vertical'",
description: "The orientation of the slider.",
default: "'horizontal'",
},
dir: {
type: "'ltr' | 'rtl'",
description: "The reading direction of the slider. If omitted, assumes LTR (left-to-right) reading mode.",
},
inverted: {
type: "boolean",
description: "When true, inverts the slider direction.",
default: "false",
},
name: {
type: "string",
description: "The name of the slider. Submitted with its owning form as part of a name/value pair.",
},
form: {
type: "string",
description: "The form element that the slider belongs to.",
},
asChild: {
type: "boolean",
description: "Change the default rendered element for the one passed as a child, merging their props and behavior.",
default: "false",
},
size: {
type: "'sm' | 'md' | 'lg'",
description: "The size of the slider, affecting its height and thumb dimensions.",
default: "'md'",
},
labels: {
type: "{ left?: string | ReactNode; right?: string | ReactNode }",
description: "Custom labels for the left and right sides of the slider.",
},
labelPlacement: {
type: "'top' | 'bottom'",
description: "The placement of the labels relative to the slider track.",
default: "'top'",
},
components: {
type: "Record<'Track' | 'Range' | 'Thumb' | 'ThumbLeft' | 'ThumbRight', ElementType>",
description: "Custom component overrides for individual slider parts.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
# Split Button (/docs/components/split-button)
Import [#import]
```jsx
import { SplitButton } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const SplitButtonExample = () => {
return (
Basic Split Button
}>Edit
} />
);
};
```
Examples [#examples]
SplitButton with dropdown [#splitbutton-with-dropdown]
The split button component supports various sizes, colors, and can be combined with dropdown menus for advanced functionality.
Props [#props]
SplitButton [#splitbutton]
# Stack (/docs/components/stack)
Import [#import]
```jsx
import { Stack, VStack, HStack } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const StackExample = () => {
return (
Basic VStack
}>Home
}>Profile
}>Settings
);
};
```
Props [#props]
Stack [#stack]
",
description: "The direction of the stack. Can be a single value or responsive object for different breakpoints.",
default: "'column'",
},
gap: {
type: "string",
description: "The gap between stack items. Uses design system spacing tokens.",
default: "'$4'",
},
alignX: {
type: "'left' | 'center' | 'right'",
description: "Horizontal alignment of items within the stack.",
default: "'center'",
},
alignY: {
type: "'left' | 'center' | 'right'",
description: "Vertical alignment of items within the stack.",
default: "'center'",
},
wrap: {
type: "'wrap' | 'nowrap' | 'wrap-reverse'",
description: "Whether items should wrap to new lines when they exceed the container width.",
},
divider: {
type: "ReactElement",
description: "Custom divider element to display between stack items. Can be any React element with CSS styling.",
},
children: {
type: "ReactNode",
description: "The content to be arranged in the stack layout.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
# Stepper (/docs/components/stepper)
Import [#import]
```jsx
import { Stepper, StepperItem } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const StepperExample = () => {
const [activeStep, setActiveStep] = useState(0);
return (
Basic StepperStep 1Step 2Step 3
);
};
```
Examples [#examples]
Stepper customizations [#stepper-customizations]
The stepper component supports various customization options including custom components, different step states, and advanced styling.
When customizing stepper components using the `components` prop, it's recommended to render each component as a separate function outside the main component. This approach provides better performance, cleaner code organization, and proper state handling.
Instead of inline component definitions, create dedicated functions like `renderedContainer`, `renderedStepper`, and `renderedStep` to ensure optimal re-rendering behavior and maintainability.
Props [#props]
Stepper [#stepper]
void",
description: "Callback function called when the active step changes, either through navigation or direct step clicks.",
},
children: {
type: "ReactElement[]",
description: "Array of StepperItem components that define the steps in the stepper.",
},
components: {
type: "Object",
description: "Custom component overrides for Container, Step, and Separator elements.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
StepperItem [#stepperitem]
# Switch (/docs/components/switch)
Import [#import]
```jsx
import { Switch } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const SwitchExample = () => {
const [enabled, setEnabled] = useState(false);
return (
Enable notifications
);
};
```
Props [#props]
Switch [#switch]
void",
description: "Event handler called when the switch state changes.",
},
disabled: {
type: "boolean",
description: "When true, prevents the user from interacting with the switch.",
default: "false",
},
required: {
type: "boolean",
description: "When true, indicates that the switch is required in a form context.",
default: "false",
},
size: {
type: "'sm' | 'md'",
description: "The size of the switch component.",
default: "'sm'",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
**Applicable**
\- Switch, SwitchThumb
# Table (/docs/components/table)
Import [#import]
```jsx
import { Table, Thead, Tbody, Tr, Th, Td } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const TableExample = () => {
return (
Name
Email
Role
John Doe
john@example.com
Admin
Jane Smith
jane@example.com
User
);
};
```
Props [#props]
Table [#table]
Tr [#tr]
# Tabs (/docs/components/tabs)
Import [#import]
```jsx
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const TabsExample = () => {
return (
AccountPasswordMake changes to your account here.Change your password here.
);
};
```
Props [#props]
Tabs [#tabs]
void",
description: "Event handler called when the active tab changes.",
},
orientation: {
type: "'horizontal' | 'vertical'",
description: "The orientation of the tabs.",
default: "'horizontal'",
},
dir: {
type: "'ltr' | 'rtl'",
description: "The reading direction of the tabs when applicable.",
},
activationMode: {
type: "'automatic' | 'manual'",
description: "Controls whether tabs are activated automatically or manually.",
default: "'automatic'",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
TabsList [#tabslist]
TabsTrigger [#tabstrigger]
TabsContent [#tabscontent]
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
**Applicable**
\- Tabs, TabsList
**Applicable**
\- TabsTrigger
**Applicable**
\- TabsContent
# Text (/docs/components/text)
Import [#import]
```jsx
import { Text } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const TextExample = () => {
return (
Large Bold HeadingMedium Weight SubheadingRegular body text content
This is a long text that will be truncated with ellipsis
);
};
```
Props [#props]
Text [#text]
# Textarea (/docs/components/textarea)
Import [#import]
```jsx
import { Textarea } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const TextareaExample = () => {
const [value, setValue] = useState("");
return (
);
};
```
Examples [#examples]
Variants and Sizes [#variants-and-sizes]
Explore different visual styles and sizes for various use cases.
Props [#props]
Textarea [#textarea]
) => void",
description: "Event handler called when the textarea value changes.",
},
variant: {
type: "'default' | 'filled'",
description: "The visual style variant of the textarea.",
default: "'default'",
},
size: {
type: "'sm' | 'md' | 'lg' | 'xl'",
description: "The size of the textarea affecting padding and font size.",
},
resize: {
type: "'both' | 'vertical' | 'horizontal' | 'none'",
description: "Controls the textarea's resize ability.",
default: "'both'",
},
rows: {
type: "number",
description: "The number of visible text lines for the textarea.",
},
cols: {
type: "number",
description: "The number of visible character columns for the textarea.",
},
disabled: {
type: "boolean",
description: "When true, prevents the user from interacting with the textarea.",
default: "false",
},
maxLength: {
type: "number",
description: "The maximum number of characters allowed in the textarea.",
},
showCount: {
type: "boolean",
description: "When true, displays the character count.",
default: "false",
},
renderCounter: {
type: "({ length: number; maxLength?: number }) => ReactNode",
description: "Custom function to render the character counter.",
},
error: {
type: "string",
description: "Error message to display below the textarea.",
},
errorBorder: {
type: "boolean",
description: "When true, applies error styling to the textarea border.",
default: "false",
},
helperText: {
type: "string",
description: "Helper text displayed below the textarea.",
},
requiredIndicator: {
type: "boolean | ReactElement",
description: "Shows a required indicator next to the label.",
},
info: {
type: "string | ReactNode",
description: "Adds an info icon with tooltip beside the label.",
},
css: {
type: "object",
description: "Custom CSS styles for the textarea.",
},
className: {
type: "string",
description: "Custom CSS class name for the textarea.",
},
id: {
type: "string",
description: "The unique identifier for the textarea.",
},
}}
/>
# TimePicker (/docs/components/timepicker)
Import [#import]
```jsx
import { TimePicker } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const TimePickerExample = () => {
return (
);
};
```
Props [#props]
TimePicker [#timepicker]
void",
description: "Event handler called when the time value changes.",
},
hourCycle: {
type: "12 | 24",
description: "The hour cycle format for displaying time.",
default: "24",
},
granularity: {
type: "'hour' | 'minute' | 'second'",
description: "The granularity of the time picker determining which segments are shown.",
default: "'minute'",
},
isDisabled: {
type: "boolean",
description: "When true, prevents the user from interacting with the time picker.",
default: "false",
},
isReadOnly: {
type: "boolean",
description: "When true, prevents the user from editing the time value.",
default: "false",
},
isRequired: {
type: "boolean",
description: "When true, indicates that the time picker is required.",
default: "false",
},
autoFocus: {
type: "boolean",
description: "When true, automatically focuses the time picker on mount.",
default: "false",
},
placeholderValue: {
type: "TimeValue",
description: "The placeholder time value to display when the field is empty.",
},
minValue: {
type: "TimeValue",
description: "The minimum time value that can be selected.",
},
maxValue: {
type: "TimeValue",
description: "The maximum time value that can be selected.",
},
step: {
type: "number",
description: "The step value for incrementing/decrementing time values.",
},
formatOptions: {
type: "Intl.DateTimeFormatOptions",
description: "Formatting options for displaying the time value.",
},
locale: {
type: "string",
description: "The locale to use for formatting the time value.",
},
description: {
type: "string",
description: "A description for the time picker to provide additional context.",
},
errorMessage: {
type: "string",
description: "An error message to display when the time picker is invalid.",
},
validationState: {
type: "'valid' | 'invalid'",
description: "The validation state of the time picker.",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
# Toast (/docs/components/toast)
Import [#import]
```jsx
import { Toastr, toast } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const ToastExample = () => {
return (
<>
>
);
};
```
Examples [#examples]
Variants and Advanced Features [#variants-and-advanced-features]
Explore different toast variants, actions, promise handling, and advanced configurations.
Props [#props]
Toastr [#toastr]
toast [#toast]
void",
description: "Event handler called when the toast open state changes.",
},
showCloseButton: {
type: "boolean",
description: "Shows a close button inside the toast when set to true.",
default: "false",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
toast.promise [#toastpromise]
",
description: "The promise to handle with loading, success, and error states.",
},
loading: {
type: "ToastOptions",
description: "Toast configuration to show while the promise is loading.",
},
success: {
type: "(data: T) => ToastOptions",
description: "Function that returns toast configuration for successful promise resolution.",
},
error: {
type: "(error: any) => ToastOptions",
description: "Function that returns toast configuration for promise rejection.",
},
}}
/>
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
# Tooltip (/docs/components/tooltip)
Import [#import]
```jsx
import { TooltipProvider, Tooltip } from "@sparrowengg/twigs-react";
```
Usage [#usage]
```jsx
const TooltipExample = () => {
return (
);
};
```
Props [#props]
TooltipProvider [#tooltipprovider]
Tooltip [#tooltip]
void",
description: "Event handler called when the open state changes.",
},
delayDuration: {
type: "number",
description: "The duration from when the mouse enters a tooltip trigger until the tooltip opens.",
},
disableHoverableContent: {
type: "boolean",
description: "Prevents TooltipContent from remaining open when hovering over it.",
},
sideOffset: {
type: "number",
description: "The distance in pixels from the trigger.",
default: "0",
},
alignOffset: {
type: "number",
description: "An offset in pixels from the 'align' position.",
default: "0",
},
avoidCollisions: {
type: "boolean",
description: "When true, overrides the side and align preferences to prevent collisions.",
default: "true",
},
collisionBoundary: {
type: "Element | null | Array",
description: "The element used as the collision boundary.",
default: "[]",
},
collisionPadding: {
type: "number | Partial>",
description: "The distance in pixels from the boundary edges where collision detection should occur.",
default: "0",
},
arrowPadding: {
type: "number",
description: "The padding between the arrow and the edges of the content.",
default: "0",
},
sticky: {
type: "'partial' | 'always'",
description: "The sticky behavior on the align axis.",
default: "'partial'",
},
hideWhenDetached: {
type: "boolean",
description: "Whether the tooltip should be hidden when the trigger becomes detached.",
default: "false",
},
css: {
type: "object",
description: "Custom CSS styles for the component.",
},
}}
/>
Data Attributes [#data-attributes]
These props are used for the conditional rendering of the components
# Base Dropdown (/docs/editor/base-dropdown)
Description [#description]
The base dropdown is a component that can be used to create suggestion boxes inside the editor that are triggered by certain letters or phrases. The [Mentions plugin](../features/mentions) and [Hashtag plugin](../features/hashtags) are built on top of this component.
Props [#props]
triggerFunction [#triggerfunction]
```typescript
triggerFunction: (text: string) => MenuTextMatch | null;
```
`MenuTextMatch` is an object with the following properties:
```typescript
type MenuTextMatch = {
leadOffset: number;
matchingString: string;
replaceableString: string;
};
```
This function is used to trigger the dropdown. It receives the text that the user has typed and should return an object with the `leadOffset`, `matchingString`, and `replaceableString` properties. The `leadOffset` is the number of characters that the dropdown should be placed before the cursor. The `matchingString` is the string that the dropdown should match against. The `replaceableString` is the string that should be replaced when the user selects an item from the dropdown. If `null` is returned, the dropdown will not be triggered.
getResults [#getresults]
```typescript
getResults: (text: string | null) => Promise | TypeaheadMenuData[];
```
`TypeaheadMenuData` is an object with the following properties:
```typescript
type TypeaheadMenuData = {
value: string;
label: string;
[x: string]: unknown;
};
```
This functions is called after the dropdown is opened to get the results that should be displayed. It receives the `matchingString` from the `triggerFunction` as an argument. It should return an array of objects with the `value` and `label` properties. If the results are already available, you can return them directly. If the results are fetched asynchronously, you can return a promise that resolves to the results.
$createNode [#createnode]
```tsx
$createNode?: (data: TypeaheadOption) => TextNode;
```
`TypeaheadOption` is an object with the following properties:
```typescript
class TypeaheadOption {
key: string;
ref?: MutableRefObject;
data: TypeaheadMenuData;
constructor(key: string);
setRefElement(element: HTMLElement | null): void;
}
```
This function is used to create a node when an item is selected from the dropdown. It receives a `TypeaheadOption` object as an argument and should return a class that extends `TextNode` that will be inserted into the editor.
renderMenu [#rendermenu]
```tsx
renderMenu?: (args: {
anchorElementRef: React.MutableRefObject;
selectedIndex: number | null;
selectOptionAndCleanUp: (option: TypeaheadOption) => void;
setHighlightedIndex: (index: number) => void;
menuOptions: TypeaheadOption[];
}) => ReactPortal | React.JSX.Element | null;
```
Used to render a custom dropdown menu. If this prop is not provided, the default dropdown menu will be rendered. It receives an object with the following properties:
* `anchorElementRef`: A ref to the element that the dropdown should be anchored to. When using React portal, you should pass this ref to the portal target.
* `selectedIndex`: The index of the currently selected item in the dropdown.
* `selectOptionAndCleanUp`: A function that should be called when an item is selected from the dropdown. It receives a `TypeaheadOption` object as an argument.
* `setHighlightedIndex`: A function that should be called when an item is hovered over in the dropdown. It receives the index of the hovered item as an argument.
* `menuOptions`: An array of `TypeaheadOption` objects that should be rendered in the dropdown.
renderMenuItemContent [#rendermenuitemcontent]
```tsx
renderMenuItemContent?: (props: {
index: number;
isSelected: boolean;
option: TypeaheadOption;
}) => ReactNode;
```
Used to render custom content for each item in the dropdown. It receives an object with the following properties:
* `index`: The index of the item in the dropdown.
* `isSelected`: A boolean indicating whether the item is currently selected.
* `option`: The `TypeaheadOption` object that should be rendered.
Note that this prop is only used when the `renderMenu` prop is not provided.
onMenuItemSelect [#onmenuitemselect]
```tsx
onMenuItemSelect?: (option: TypeaheadOption, closeMenu?: () => void) => void | boolean;
```
A function that is called when an item is selected from the dropdown. It receives a `TypeaheadOption` object as an argument. If this function returns `true`, the default actions such as inserting the selected item into the editor and closing the dropdown will be prevented. You can use this function to implement custom behavior when an item is selected.
# Emoji (/docs/editor/emoji)
Description [#description]
Emoji plugin can be used to add emoji selector dropdown to the editor. The dropdown will be opened when the user types `:`. The dropdown will filter and show the emojis that contain the letters that the user typed.
By default all emojis in this [list](https://github.com/github/gemoji/blob/master/db/emoji.json) are available. You can customize the list of emojis by using a custom `getResults` function.
Custom Results [#custom-results]
You can use custom results by passing a `getResults` function to the `Emoji` plugin. The `getResults` function should return a list of emojis that match the search query.
```jsx
{
const data = someFunctionToGetEmojis(text);
return data.map(item => ({
label: item.label, // Will be shown in the dropdown list
value: item.value, // Will be inserted in the editor
}))
}}
/>
```
# Hashtags (/docs/editor/hashtags)
Description [#description]
Hashtags plugin can be used to add hashtags dropdown to the editor. The dropdown will be opened when the user types `#` followed by a letter. The dropdown will show the hashtags that start with the letter that the user typed.
# Setup (/docs/editor)
import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
Installation [#installation]
```bash
npm install @sparrowengg/twigs-editor-react
```
```bash
yarn add @sparrowengg/twigs-editor-react
```
```bash
pnpm add @sparrowengg/twigs-editor-react
```
The editor uses the following packages as peer dependencies, so you need to install them as well.
```bash
npm install lexical @lexical/code @lexical/html @lexical/link @lexical/list @lexical/react @lexical/rich-text @lexical/selection @lexical/utils
```
```bash
yarn add lexical @lexical/code @lexical/html @lexical/link @lexical/list @lexical/react @lexical/rich-text @lexical/selection @lexical/utils
```
```bash
pnpm add lexical @lexical/code @lexical/html @lexical/link @lexical/list @lexical/react @lexical/rich-text @lexical/selection @lexical/utils
```
Import [#import]
```jsx
import { Editor } from "@sparrowengg/twigs-editor-react";
```
Usage [#usage]
Handling Data [#handling-data]
The `onChange` property can be used to get the editor state, which can be stored in a state variable. You can also pass `initialEditorState` prop to set the initial state of the editor.
The editor is not two way binded, so even if you update the `initialEditorState` after initial render, the editor will not update.
Another method to get / set the editor state is by using the `dataManagementRef` prop. This prop takes a ref object, which can be used to get / set the editor state.
```tsx
import { DataManagementPluginHandle } from "@sparrowengg/twigs-editor-react";
const Component = () => {
// DataManagementPluginHandle used for typescript
const dataManagementRef = useRef(null);
return (
);
};
```
Currently `getDataAsync` returns the data in HTML and JSON Format, however to set the data, you can only use `setHtml` method. Other methods and helpers will be introduced soon.
# Link (/docs/editor/link)
DialogLinkEditor [#dialoglinkeditor]
Link feature is included by default in the editor and it allows you to add hyperlinks. However in order to get the link editor modal, you need to add the `DialogLinkEditor` plugin to the editor.
ConvertSelectionToLinkOnPastePlugin [#convertselectiontolinkonpasteplugin]
This plugin converts the selected text to a link if the pasted text is a URL.
The example below demonstrates both the `DialogLinkEditor` and `ConvertSelectionToLinkOnPastePlugin` working together. You can:
1. **Add links manually**: Use the link tool in the toolbar to add hyperlinks
2. **Convert pasted URLs**: Select text and paste a URL to automatically convert it to a link
Example [#example]
# Mentions (/docs/editor/mentions)
Description [#description]
Mentions plugin can be used to add mentions dropdown to the editor. The dropdown will be opened when the user types `@` followed by a letter in the editor. The dropdown will show the list of users that can be mentioned based on a custom filter logic.
Props [#props]
getResults [#getresults]
Type: `(searchString: string) => Promise<{label: string; value: string; [x: string]: any }[]> | {label: string; value: string; [x: string]: any }[]`
This function is called when the user types `@` followed by a letter in the editor. The function should return a list of users that can be mentioned based on the search string. The function should return an array of objects with `label` and `value` keys. The `label` key is used to display the user name in the dropdown and the `value` key is used to replace the mention in the editor.
triggerFunction [#triggerfunction]
Type: `(searchString: string) => MenuTextMatch | null`
`MenuTextMatch` has the following type:
```ts
interface MenuTextMatch {
leadOffset: number;
matchingString: string;
replaceableString: string;
}
```
This function can be used to override the default trigger function.
Refer [base dropdown](../helpers/base-dropdown.mdx) for more details about the props of the dropdown.
# Slash Commands (/docs/editor/slash-command-feature)
Creating the plugin [#creating-the-plugin]
The plugin will be built on top of the `EditorLookupDropdownBase` component. For more information about the `EditorLookupDropdownBase` component, see the [Lookup dropdown page](../helpers/base-dropdown.mdx).
As described in the lookup dropdown page, we first need to create a regex based trigger function that will be used to detect when the user types a slash command. The trigger function will be used to open the dropdown when the user types `/`.
```tsx
import { MenuTextMatch } from "@lexical/react/LexicalTypeaheadMenuPlugin";
const PUNCTUATION =
"\\.,\\+\\*\\?\\$\\@\\|#{}\\(\\)\\^\\-\\[\\]\\\\/!%'\"~=<>_:;";
const TRIGGERS = ["/"].join("");
// Chars we expect to see in a slash command (non-space, non-punctuation).
const VALID_CHARS = `[^${TRIGGERS}${PUNCTUATION}\\s]`;
const LENGTH_LIMIT = 75;
const SlashRegex = new RegExp(
"(^|\\s|\\()(" +
`[${TRIGGERS}]` +
`((?:${VALID_CHARS}){0,${LENGTH_LIMIT}})` +
")$"
);
export function checkForSlashes(
text: string,
minMatchLength: number
): MenuTextMatch | null {
const match = SlashRegex.exec(text);
if (match !== null) {
// The strategy ignores leading whitespace but we need to know it's
// length to add it to the leadOffset
const maybeLeadingWhitespace = match[1];
const matchingString = match[3];
if (matchingString.length >= minMatchLength) {
return {
leadOffset: match.index + maybeLeadingWhitespace.length,
matchingString,
replaceableString: match[2],
};
}
}
return null;
}
function getPossibleQueryMatch(text: string): MenuTextMatch | null {
return checkForSlashes(text, 0);
}
```
Now we can create the component that uses `EditorLookupDropdownBase` to create the dropdown. The component will be responsible for rendering the dropdown and handling the selection of items from the dropdown.
```tsx
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { $getSelection, $isTextNode } from "lexical";
import { EditorLookupDropdownBase } from "@sparrowengg/twigs-editor-react";
export const SlashesPlugin = () => {
const [editor] = useLexicalComposerContext();
const commands = [
{ label: "Show alert", value: "show-alert" },
{ label: "Show prompt", value: "show-prompt" },
{ label: "Show confirm", value: "show-confirm" },
];
return (
{
return commands.filter((command) =>
command.label
.toLowerCase()
.includes((searchString || "").toLowerCase())
);
}}
triggerFunction={(text) => getPossibleQueryMatch(text)}
onMenuItemSelect={(item, closeMenu) => {
let promptValue = '';
// Perform different action based on the selected command
if (item.data.value === "show-alert") {
alert("Alert!");
} else if (item.data.value === "show-prompt") {
promptValue = prompt("Prompt!");
} else if (item.data.value === "show-confirm") {
confirm("Confirm!");
}
// You can also make changes to the editor content based on a command
// This can also include inserting custom nodes, such as MentionNode
editor.update(() => {
const selection = $getSelection();
const nodes = selection?.getNodes();
if (nodes?.[0] && $isTextNode(nodes[0])) {
if (promptValue) {
nodes[0].setTextContent(promptValue);
} else {
nodes[0].setTextContent('');
}
}
});
closeMenu();
// Return true means that we don't want to perform the default action after menu item selection
return true;
}}
/>
);
};
```
Putting it all together [#putting-it-all-together]
# Toolbar (/docs/editor/toolbar)
Import [#import]
It can be added by importing `EditorToolbar` component from `@sparrowengg/twigs-react` and adding it to the editor. By default the toolbar contains the following tools:
* Format tool (headings / paragraph)
* Bold
* Italic
* Underline
* Unordered List
* Ordered List
* Link (Requires `` to be added)
* Codeblock
* Inline code
* Text align tool
```tsx
```
Customizing [#customizing]
Using tools prop [#using-tools-prop]
The tools shown in the toolbar can be customized by passing the `tools` prop. The prop is an array that can contain the following values: `format`, `bold`, `italic`, `underline`, `unordered-list`, `ordered-list`, `link`, `codeblock`, `code`, `text-align`; it also supports an object in the following format for custom components:
```tsx
{
renderComponent: (props: { editor: LexicalEditor }) => ReactNode;
}
```
This can be used to change the order of tools or to add custom tools to the toolbar. The `editor` prop is the instance of the editor, and it can be used to interact with the editor.
Using child components [#using-child-components]
Components can be directly added as children to the `EditorToolbar` component. You can use the default tools by importing them from `@sparrowengg/twigs-react`.
```tsx
import {
Editor,
EditorToolbar,
BoldTool,
ItalicTool,
} from "@sparrowengg/twigs-editor-react";
import { Button, Flex } from "@sparrowengg/twigs-react";
```
You can also add custom components to the default tools by passing a `renderButton` prop.
# Getting Started (/docs/getting-started)
import { Callout } from 'fumadocs-ui/components/callout';
import { Card, Cards } from 'fumadocs-ui/components/card';
import { Steps, Step } from 'fumadocs-ui/components/steps';
import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
import img from '@/assets/images/nextjs.svg';
Overview [#overview]
Twigs is designed to be the perfect foundation for building modern web applications. Our component library combines the power of **Radix UI's** accessibility-first approach with **Stitches'** powerful CSS-in-JS capabilities to deliver components that are both developer-friendly and user-accessible.
Installation [#installation]
Install the Package [#install-the-package]
Install Twigs React components using your preferred package manager
```bash
npm install @sparrowengg/twigs-react @sparrowengg/twigs-react-icons
```
```bash
yarn add @sparrowengg/twigs-react @sparrowengg/twigs-react-icons
```
```bash
pnpm add @sparrowengg/twigs-react @sparrowengg/twigs-react-icons
```
Install Editor (Optional) [#install-editor-optional]
Editor provides rich text editing capabilities
```bash
npm install @sparrowengg/twigs-editor-react
```
```bash
yarn add @sparrowengg/twigs-editor-react
```
```bash
pnpm add @sparrowengg/twigs-editor-react
```
ThemeProvider [#themeprovider]
Setting up the `ThemeProvider` in your root file
```jsx
// [!code highlight]
import { ThemeProvider, defaultTheme } from '@sparrowengg/twigs-react'
function App() {
return (
// [!code highlight]
)
}
```
For the best experience, wrap `ThemeProvider` in your `main.jsx` or `main.tsx` file at the root of your application. This ensures all components have access to the theme.
Quick Start [#quick-start]
Here's a simple example to get you started:
Framework Integration [#framework-integration]
} href="/docs/getting-started/nextjs">
Learn how to integrate Twigs with NextJS applications. Configure the customTheme setup
Architecture [#architecture]
Twigs is built on top of **Radix UI**, a low-level UI primitive library that provides:
* **Unstyled, accessible components** that work with any styling solution
* **Keyboard navigation** and focus management
* **Screen reader support** with proper ARIA attributes
* **Composable APIs** for maximum flexibility
Every component in Twigs is powered by **Stitches**, a CSS-in-JS library that provides:
* **CSS Props**: Style components directly through props
* **Design Tokens**: Consistent spacing, colors, and typography
* **Variant System**: Easy component customization
* **Near-zero Runtime**: CSS is generated at build time for optimal performance
**CSS Props Support**: Every component in Twigs supports CSS props thanks to Stitches. You can style any component directly through the `css` prop, making customization effortless and intuitive.
Next Steps [#next-steps]
Now that you have Twigs installed, explore our components:
* **[Theming](/docs/getting-started/theming)** - Learn how to customize themes and design tokens
* **[Components](/docs/components)** - Browse our comprehensive component library
* **[Icons](/docs/icons)** - Discover our icon library and usage patterns
# NextJS (/docs/getting-started/nextjs)
Next.js supports two routing architectures: **App Router** (Next.js 13+) and **Pages Router** (Next.js 12 and below). Choose the setup that matches your Next.js version.
App Router [#app-router]
App Router uses the `app` directory with server components by default. Create a client component provider to wrap your app with ThemeProvider.
Create Theme Provider [#create-theme-provider]
Create a client component to wrap your app with ThemeProvider:
```tsx title="app/providers.tsx"
'use client';
import { ThemeProvider } from '@sparrowengg/twigs-react';
import type { ReactNode } from 'react';
export default function Providers({
children,
theme
}: {
children: ReactNode;
theme?: any;
}) {
return (
// [!code highlight]
{children}
);
}
```
Wrap Layout [#wrap-layout]
Wrap your root layout with the provider:
```tsx title="app/layout.tsx"
import Providers from './providers';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
// [!code highlight]
{children}
);
}
```
**Why use a provider?** App Router's `layout.tsx` is a server component by default and cannot use client components like `ThemeProvider` directly. Creating a separate `'use client'` provider component allows you to use `ThemeProvider` while keeping your layout as a server component.
With Custom Theme [#with-custom-theme]
Use `twigs.config.js` or pass theme directly:
```tsx title="app/layout.tsx"
import Providers from './providers';
import twigsConfig from '../twigs.config.js'; // [!code highlight]
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
// [!code highlight]
{children}
);
}
```
Pages Router [#pages-router]
Pages Router uses `pages` directory. Wrap your app with ThemeProvider in `_app.tsx`.
Basic Setup [#basic-setup]
Wrap your app in `_app.tsx`:
```tsx title="pages/_app.tsx"
import { ThemeProvider } from '@sparrowengg/twigs-react'; // [!code highlight]
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
// [!code highlight]
);
}
```
**Why wrap directly?** Pages Router supports both server and client components, `_app.tsx` handles client-side routing and state. So you can use `ThemeProvider` directly without needing a separate provider wrapper.
With Custom Theme [#with-custom-theme-1]
Use `twigs.config.js` or pass theme directly:
```tsx title="pages/_app.tsx"
import { ThemeProvider } from '@sparrowengg/twigs-react';
import twigsConfig from '../twigs.config.js'; // [!code highlight]
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
// [!code highlight]
);
}
```
**Server-Side Rendering:** For information about server-side rendering with Twigs, including how to handle styling and theme configuration in SSR environments, see the [Server-Side Rendering](/docs/getting-started/theming#server-side-rendering) section in the Theming guide.
# Theming (/docs/getting-started/theming)
import { DefaultThemeContainer } from "@/component/theming";
import { TwigsColorPalette } from "@/component/theming/colors";
import { AllFontSizes } from "@/component/theming/font-sizes";
import { AllFontWeights } from "@/component/theming/font-weights";
import { AllLineHeights } from "@/component/theming/line-heights";
import { AllBordersAndRadii } from "@/component/theming/borders-and-radii";
import { AllSpaces } from "@/component/theming/spaces";
import { AllSizes } from "@/component/theming/sizes";
import { DynamicCodeBlock } from "fumadocs-ui/components/dynamic-codeblock";
Theme Setup [#theme-setup]
Twigs uses [Stitches](https://stitches.dev/) as its styling engine. The theme system consists of:
1. **Default Theme** ([`stitches.config.ts`](https://github.com/surveysparrow/twigs/blob/master/packages/react-components/src/stitches.config.ts)) - Contains all default theme values and configuration functions for initial setup.
2. **ThemeProvider** - Applies and merges custom themes with defaults.
ThemeProvider Architecture [#themeprovider-architecture]
The `ThemeProvider` automatically:
* **Runs global styles on mount** - Applies CSS reset and base styles (box-sizing, font-family, etc.)
* **Merges themes** - Combines your custom theme with defaults
* **Applies theme** - Adds merged theme as CSS class to document root
```tsx
// Simplified ThemeProvider behavior
const mergeThemes = (outerTheme: any, theme: any) => {
return { ...outerTheme, ...theme };
};
useEffect(() => {
// [!code highlight]
globalStyles(); // Runs once on mount - applies CSS reset & base styles
}, []);
```
**Key behaviors:**
* Global styles applied automatically (CSS reset, box-sizing, font-family)
* Custom values **override** defaults at the same key level
* Nested objects are **merged** (not replaced)
* Theme is applied as a CSS class to document root
* Changes update all components automatically
Custom themes merge with defaults - only specified keys override:
```jsx
// Default theme
{ colors: { primary: "#2E666D", secondary: "#363A43" } }
// Custom theme
{ colors: { primary: "#FF5733" } }
// Result: primary overridden, secondary preserved
// [!code highlight]
{ colors: { primary: "#FF5733", secondary: "#363A43" } }
```
Basic Setup [#basic-setup]
Wrap your application with `ThemeProvider`:
```jsx
// [!code highlight]
import { ThemeProvider, defaultTheme } from '@sparrowengg/twigs-react'
function App() {
return (
// [!code highlight]
)
}
```
For the best experience, wrap `ThemeProvider` in your `main.jsx` or `main.tsx` file at the root of your application. This ensures all components have access to the theme.
Customizing the Theme [#customizing-the-theme]
Using Custom File (Recommended) [#using-custom-file-recommended]
Create a `twigs.config.js` or `twigs.config.ts` file in your project root:
```jsx
export default {
theme: {
extends: {
// overridding the default colors
colors: {
primary500: "blue",
secondary500: "green",
},
// overridding the default font
fonts: {
body: "DM Sans, sans-serif",
heading: "DM Sans, sans-serif",
},
}
}
};
```
Make sure to import your custom fonts (e.g., `DM Sans`) in your project via Google Fonts, a CDN, or local files. The font will not apply unless it's properly loaded in your application.
Import and use in `ThemeProvider`:
```jsx
import { ThemeProvider } from '@sparrowengg/twigs-react';
import twigsConfig from '../twigs.config.js'; // [!code highlight]
function App() {
return (
// [!code highlight]
);
}
```
**Benefits:** Better organization, [IntelliSense](https://marketplace.visualstudio.com/items?itemName=SurveySparrow.twigs-intellisense) support, version control friendly.
Direct Theme Prop [#direct-theme-prop]
Pass theme object directly:
```jsx
import { ThemeProvider } from '@sparrowengg/twigs-react';
const customTheme = {
colors: {
primary: "#2E666D",
},
fontSizes: {
md: "1.125rem",
},
};
function App() {
return (
// [!code highlight]
);
}
```
Dark Mode Configuration [#dark-mode-configuration]
Add dark mode support using the `createTheme` function, which generates a theme class that can be applied to override specific tokens while inheriting others from your default theme.
Add Dark Theme [#add-dark-theme]
Extend your `twigs.config` by altering the default export to include dark theme configuration
```jsx
// [!code highlight]
import { createTheme } from '@sparrowengg/twigs-react';
const config = {
theme: {
extends: {
colors: {
primary500: "blue",
secondary500: "green",
},
fonts: {
body: "DM Sans, sans-serif",
},
},
// [!code highlight]
dark: {
colors: {
primary500: "#5CB5BD",
secondary500: "#A3AEBD",
// Override colors for dark mode
},
}
}
};
export default config;
// [!code highlight]
export const darkTheme = createTheme('dark-theme', config.theme.dark);
```
Apply Theme class [#apply-theme-class]
Use `ThemeProvider` for your default theme and apply the `darkTheme` class conditionally when needed.
```jsx
import config, { darkTheme } from '../twigs.config.js'; // [!code highlight]
import { useState } from "react";
function App() {
const [isDark,setIsDark] = useState(false);
return (
// [!code highlight]
);
}
```
Apply the dark theme class in your `App.tsx` or `App.jsx` file to ensure consistent theming across your entire application.
**How it works:**
* `ThemeProvider` applies your custom theme via the `theme` prop.
* Dark theme class is applied to the wrapper div when `isDark` is true.
* Both work together: custom theme + dark mode overrides.
* Only tokens defined in dark theme override; others use your custom/default theme.
See the [Stitches Custom Theming documentation](https://stitches.dev/docs/theming) for complete details regarding the dark mode configuration.
Theme Variables [#theme-variables]
Reference theme variables using the `$` prefix in the `css` prop:
```jsx
Twigs
```
Reference [#reference]
| Category | Example | Keys |
| ------------- | -------------------------- | -------------- |
| Colors | `$primary500`, `$white900` | Named |
| Spacing | `$4`, `$10`, `$24` | Numeric (1-50) |
| Font Sizes | `$sm`, `$md`, `$xl` | Named |
| Font Weights | `$4`, `$6`, `$7` | Numeric (1-9) |
| Border Radius | `$sm`, `$md`, `$pill` | Named |
| Sizes | `$10`, `$20`, `$25` | Numeric (1-34) |
Breakpoints [#breakpoints]
Twigs includes pre-configured breakpoints for responsive design. Use them with the `@` prefix in your `css` prop:
```jsx
Responsive Content
```
**Available breakpoints:**
* `@screen-xxs` - 320px
* `@screen-xs` - 480px
* `@screen-sm` - 640px
* `@screen-md` - 768px
* `@screen-lg` - 1024px
* `@screen-xl` - 1280px
* `@screen-2xl` - 1536px
Breakpoints are pre-configured in the Twigs setup and cannot be overridden through `ThemeProvider`. For custom breakpoints, use standard CSS media queries directly in your `css` prop.
Scale-Prefixed Tokens [#scale-prefixed-tokens]
By default, theme tokens are automatically mapped to their corresponding CSS properties. For example, `$primary500` works in color-related properties, and `$6` works in spacing properties.
When you need to explicitly target a token from a specific scale (e.g., using a color token in a non-color property), use scale-prefixed syntax:
```jsx
Content
```
**Scale-prefixed syntax:** `$scaleName$tokenName`
* `$colors$primary500` - Explicitly use color token
* `$space$6` - Explicitly use spacing token
* `$sizes$10` - Explicitly use size token
For complete property mapping details, see the [Stitches Property Mapping documentation](https://stitches.dev/docs/tokens#property-mapping).
Color Opacity Utils [#color-opacity-utils]
Twigs provides utility functions to apply opacity to theme colors. These utils convert theme colors to RGBA format with your specified opacity:
```jsx
Content with opacity
```
**Available utils:**
* `backgroundColorOpacity: [colorToken, opacity]` - Apply opacity to background color
* `colorOpacity: [colorToken, opacity]` - Apply opacity to text color
* `borderColorOpacity: [colorToken, opacity]` - Apply opacity to border color
**Opacity value:** Number between 0 (transparent) and 1 (opaque).
Advanced Styling [#advanced-styling]
When modifying default colors or styles of Twigs components, you may need to use `!important` to override component defaults that have higher CSS specificity:
```jsx
```
**Best practice:** Try without `!important` first. Only add it when the style isn't being applied due to specificity conflicts.
Practical Example [#practical-example]
Server-Side Rendering [#server-side-rendering]
Twigs supports server-side rendering (SSR) through the `getCssText` function, which extracts all CSS needed for hydration.
Basic Setup [#basic-setup-1]
Here's an example of `SSR` with Next.js
```tsx title="pages/_document.tsx"
import { Html, Head, Main, NextScript } from "next/document";
import { getCssText } from "@sparrowengg/twigs-react"; // [!code highlight]
export default function Document() {
return (
// [!code highlight]
);
}
```
**Important:** Always include `id="stitches"` on the style tag for optimal hydration.
**How it works:**
* `getCssText()` returns all CSS generated by Stitches
* Styles are injected server-side to prevent FOUC (Flash of Unstyled Content)
Complete Theme Reference [#complete-theme-reference]
Colors [#colors]
Click any color swatch to copy its token name, Use these values in the css prop to style your components.
Font Sizes [#font-sizes]
Font Weights [#font-weights]
Line Heights [#line-heights]
Border Widths and Radius [#border-widths-and-radius]
Spacing [#spacing]
Space values for padding, margin, gap (numeric keys 1-50):
Sizing [#sizing]
Size tokens for height, width, dimensions (numeric keys 1-34):
Default Theme Object [#default-theme-object]
# Overview (/docs/icons)
Installation [#installation]
```bash
npm i @sparrowengg/twigs-react-icons
```
Usage [#usage]
```jsx
import { AddColumnIcon } from "@sparrowengg/twigs-react-icons";
const App = () => {
return ;
};
```
Props [#props]
Icons [#icons]
All icons support the following props:
# Icons Library (/docs/icons/playground)
import { Customization } from "@/component/icons/customization";
Playground [#playground]
# Extensions (/docs/tools)
Official [#official]
* [Twigs Intellisense](https://marketplace.visualstudio.com/items?itemName=SurveySparrow.twigs-intellisense) - VSCode extension for Twigs. Improves the code completion experience by providing token values from theme config.
Community [#community]
* [Twigs Color Match](https://twigs-color-matcher.vercel.app/) - Online tool to find the closest Twigs color token.
* [Twigs Themer](https://twigs-themer.vercel.app/) - Online tool to test your theme.
# Upgrade Guide (/docs/tools/upgrade-guide)
Select Versions [#select-versions]
import { UpgradeGuide } from "@/component/upgrade-guide/upgrade-guide";