Skip to main content

Action Sheet

Basic

This is a basic example of using the ActionSheet component.

Share
const ShareContentComponent = () => {
const [isOpen, setIsOpen] = React.useState(false);
const colorMode = "light";
const { color } = getTokens();

const iconColor = useColorModeValue(
color["$primary.500"].val,
color["$primary.400"].val,
colorMode
);
const dividerColor = useColorModeValue(
"$primaryGray.200",
"$primaryGray.700",
colorMode
);

return (
<Box alignItems="flex-start">
<Button onPress={() => setIsOpen(true)}>
<Button.LeftIcon>
<Share2 size={18} color="white" />
</Button.LeftIcon>
Share
</Button>
<ActionSheet
isOpen={isOpen}
onClose={() => setIsOpen(false)}
snapPoints={[55]}
colorMode={colorMode}
>
<ActionSheet.Content padding="$4">
<VStack gap="$4" width="$full">
<TitleThree marginBottom="$0" textAlign="center">
Share this article
</TitleThree>
<Subhead
marginBottom="$0"
textAlign="center"
color={useColorModeValue(
"$primaryGray.600",
"$primaryGray.400",
colorMode
)}
>
Choose how you'd like to share
</Subhead>

<HStack justifyContent="center" gap="$6" paddingVertical="$4">
<VStack alignItems="center" gap="$2">
<Box
backgroundColor={useColorModeValue(
"$primaryGray.100",
"$primaryGray.800",
colorMode
)}
padding="$3"
borderRadius="$12"
>
<Twitter size={24} color={iconColor} />
</Box>
<Subhead marginBottom="$0">Twitter</Subhead>
</VStack>
{/* ...Facebook, Email, and Copy Link options... */}
</HStack>

<Box
borderTopWidth={1}
borderTopColor={dividerColor}
paddingTop="$4"
>
<HStack gap="$4" justifyContent="center">
<Button
colorMode={colorMode}
variant="secondary"
onPress={() => setIsOpen(false)}
>
<Button.LeftIcon>
<Bookmark size={18} />
</Button.LeftIcon>
Save for later
</Button>
<Button
colorMode={colorMode}
variant="tertiary"
onPress={() => setIsOpen(false)}
>
<Button.LeftIcon>
<Flag size={18} />
</Button.LeftIcon>
Report
</Button>
</HStack>
</Box>
</VStack>
</ActionSheet.Content>
</ActionSheet>
</Box>
);
};

Disable Overlay

You can use the disableOverlay prop to disable the overlay that appears behind the ActionSheet component.

Add Media
const MediaPickerComponent = () => {
const [isOpen, setIsOpen] = React.useState(false);
const colorMode = "light";
const { color } = getTokens();

const cameraColor = useColorModeValue(
color["$emerald.500"].val,
color["$emerald.400"].val,
colorMode
);
const galleryColor = useColorModeValue(
color["$violet.500"].val,
color["$violet.400"].val,
colorMode
);
const documentColor = useColorModeValue(
color["$amber.500"].val,
color["$amber.400"].val,
colorMode
);
const audioColor = useColorModeValue(
color["$rose.500"].val,
color["$rose.400"].val,
colorMode
);

const ActionButton = ({
icon,
label,
bgColor,
}: {
icon: React.ReactNode;
label: string;
bgColor: string;
}) => (
<VStack alignItems="center" gap="$2" flex={1}>
<Box
backgroundColor={bgColor}
padding="$4"
borderRadius="$4"
width="$full"
alignItems="center"
>
{icon}
</Box>
<Subhead marginBottom="$0">{label}</Subhead>
</VStack>
);

return (
<Box alignItems="flex-start">
<Button onPress={() => setIsOpen(true)}>
<Button.LeftIcon>
<Camera size={18} color="white" />
</Button.LeftIcon>
Add Media
</Button>
<ActionSheet
disableOverlay
isOpen={isOpen}
onClose={() => setIsOpen(false)}
snapPoints={[40]}
colorMode={colorMode}
>
<ActionSheet.Content flex={1} padding="$4">
<VStack gap="$4" width="$full">
<TitleThree marginBottom="$0" textAlign="center">
Add to your post
</TitleThree>
<HStack gap="$3" paddingTop="$2">
<ActionButton
icon={<Camera size={28} color={cameraColor} />}
label="Camera"
bgColor={
useColorModeValue(
"$emerald.100",
"$emerald.900",
colorMode
) as string
}
/>
{/* ...Gallery, Document, and Audio buttons... */}
</HStack>
</VStack>
</ActionSheet.Content>
</ActionSheet>
</Box>
);
};

