Glowup
Template
Checkout
A one-page checkout: shipping address, delivery options and payment method beside an order summary with quantity, promo code, totals and a pay button that carries its own progress.
Live preview
Checkout
Shipping address
First name
Last name
Address
󰟙
City
Postcode
Delivery
Payment
Card number
󰆛
Expiry
CVC
Order summary
Wireless headphones
€89.00 · Midnight blue
Quantity
USB-C cable
€24.50 · 2 m
Promo code
Try GLOWUP10
Subtotal
€113.50
Shipping
Free
Total
€113.50
Built with
16 Glowup components and the theme's tokens — no hex values, no hand-rolled controls. Each chip opens that component's reference.
HStack
VStack
The code
The whole of Checkout.tsx. It imports only React, React Native and @its/glowup-ui, so it can be pasted into an app as is.
Checkout.tsx
import { useEffect, useState } from "react"; import { Avatar, Box, Breadcrumbs, Button, ButtonGroup, Checkbox, Divider, Grid, HStack, Input, ListItem, ProgressButton, RadioGroup, Spinner, Typography, VStack, useTheme, type ButtonGroupOption, } from "@its/glowup-ui"; const DELIVERY = [ { id: "standard", label: "Standard · 3–5 days · Free", value: "standard" }, { id: "express", label: "Express · next day · €9.90", value: "express" }, ]; const PAYMENT: ButtonGroupOption[] = [ { value: "card", label: "Card", icon: "credit-card-outline" }, { value: "paypal", label: "PayPal", icon: "wallet-outline" }, ]; const euro = (value: number) => `€${value.toFixed(2)}`; /** * One-page checkout: the form on the left, the order summary on the right * (below it when narrow). The pay button carries its own progress, so the * reader never loses track of the payment while it runs. */ export default function Checkout() { const { theme } = useTheme(); const [quantity, setQuantity] = useState(1); const [delivery, setDelivery] = useState("standard"); const [payment, setPayment] = useState("card"); const [promo, setPromo] = useState(""); const [discount, setDiscount] = useState(0); const [sameAddress, setSameAddress] = useState(true); const [status, setStatus] = useState< "idle" | "loading" | "success" | "error" >("idle"); const [progress, setProgress] = useState(0); const subtotal = 89 * quantity + 24.5; const shipping = delivery === "express" ? 9.9 : 0; const total = subtotal + shipping - discount; // Stand-in for a payment request: fills the button in five steps. useEffect(() => { if (status !== "loading") return; const timer = setTimeout( () => progress >= 1 ? setStatus("success") : setProgress(progress + 0.25), 300, ); return () => clearTimeout(timer); }, [status, progress]); const sectionTitle = (title: string) => ( <Typography variant="titleMedium" style={{ color: theme.colors.onSurface }}> {title} </Typography> ); return ( <Box p="l" gap="l" radius="extraLarge" bg="surface" borderWidth={1} borderColor="outlineVariant" > <VStack spacing="s"> <Breadcrumbs items={[ { id: "cart", label: "Cart", onPress: () => {} }, { id: "checkout", label: "Checkout" }, ]} /> <Typography variant="headlineSmall" style={{ color: theme.colors.onSurface }} > Checkout </Typography> </VStack> <HStack wrap spacing="xl" align="flex-start"> {/* Form */} <VStack spacing="l" style={{ flexGrow: 3, flexBasis: 340 }}> <VStack spacing="m"> {sectionTitle("Shipping address")} <Grid columns={2} spacing="m"> <Input label="First name" value="Ada" onChangeText={() => {}} /> <Input label="Last name" value="Lovelace" onChangeText={() => {}} /> </Grid> <Input label="Address" value="12 St James's Square" onChangeText={() => {}} leadingIcon="map-marker-outline" /> <Grid columns={2} spacing="m"> <Input label="City" value="London" onChangeText={() => {}} /> <Input label="Postcode" value="SW1Y 4LB" onChangeText={() => {}} /> </Grid> <Checkbox checked={sameAddress} onValueChange={setSameAddress} label="Billing address is the same" /> </VStack> <Divider /> <VStack spacing="m"> {sectionTitle("Delivery")} <RadioGroup options={DELIVERY} value={delivery} onValueChange={setDelivery} /> </VStack> <Divider /> <VStack spacing="m"> {sectionTitle("Payment")} <ButtonGroup type="connected" accessibilityLabel="Payment method" options={PAYMENT} value={payment} onValueChange={setPayment} /> {payment === "card" ? ( <VStack spacing="m"> <Input label="Card number" value="" onChangeText={() => {}} placeholder="1234 1234 1234 1234" leadingIcon="credit-card-outline" /> <Grid columns={2} spacing="m"> <Input label="Expiry" value="" onChangeText={() => {}} placeholder="MM / YY" /> <Input label="CVC" value="" onChangeText={() => {}} placeholder="123" /> </Grid> </VStack> ) : ( <Typography variant="bodyMedium" style={{ color: theme.colors.onSurfaceVariant }} > You will be redirected to PayPal to complete the payment. </Typography> )} </VStack> </VStack> {/* Summary */} <Box p="l" gap="m" radius="large" bg="surfaceContainerLow" style={{ flexGrow: 2, flexBasis: 280 }} > {sectionTitle("Order summary")} <ListItem leading={<Avatar icon="headphones" variant="rounded" size={48} />} secondary={`${euro(89)} · Midnight blue`} > Wireless headphones </ListItem> <HStack justify="space-between" align="center"> <Typography variant="labelLarge" style={{ color: theme.colors.onSurfaceVariant }} > Quantity </Typography> <Spinner value={quantity} onChange={setQuantity} min={1} max={9} /> </HStack> <ListItem leading={<Avatar icon="cable-data" variant="rounded" size={48} />} secondary={`${euro(24.5)} · 2 m`} > USB-C cable </ListItem> <HStack spacing="s" align="flex-start"> <Input label="Promo code" value={promo} onChangeText={setPromo} helperText={discount ? "GLOWUP10 applied" : "Try GLOWUP10"} style={{ flex: 1 }} /> <Button mode="tonal" style={{ marginTop: theme.spacing.s }} onPress={() => setDiscount(promo.trim().toUpperCase() === "GLOWUP10" ? 10 : 0) } > Apply </Button> </HStack> <Divider /> {[ ["Subtotal", euro(subtotal)], ["Shipping", shipping ? euro(shipping) : "Free"], ...(discount ? [["Discount", `−${euro(discount)}`]] : []), ].map(([label, value]) => ( <HStack key={label} justify="space-between"> <Typography variant="bodyMedium" style={{ color: theme.colors.onSurfaceVariant }} > {label} </Typography> <Typography variant="bodyMedium" style={{ color: theme.colors.onSurface }} > {value} </Typography> </HStack> ))} <HStack justify="space-between" align="center"> <Typography variant="titleMedium" style={{ color: theme.colors.onSurface }} > Total </Typography> <Typography variant="headlineSmall" style={{ color: theme.colors.onSurface }} > {euro(total)} </Typography> </HStack> <ProgressButton fullWidth iconName="lock-outline" status={status} progress={progress} loadingLabel="Processing payment…" successLabel="Payment complete" onPress={() => { setProgress(0); setStatus("loading"); }} successDuration={0} > {`Pay ${euro(total)}`} </ProgressButton> </Box> </HStack> </Box> ); }