A modern, customizable UI component library for React Native
Modern, customizable, and production-ready UI components for React Native & Expo.



- Built for Expo & React Native
- Smooth animations
- Fully customizable tabs and styling
- Icon support for each tab
- Dynamic width ratio support
---
π Exciting new features are on the way!
π οΈ Planned updates include fresh hooks and components with improved performance.
1. Data Fetching usePaganation hook
2. Carousel
3. ImageStack
4. Tabs
---
- π Portfolio Website
- β¨ Facebook
- π» GitHub
---
. If you encounter an error, stop the Terminal and run the project again.
---
. [β
] Modals
. [β
] BottomSheet
. [β
] SelectList
. [β
] Popup
. [β
] RangeSlider
. [β
] RadialProgress
. [β
] RadioCircle
. [β
] Table
. [β
] OTPInput
. [β
] Toast
. [β
] InternetStatusToast
. [β
] Input
. [β
] Button
. [β
] CheckBox
. [β
] Switch
. [β
] StarRating
. [β
] NumberCount
. [β
] Typo
. [β
] Divider
. [β
] useBackExit
. [β
] useScrollShadowAnimation
. [β
] TabSlider
. [β
] ExpandableText
---
``bash`
npm install react-native-modern-elementsor
yarn add react-native-modern-elements
`tsx
import React from "react";
import { TouchableOpacity, View, Text } from "react-native";
import { Modals } from "react-native-modern-elements";
const ModalsUsing = () => {
const [modalVisible, setModalVisible] = React.useState(false);
const handleShowProduct = (product: any) => {
setModalVisible(product);
};
return (
style={styles.openButton}
>
animation="LeftToCenterCloseToRight"
onClose={() => setModalVisible(false)}
modalContainer={{
width: "90%",
height: verticalScale(250),
paddingHorizontal: verticalScale(20),
paddingTop: verticalScale(20),
paddingBottom: verticalScale(17),
}}
>
Success
Congratulations registration was successful
);
};
export default ModalsUsing;
const styles = StyleSheet.create({
header: {
width: "100%",
height: 40,
alignItems: "flex-end",
},
openButton: {
backgroundColor: "#F194FF",
borderRadius: 20,
padding: 10,
elevation: 2,
},
buttonText: {
color: "white",
fontWeight: "bold",
textAlign: "center",
},
});
`
`tsx
import { colors } from "@/src/constants/theme";
import { verticalScale } from "@/src/utils/styling";
import React from "react";
import { Text, TouchableOpacity, View } from "react-native";
import CancelIcon from "@/src/assets/svg/CancelIcon";
import {
BottomSheet,
BottomSheetHandle,
Input,
Divider,
Button,
} from "react-native-modern-elements";
const BottomSheet = () => {
const refScrollable = React.useRef
const showUpdatedPasswordbs = () => {
refScrollable.current?.open();
};
return (
style={{
backgroundColor: "blue",
padding: 10,
borderRadius: 5,
marginTop: 10,
}}
>
// snapPoints={snapPoints}
snapPoints={["30%", "50%"]}
draggable
// dragOnContent
closeOnPressBack={false}
closeOnPressMask={false}
wrapperColors={{ backgroundColor: "transparent" }}
defaultOpen={true}
showIndicator={true}
mainContainer={{
backgroundColor: "white",
shadowColor: "gray",
elevation: 20,
shadowRadius: 10,
borderTopRightRadius: 0,
borderTopLeftRadius: 0,
}}
// openDuration={310}
>
width: "90%",
alignSelf: "center",
// height: verticalScale(440),
}}
>
{/ header /}
className="w-16 h-10 flex-row items-center justify-end "
>
width: "90%",
alignSelf: "center",
height: verticalScale(335),
}}
>
lable="Current Password"
lableStyle={{ color: colors?.black_70, fontSize: 14 }}
placeholder="Password"
secureTextEntry
containerStyle={{
borderRadius: 50,
backgroundColor: colors.black_10,
overflow: "hidden",
}}
iconStyle={{ marginLeft: verticalScale(4) }}
/>
lable="New Password"
lableStyle={{ color: colors?.black_70, fontSize: 14 }}
placeholder="New Password"
secureTextEntry
containerStyle={{
borderRadius: 50,
backgroundColor: colors.black_10,
overflow: "hidden",
}}
iconStyle={{ marginLeft: verticalScale(4) }}
/>
lable="Re-type New Password"
lableStyle={{ color: colors?.black_70, fontSize: 14 }}
placeholder="Re-type New Password"
secureTextEntry
containerStyle={{
borderRadius: 50,
backgroundColor: colors.black_10,
overflow: "hidden",
}}
iconStyle={{ marginLeft: verticalScale(4) }}
/>
);
};
export default BottomSheet;
`
`tsx
import { colors } from "@/src/constants/theme";
import React from "react";
import { Text, View } from "react-native";
import { Table, Divider } from "react-native-modern-elements";
const TableUsing = () => {
const [data, setData] = React.useState
React.useEffect(() => {
const url = "https://api.escuelajs.co/api/v1/products";
fetch(url)
.then((res) => res.json())
.then((data) => {
const limitedData = data?.slice(0, 20);
setData(limitedData);
});
}, []);
return (
barStyle="dark-content"
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
padding: 16,
}}
>
tableStyles={{
borderTopRightRadius: 10,
borderTopLeftRadius: 10,
borderBottomRightRadius: 5,
borderBottomLeftRadius: 5,
borderWidth: 1,
borderColor: colors?.black_15,
}}
contentContainerStyle={{ paddingBottom: 200 }}
divider={true}
// dividerHeight={0.7}
// dividerColors={colors?.black_5}
dividerWight={"100%"}
headerShadowStyle={{
shadowColor: "purple",
shadowOpacity: 0.3,
shadowOffset: { width: 0, height: 3 },
shadowRadius: 5,
elevation: 6,
}}
cellRowStyle={{
paddingHorizontal: 10,
paddingVertical: 10,
flexDirection: "row",
}}
showsVerticalScrollIndicator={false}
HeaderRowStyle={{ backgroundColor: colors?.black_10 }}
headerTexts={{
fontSize: 16,
fontWeight: "500",
paddingVertical: 2,
color: colors?.black_70,
}}
onRowPress={(item, rowIndex) => {
router.push("/(screens)/(tabs)/account");
}}
// data={users}
data={data}
defaultAlign="left" // Global text alignment for all columns
columns={[
{
label: "Product",
key: "Product",
// Control width of this column
align: "left", // Override defaultAlign at the column level
headerTextAlign: "left",
render: (value, hello) => (
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
}}
>
images={hello?.images}
containerStyle={{
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: 5,
}}
imageWrapperStyle={{ borderRadius: 20 }}
imageContainerStyle={{
height: 25,
width: 25,
// borderRadius: 20,
}}
/>
),
headerStyle: {
// backgroundColor: "#f0f8ff",
flex: 3,
},
},
{
label: "Quantity",
key: "email",
align: "center", // Override defaultAlign at the column level
// headerTextAlign: "left",
headerStyle: {
// backgroundColor: "#f0f8ff",
flex: 2,
},
render: (value, row, rowIndex) => (
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
}}
>
),
},
{
label: "Age",
key: "age",
align: "center", // Override defaultAlign at the column level
headerStyle: {
// backgroundColor: "#f0f8ff",
flex: 2,
},
render: (value, row, rowIndex) => (
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
}}
>
$5,000
),
},
]}
summary={{
align: "right",
content: (
padding: 5,
gap: 6,
}}
>
fontWeight: "400",
fontStyle: "italic",
fontSize: 18,
color: colors.black_50,
}}
>
Summary
),
style: {
backgroundColor: colors.black_10,
width: "80%",
// height: 300,
borderRadius: 10,
},
}}
/>
);
};
export default TableUsing;
`
`tsxotp
import { OTPInput } from "react-native-modern-elements";
const [otp, setOtp] = useState
{/ OTPInput component updates the state correctly /}`
;
`tsx
import { StarRating } from "react-native-modern-elements";
size={20}
startgap={2}
activeStartColor={colors?.rose}
inActiveStartColor={colors?.green}
/>;
`
`tsx`
import { StarRating } from "react-native-modern-elements";
Radialsize={150}
strokeLinecap="round"
strokeWidths={12}
percentageTextSize={20}
percentageTextFontWeight={"600"}
animationDuration={1000}
color={{
high: "green-medium",
low: "red",
medium: "yellow",
veryHigh: "green",
}}
/>;
`tsx`
import { NumberCount } from "react-native-modern-elements";
formatPrice
locale="en-US"
prefix="$"
style={{
fontSize: verticalScale(28),
fontWeight: "800",
color: colors?.black,
}}
/>;
`tsx
import { SelectList } from "react-native-modern-elements";
const [selected, setSelected] = React.useState
const data = [
{
key: "1",
value: "desc",
modallable: "Recently",
icons:
},
{
key: "2",
value: "asc",
modallable: "Oldest",
icons:
},
];
const handleSelectItem = (item: string) => {
setSelected(item); // Set selected item
};
searchPlaceholder="Search ..."
// maxHeight={300}
// setSelected={(val) => setSelected(val)}
setSelected={handleSelectItem}
fontFamily="lato"
data={data as any}
DefaultTitle="Sort by"
maxHeight={160}
Reset
selectedIcons={false}
searchicon={false}
search={true}
dropdownTextStyles={{ fontStyle: "italic" }}
dropdownItemStyles={{
paddingHorizontal: 20,
paddingVertical: 10,
borderBottomColor: colors?.black_10,
borderBottomWidth: 0.7,
}}
InputboxStyles={{
borderRadius: 10,
height: 50,
backgroundColor: colors?.white,
paddingHorizontal: 10,
}} //override default styles
// defaultOption={{ key: "1", value: "Sort by" }} //default selected option
onSelect={() => {}}
save="value"
dropdownShown={false}
dropdownShadow={true}
/>
`
`tsx
import { Popup } from "react-native-modern-elements";
const [selected, setSelected] = React.useState
const handleSelectItem = (item: string) => {
console.log("handleSelectItem", item);
setSelected(item);
};
{ key: 1, value: "Apple", modallable: "π Apple" },
{ key: 2, value: "Banana", modallable: "π Banana" },
{ key: 3, value: "Cherry", modallable: "π Cherry" },
]}
save="key"
setSelected={(val) => handleSelectItem(key)}
setSelected={(val) => setSelected(val)}
save="value"
maxHeight={130}
// maxWidth={150}
// gap={8}
// popupShown={dropdownShown} // pass state down
// setPopupShown={setDropdownShown} // pass updater
popupShadow={true}
animation="updown"
popupBoxStyle={{
paddingVertical: 0,
paddingHorizontal: 0,
// backgroundColor: colors?.green,
}}
renderButton={(toggle, selected) => (
style={{
flexDirection: "row",
width: 120,
height: 45,
alignItems: "center",
justifyContent: "space-between",
padding: 12,
backgroundColor: "#fff",
borderWidth: 1,
borderColor: "#ddd",
borderRadius: 8,
}}
>
)}
/>;
`
`tsx`
import { RadioCircle } from "react-native-modern-elements";
const [isOn, setIsOn] = useState(false); // true = ON, false = OFF
const [selected, setSelected] = useState("apple");
selected={selected}
onSelect={setSelected}
circleSize={24}
circleColor="green"
selectedColor="green"
activeDotSize={16}
containerStyle={{ marginVertical: 8, borderWidth: 1 }}
/>;
value="Nizam"
selected={selected}
onSelect={setSelected}
circleSize={35}
circleBorderWidth={2}
circleColor="#ff0000"
selectedColor="#ff0000"
activeDotSize={24} // <-- control active dot width
containerStyle={{ marginVertical: 8 }}
/>
type="one"
selected={isOn ? "toggle" : ""} // toggle selected state
onSelect={() => setIsOn(!isOn)} // flip state
circleSize={30}
circleBorderWidth={2}
circleColor={isOn ? "green" : "gray"}
selectedColor="green"
activeDotSize={18}
containerStyle={{ marginVertical: 10 }}
/>;
`tsx/delivery-company
import { Switch } from "react-native-modern-elements";
onValueChange={(newValue) => {
// toggleConnection(item?.id, newValue);
// revalidateManager.run();`
console.log("newValue", newValue);
}}
// disabled={isActive === false ? false : !isActive ? true : false}
activeColor="green"
inactiveColor="gray"
switchContainers={{
width: verticalScale(55),
height: verticalScale(25),
}}
switchCircle={{
width: verticalScale(20),
height: verticalScale(20),
}}
switchTexts={{
fontSize: verticalScale(10),
fontWeight: "800",
}}
/>;
`tsx
import { RangeSlider } from "react-native-modern-elements";
const MIN_DEFAULT = 500;
const MAX_DEFAULT = 10500;
const minRef = React.useRef(MIN_DEFAULT);
const maxRef = React.useRef(MAX_DEFAULT);
defaultLeftPercent={0.15} // 15%
defaultRightPercent={0.7} // 70%
min={MIN_DEFAULT}
max={MAX_DEFAULT}
mode="range"
step={100}
thumbStyle="two"
thumbValue="Value"
TrackthumbLabelBackgroundColor="green"
textColor="blue"
fontSize={10}
TrackHeight={8}
priceSymbols="#"
trickBorderRadious={100}
onValueChange={(range) => {
minRef.current = range?.min;
maxRef.current = range?.max;
}}
/>;
`
π€³Your root layout using
`tsx
import SplashScreenComponent from "@/src/components/splashScreen/SplashScreenComponent";
import { useFonts } from "expo-font";
import { Stack } from "expo-router";
import * as SplashScreen from "expo-splash-screen";
import { useEffect, useState } from "react";
import { View } from "react-native";
import {
Toast,
toastRef,
useBackExit,
InternetStatusToast,
} from "react-native-modern-elements";
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
useBackExit();
const [isSplashVisible, setSplashVisible] = useState(true);
const [fontsLoaded] = useFonts({
SpaceMono: require("../../assets/fonts/SpaceMono-Regular.ttf"),
});
useEffect(() => {
if (fontsLoaded) {
setTimeout(() => {
setSplashVisible(false);
SplashScreen.hideAsync();
}, 2500);
}
}, [fontsLoaded]);
if (isSplashVisible) {
return
}
return (
{/
<>
defaultAnimation="center"
top={50}
// customIcons={{
// success:
// error:
// info:
// }}
contentStyle={{ gap: 5 }}
width={300}
iconSize={20}
duration={50000}
maxHeight={400}
textStyle={{ fontStyle: "italic" }}
containerStyle={{
borderRadius: 10,
paddingHorizontal: 0,
paddingVertical: 0,
}}
iconColor="yellow"
/>
>
{/ Show banner globally at bottom /}
);
}
`
`tsx
import { toastRef } from "react-native-modern-elements";
const handleSubmit = async () => {
if (isOffline) {
ToastAndroid.show(
" Please connect your internet to continue",
ToastAndroid.SHORT
);
return;
}
const data = {
email: values.email,
password: values.password,
};
try {
setLoading(true);
const res = await loginServerAction(data as any);
if (res?.data?.accessToken) {
// Alert.alert("Login Successful", "You are now logged in.");
router.replace("/(screens)/(tabs)");
setLoading(false);
// ToastAndroid.show("Login successfully!", ToastAndroid.SHORT);
toastRef.current?.show(
"Login successfull",
"success",
"rightToCenterCloseRight"
);
} else {
handleApiError(res);
setLoading(false);
}
} catch (error) {
console.error("Error during form submission:", error);
}
};
`
`tsx
import { CheckBox } from "react-native-modern-elements";
const [checkedItems, setCheckedItems] = useState<{ [key: string]: boolean }>(
{}
);
onChange={(val: boolean) => {
setIsChecked(val);
}}
checkBoxStyle={{
width: 40,
height: 40,
...classComponent.borderStyle,
}}
iconSize={35}
// text="Accept Terms"
/>;
`
`tsx`
import { Button } from "react-native-modern-elements";
disabled={!isFormValid}
onPress={handleSubmit}
style={{
backgroundColor: isFormValid ? colors.primary : colors.black,
opacity: isFormValid ? 1 : 0.6,
borderRadius: verticalScale(50),
height: verticalScale(46),
}}
>
{loading ? (
Continue
) : (
Continue
)}
;
`tsx`
import { Input } from "react-native-modern-elements";
placeholder="Name"
value={values.full_name}
onChangeText={(value) => {
handleChange("full_name", value);
setRef("full_name", value);
validateField(value);
}}
error={errors.full_name || ""}
icon={
containerStyle={{
borderRadius: 50,
backgroundColor: colors.bgColors,
overflow: "hidden",
}}
iconStyle={{ marginLeft: verticalScale(4) }}
/>;
`tsx
import { TabSlider } from "react-native-modern-elements";
renderTabItem={({ tab, isActive, onPress }) => (
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 10,
backgroundColor: isActive ? "orange" : "#eee",
}}
>
{/ Label /}
color: isActive ? "white" : "black",
fontWeight: "600",
}}
>
{tab.label}
{/ Tooltip (if exists) /}
{tab.tooltip !== undefined && (
marginLeft: 8,
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 14,
backgroundColor: isActive
? "white" // tooltipActiveColor alternative
: "orange", // tooltipInactiveColor alternative (match your design)
}}
>
color: isActive ? "black" : "white",
fontSize: verticalScale(11),
fontWeight: "600",
}}
>
{tab.tooltip}
)}
)}
variant="pill"
alignTabs="center"
// buttonStyle={{ backgroundColor: ac "black", paddingVertical: 14 }}
buttonTextStyle={{ color: "white" }}
buttonContainerStyle={{
marginHorizontal: 10,
}}
activeColor="green"
inactiveColor="white"
tooltipActiveColor="white"
tooltipInactiveColor="green"
// header={
footer={
tabs={[
{ label: "Newest", tooltip: 20 },
{ label: "Top Sell" },
{ label: "Popular" },
]}
>
const styles = StyleSheet.create({
page: {
// flex: 1,
justifyContent: "center",
alignItems: "center",
// padding: 20,
},
pageText: {
fontSize: 24,
fontWeight: "bold",
},
});
`
`tsx``
import { ExpandableText } from "react-native-modern-elements";
fontSize: verticalScale(13),
color: colors?.black_50,
lineHeight: 23,
letterSpacing: 0.9,
textDecorationLine: "underline",
}}
>
Breathe elegance into your summer wardrobe with Zaraβs Oversized Linen Blend
Shirt in a gentle blush pink tone.
;