Hide Drag Indicator

You can use the hideDragIndicator prop to hide the drag indicator that appears at the top of the ActionSheet component.

Buy Now
const QuickCheckoutComponent = () => {
const [isOpen, setIsOpen] = React.useState(false);
const colorMode = "light";
const { color } = getTokens();

const priceColor = useColorModeValue(
"$primary.600",
"$primary.200",
colorMode
);
const labelColor = useColorModeValue(
"$primaryGray.600",
"$primaryGray.200",
colorMode
);
const cardIconColor = useColorModeValue(
color["$primary.500"].val,
color["$primary.200"].val,
colorMode
);

return (
<Box alignItems="flex-start">
<Button onPress={() => setIsOpen(true)}>
<Button.LeftIcon>
<ShoppingCart size={18} color="white" />
</Button.LeftIcon>
Buy Now
</Button>
<ActionSheet
hideDragIndicator
isOpen={isOpen}
onClose={() => setIsOpen(false)}
snapPoints={[60]}
colorMode={colorMode}
>
<ActionSheet.Content flex={1} padding="$4">
<VStack gap="$4" width="$full">
<HStack justifyContent="space-between" alignItems="center">
<TitleThree marginBottom="$0">Quick Checkout</TitleThree>
<Button
size="sm"
variant="tertiary"
colorMode={colorMode}
onPress={() => setIsOpen(false)}
>
<Button.LeftIcon>
<X size={20} />
</Button.LeftIcon>
</Button>
</HStack>

<HStack
gap="$3"
padding="$3"
backgroundColor={useColorModeValue(
"$primaryGray.100",
"$primaryDark.12",
colorMode
)}
borderRadius="$2"
>
<Box
width={60}
height={60}
backgroundColor={useColorModeValue(
"$primaryGray.200",
"$primaryGray.700",
colorMode
)}
borderRadius="$2"
/>
<VStack flex={1} justifyContent="center">
<Body marginBottom="$0" fontWeight="$semibold">
Premium Wireless Headphones
</Body>
<Subhead marginBottom="$0" color={labelColor}>
Black • Qty: 1
</Subhead>
</VStack>
<Body marginBottom="$0" fontWeight="$bold" color={priceColor}>
$299.00
</Body>
</HStack>

<VStack gap="$3">
<HStack gap="$2" alignItems="center">
<CreditCard size={18} color={cardIconColor} />
<Body marginBottom="$0">•••• 4242</Body>
<Body marginBottom="$0" color={labelColor}>
Visa
</Body>
</HStack>
<HStack gap="$2" alignItems="center">
<MapPin size={18} color={cardIconColor} />
<Body marginBottom="$0" flex={1} numberOfLines={1}>
123 Main St, San Francisco, CA
</Body>
</HStack>
</VStack>

<VStack gap="$2" paddingTop="$2">
<HStack justifyContent="space-between">
<Body marginBottom="$0" color={labelColor}>
Subtotal
</Body>
<Body marginBottom="$0">$299.00</Body>
</HStack>
<HStack justifyContent="space-between">
<Body marginBottom="$0" color={labelColor}>
Shipping
</Body>
<Body
marginBottom="$0"
color={useColorModeValue(
"$emerald.600",
"$emerald.400",
colorMode
)}
>
FREE
</Body>
</HStack>
<HStack justifyContent="space-between" paddingTop="$2">
<Body marginBottom="$0" fontWeight="$bold">
Total
</Body>
<TitleThree marginBottom="$0" color={priceColor}>
$299.00
</TitleThree>
</HStack>
</VStack>

<Button
colorMode={colorMode}
width="$full"
onPress={() => setIsOpen(false)}
>
Complete Purchase
</Button>
</VStack>
</ActionSheet.Content>
</ActionSheet>
</Box>
);
};

