Glowup
Template
Pricing
A pricing page: a monthly/yearly switch, three plans with the recommended one raised by the glow variant, and a FAQ of accordions underneath.
Live preview
Simple pricing that grows with you
Start free. Upgrade when the team does.
Starter
For side projects and trying things out.
€0
/ user / month
󰗠
1 project
󰗠
Community support
󰗠
1 GB storage
Pro
󰓎
Popular
󰄬
For teams shipping to production.
€20
/ user / month
󰗠
Unlimited projects
󰗠
Priority support
󰗠
100 GB storage
󰗠
Custom domains
Enterprise
For organisations with compliance needs.
€66
/ user / month
󰗠
Everything in Pro
󰗠
SSO and audit log
󰗠
99.99% SLA
Frequently asked
Built with
11 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 Pricing.tsx. It imports only React, React Native and @its/glowup-ui, so it can be pasted into an app as is.
Pricing.tsx
import { useState } from "react"; import { Accordion, Box, Button, ButtonGroup, Card, Chip, Divider, HStack, Icon, Typography, VStack, useTheme, } from "@its/glowup-ui"; const PLANS = [ { id: "starter", name: "Starter", blurb: "For side projects and trying things out.", monthly: 0, features: ["1 project", "Community support", "1 GB storage"], }, { id: "pro", name: "Pro", blurb: "For teams shipping to production.", monthly: 24, featured: true, features: [ "Unlimited projects", "Priority support", "100 GB storage", "Custom domains", ], }, { id: "enterprise", name: "Enterprise", blurb: "For organisations with compliance needs.", monthly: 79, features: ["Everything in Pro", "SSO and audit log", "99.99% SLA"], }, ]; const FAQ = [ { q: "Can I change plan later?", a: "Yes. Upgrades apply immediately; downgrades at the end of the billing period.", }, { q: "Is there a free trial of Pro?", a: "Every new workspace gets 14 days of Pro, no card required.", }, { q: "How does yearly billing work?", a: "You pay for ten months and get twelve. Cancel any time for a pro-rata refund.", }, ]; /** * Plans side by side (stacked when narrow), the recommended one raised with * the glow variant. Each plan is a card because it is one choice the reader * acts on as a whole; the FAQ underneath is a plain list of accordions. */ export default function Pricing() { const { theme } = useTheme(); const [billing, setBilling] = useState("yearly"); const [chosen, setChosen] = useState("pro"); const priceOf = (monthly: number) => billing === "yearly" ? Math.round((monthly * 10) / 12) : monthly; return ( <Box p="l" gap="xl" radius="extraLarge" bg="surface" borderWidth={1} borderColor="outlineVariant" > <VStack spacing="m" align="center"> <Typography variant="headlineMedium" style={{ color: theme.colors.onSurface, textAlign: "center" }} > Simple pricing that grows with you </Typography> <Typography variant="bodyLarge" style={{ color: theme.colors.onSurfaceVariant, textAlign: "center" }} > Start free. Upgrade when the team does. </Typography> <ButtonGroup type="connected" accessibilityLabel="Billing period" options={[ { value: "monthly", label: "Monthly" }, { value: "yearly", label: "Yearly · 2 months free" }, ]} value={billing} onValueChange={setBilling} /> </VStack> <HStack wrap spacing="m" align="stretch"> {PLANS.map((plan) => ( <Card key={plan.id} variant={plan.featured ? "glow" : "outlined"} style={{ flexGrow: 1, flexBasis: 240 }} > <VStack spacing="m" p="m"> <HStack justify="space-between" align="center"> <Typography variant="titleLarge" style={{ color: theme.colors.onSurface }} > {plan.name} </Typography> {plan.featured && ( <Chip label="Popular" size="small" icon="star" selected /> )} </HStack> <Typography variant="bodyMedium" style={{ color: theme.colors.onSurfaceVariant }} > {plan.blurb} </Typography> <HStack align="flex-end" spacing="xs"> <Typography variant="displaySmall" style={{ color: theme.colors.onSurface }} > €{priceOf(plan.monthly)} </Typography> <Typography variant="bodyMedium" style={{ color: theme.colors.onSurfaceVariant }} > / user / month </Typography> </HStack> <Divider /> <VStack spacing="s"> {plan.features.map((feature) => ( <HStack key={feature} spacing="s" align="center"> <Icon source="check-circle" size={20} color={theme.colors.primary} /> <Typography variant="bodyMedium" style={{ color: theme.colors.onSurface }} > {feature} </Typography> </HStack> ))} </VStack> <Button mode={chosen === plan.id ? "filled" : "outlined"} iconName={chosen === plan.id ? "check" : undefined} fullWidth onPress={() => setChosen(plan.id)} > {chosen === plan.id ? "Selected" : `Choose ${plan.name}`} </Button> </VStack> </Card> ))} </HStack> <VStack spacing="s"> <Typography variant="titleLarge" style={{ color: theme.colors.onSurface }} > Frequently asked </Typography> {FAQ.map((item) => ( <Accordion key={item.q} title={item.q}> <Typography variant="bodyMedium" style={{ color: theme.colors.onSurfaceVariant }} > {item.a} </Typography> </Accordion> ))} </VStack> </Box> ); }