Customize Backdrop

You can use the _backdrop prop to customize the backdrop that appears behind the ActionSheet component.

Track Order
const OrderTrackingComponent = () => {
const [isOpen, setIsOpen] = React.useState(false);
const colorMode = "light";
const { color } = getTokens();

const completedColor = useColorModeValue(
color["$emerald.500"].val,
color["$emerald.700"].val,
colorMode
);
const activeColor = useColorModeValue(
color["$primary.500"].val,
color["$primary.700"].val,
colorMode
);
const pendingColor = useColorModeValue(
color["$primaryGray.400"].val,
color["$primaryGray.700"].val,
colorMode
);
const lineColor = useColorModeValue(
"$primaryGray.300",
"$primaryGray.600",
colorMode
);
const labelColor = useColorModeValue(
"$primaryGray.600",
"$primaryGray.400",
colorMode
);

const TrackingStep = ({
icon,
title,
subtitle,
status,
isLast = false,
}: {
icon: React.ReactNode;
title: string;
subtitle: string;
status: "completed" | "active" | "pending";
isLast?: boolean;
}) => {
return (
<HStack gap="$3">
<VStack alignItems="center">
<Box
backgroundColor={
status === "completed"
? useColorModeValue("$emerald.100", "$emerald.300", colorMode)
: status === "active"
? useColorModeValue("$primary.100", "$primary.300", colorMode)
: useColorModeValue(
"$primaryGray.200",
"$primaryGray.400",
colorMode
)
}
padding="$2"
borderRadius="$12"
>
{icon}
</Box>
{!isLast && <Box width={2} height={40} backgroundColor={lineColor} />}
</VStack>
<VStack flex={1} paddingBottom={isLast ? "$0" : "$4"}>
<Body marginBottom="$0" fontWeight="$semibold">
{title}
</Body>
<Subhead marginBottom="$0" color={labelColor}>
{subtitle}
</Subhead>
</VStack>
</HStack>
);
};

return (
<Box alignItems="flex-start">
<Button onPress={() => setIsOpen(true)}>
<Button.LeftIcon>
<Package size={18} color="white" />
</Button.LeftIcon>
Track Order
</Button>
<ActionSheet
_backdrop={{
backgroundColor: "$primary.900",
}}
isOpen={isOpen}
onClose={() => setIsOpen(false)}
snapPoints={[65]}
colorMode={colorMode}
>
<ActionSheet.Content flex={1} padding="$4">
<VStack gap="$4" width="$full">
<HStack justifyContent="space-between" alignItems="center">
<VStack>
<TitleThree marginBottom="$0">Order #12345</TitleThree>
<Subhead marginBottom="$0" color={labelColor}>
Estimated delivery: Dec 15
</Subhead>
</VStack>
<Badge accentColor="emerald">In Transit</Badge>
</HStack>

<VStack paddingTop="$2">
<TrackingStep
icon={<CheckCircle size={20} color={completedColor} />}
title="Order Confirmed"
subtitle="Dec 10, 2024 at 10:30 AM"
status="completed"
/>
{/* ...Shipped and Out for Delivery steps... */}
<TrackingStep
icon={<MapPin size={20} color={pendingColor} />}
title="Delivered"
subtitle="Pending"
status="pending"
isLast
/>
</VStack>

<Button
colorMode={colorMode}
variant="secondary"
width="$full"
onPress={() => setIsOpen(false)}
>
View Full Details
</Button>
</VStack>
</ActionSheet.Content>
</ActionSheet>
</Box>
);
};

Long Content

You can use the disableDrag prop to disable the drag gesture that closes the ActionSheet component. You can also use the dismissOnSnapToBottom prop to disable the snap to bottom gesture that closes the ActionSheet component. With this combination, you can scroll the content of the ActionSheet component without closing it.

See Reviews
const ProductReviewsComponent = () => {
const [isOpen, setIsOpen] = React.useState(false);
const colorMode = "light";
const { color } = getTokens();

const starColor = useColorModeValue(
color["$amber.500"].val,
color["$amber.400"].val,
colorMode
);
const dateColor = useColorModeValue(
"$primaryGray.500",
"$primaryGray.400",
colorMode
);
const dividerColor = useColorModeValue(
"$primaryGray.200",
"$primaryGray.700",
colorMode
);

const reviews = [
{
id: 1,
name: "Sarah M.",
avatar: "https://i.pravatar.cc/150?img=1",
rating: 5,
date: "2 days ago",
comment:
"Absolutely love these headphones! The sound quality is incredible and the battery life is amazing.",
helpful: 24,
},
// ...3 more reviews...
{
id: 5,
name: "Lisa K.",
avatar: "https://i.pravatar.cc/150?img=5",
rating: 5,
date: "1 month ago",
comment:
"Worth every penny! These are my third pair of headphones and by far the best I've ever owned.",
helpful: 41,
},
];

return (
<Box alignItems="flex-start">
<Button onPress={() => setIsOpen(true)}>
<Button.LeftIcon>
<Star size={18} color="white" />
</Button.LeftIcon>
See Reviews
</Button>
<ActionSheet
disableDrag
dismissOnSnapToBottom={false}
isOpen={isOpen}
onClose={() => setIsOpen(false)}
snapPoints={[80]}
colorMode={colorMode}
>
<ActionSheet.Content flex={1} padding="$4">
<VStack gap="$4" width="$full" flex={1}>
<HStack justifyContent="space-between" alignItems="center">
<VStack>
<TitleThree marginBottom="$0">Customer Reviews</TitleThree>
<HStack gap="$2" alignItems="center">
<HStack gap="$0.5">
{/* ...Star rating display... */}
</HStack>
<Subhead marginBottom="$0" color={dateColor}>
4.8 (128 reviews)
</Subhead>
</HStack>
</VStack>
<Button
size="sm"
variant="tertiary"
colorMode={colorMode}
onPress={() => setIsOpen(false)}
>
<Button.LeftIcon>
<X size={20} />
</Button.LeftIcon>
</Button>
</HStack>

<ScrollView
style={{ flex: 1 }}
showsVerticalScrollIndicator={false}
>
<VStack gap="$4">
{reviews.map((review, index) => (
<VStack
key={review.id}
gap="$2"
paddingBottom="$4"
borderBottomWidth={index < reviews.length - 1 ? 1 : 0}
borderBottomColor={dividerColor}
>
<HStack gap="$3" alignItems="center">
<Avatar size="sm" source={{ uri: review.avatar }} />
<VStack flex={1}>
<Body marginBottom="$0" fontWeight="$semibold">
{review.name}
</Body>
<Subhead marginBottom="$0" color={dateColor}>
{review.date}
</Subhead>
</VStack>
<HStack gap="$0.5">
{/* ...Star rating... */}
</HStack>
</HStack>
<Body marginBottom="$0">{review.comment}</Body>
<HStack gap="$2" alignItems="center">
<Button
size="sm"
variant="tertiary"
colorMode={colorMode}
>
<Button.LeftIcon>
<Heart size={14} />
</Button.LeftIcon>
</Button>
</HStack>
</VStack>
))}
</VStack>
</ScrollView>

<Button
colorMode={colorMode}
width="$full"
onPress={() => setIsOpen(false)}
>
<Button.LeftIcon>
<MessageCircle size={18} color="white" />
</Button.LeftIcon>
Write a Review
</Button>
</VStack>
</ActionSheet.Content>
</ActionSheet>
</Box>
);
};

Nested Action Sheets

You can nest multiple action sheets and modals together by using the zIndex prop to control the stacking order.

Add to Cart
const AddToCartFlowComponent = () => {
const [isOpen, setIsOpen] = React.useState(false);
const [showSizeSheet, setShowSizeSheet] = React.useState(false);
const [showConfirmModal, setShowConfirmModal] = React.useState(false);
const [selectedSize, setSelectedSize] = React.useState<string | null>(null);
const colorMode = "light";
const { color } = getTokens();

const priceColor = useColorModeValue(
"$primary.600",
"$primary.300",
colorMode
);
const labelColor = useColorModeValue(
"$primaryGray.600",
"$primaryGray.400",
colorMode
);
const selectedBorderColor = useColorModeValue(
"$primary.500",
"$primary.400",
colorMode
);
const checkColor = useColorModeValue(
color["$emerald.500"].val,
color["$emerald.400"].val,
colorMode
);

const sizes = ["XS", "S", "M", /* ...other sizes... */ "XXL"];

return (
<Box alignItems="flex-start" gap="$2">
<Button onPress={() => setIsOpen(true)}>
<Button.LeftIcon>
<ShoppingCart color="white" />
</Button.LeftIcon>
Add to Cart
</Button>

{/* First Action Sheet - Product Options */}
<ActionSheet
zIndex={1}
isOpen={isOpen}
onClose={() => setIsOpen(false)}
snapPoints={[50]}
colorMode={colorMode}
>
<ActionSheet.Content padding="$4">
<VStack gap="$4" width="$full">
<HStack gap="$4">
<Box
width={80}
height={80}
backgroundColor={useColorModeValue(
"$primaryGray.200",
"$primaryGray.700",
colorMode
)}
borderRadius="$2"
/>
<VStack flex={1} justifyContent="center">
<Body marginBottom="$0" fontWeight="$semibold">
Classic Cotton T-Shirt
</Body>
<Subhead marginBottom="$0" color={labelColor}>
Navy Blue
</Subhead>
<Body marginBottom="$0" fontWeight="$bold" color={priceColor}>
$49.99
</Body>
</VStack>
</HStack>

<VStack gap="$2">
<Subhead marginBottom="$0" fontWeight="$semibold">
Select Size
</Subhead>
<Button
colorMode={colorMode}
variant="secondary"
width="$full"
onPress={() => setShowSizeSheet(true)}
>
{selectedSize || "Choose size..."}
</Button>
</VStack>

<Button
colorMode={colorMode}
width="$full"
isDisabled={!selectedSize}
onPress={() => {
setIsOpen(false);
setTimeout(() => setShowConfirmModal(true), 300);
}}
>
Add to Cart - $49.99
</Button>
</VStack>
</ActionSheet.Content>
</ActionSheet>

{/* Second Action Sheet - Size Selection */}
<ActionSheet
zIndex={2}
isOpen={showSizeSheet}
onClose={() => setShowSizeSheet(false)}
snapPoints={[45]}
colorMode={colorMode}
>
<ActionSheet.Content padding="$4">
<VStack gap="$4" width="$full">
<TitleThree marginBottom="$0" textAlign="center">
Select Your Size
</TitleThree>
<HStack flexWrap="wrap" gap="$2" justifyContent="center">
{/* ...Size selection buttons (XS, S, M, L, XL, XXL)... */}
</HStack>
<Subhead marginBottom="$0" textAlign="center" color={labelColor}>
View size guide
</Subhead>
</VStack>
</ActionSheet.Content>
</ActionSheet>

{/* Confirmation Modal */}
<Unspaced>
<Modal
colorMode={colorMode}
zIndex={999999}
isOpen={showConfirmModal}
onClose={() => setShowConfirmModal(false)}
>
<Modal.Body>
<VStack gap="$4" padding="$4" alignItems="center">
<Box
backgroundColor={useColorModeValue(
"$emerald.100",
"$emerald.900",
colorMode
)}
padding="$4"
borderRadius="$12"
>
<CheckCircle size={48} color={checkColor} />
</Box>
<TitleThree marginBottom="$0" textAlign="center">
Added to Cart!
</TitleThree>
<Body marginBottom="$0" textAlign="center" color={labelColor}>
Classic Cotton T-Shirt ({selectedSize}) has been added to your
cart.
</Body>
</VStack>
</Modal.Body>
<Modal.Footer>
<VStack padding="$4" gap="$2" width="$full">
<Button
colorMode={colorMode}
width="$full"
onPress={() => setShowConfirmModal(false)}
>
<Button.LeftIcon>
<ShoppingCart size={18} color="white" />
</Button.LeftIcon>
View Cart (1 item)
</Button>
<Button
colorMode={colorMode}
variant="tertiary"
width="$full"
onPress={() => setShowConfirmModal(false)}
>
Continue Shopping
</Button>
</VStack>
</Modal.Footer>
</Modal>
</Unspaced>
</Box>
);
};