Glowup
Template
Sign up
A three-step registration built on Stepper: account details with a live password-strength meter, a one-time code with PinInput, then a short profile with a role and interests.
Live preview
Create your account
Step 1 of 3 · takes about a minute
Account
Verify
Profile
Work email *
󰇰
Password *
󰍁
Password strength: —
Built with
13 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 SignUp.tsx. It imports only React, React Native and @its/glowup-ui, so it can be pasted into an app as is.
SignUp.tsx
import { useState } from "react"; import { Box, Button, Checkbox, ChipGroup, EmptyState, HStack, Input, LinearProgress, PinInput, Select, Stepper, Typography, VStack, useTheme, } from "@its/glowup-ui"; const STEPS = [ { label: "Account", icon: "account-outline" }, { label: "Verify", icon: "email-check-outline" }, { label: "Profile", icon: "card-account-details-outline" }, ]; const ROLES = [ { id: "dev", label: "Developer", value: "dev" }, { id: "design", label: "Designer", value: "design" }, { id: "pm", label: "Product manager", value: "pm" }, ]; const INTERESTS = [ { value: "mobile", label: "Mobile", icon: "cellphone" }, { value: "web", label: "Web", icon: "web" }, { value: "design", label: "Design systems", icon: "palette-outline" }, { value: "a11y", label: "Accessibility", icon: "human" }, ] as const; /** 0–1 score: length plus one point per character class. */ const strengthOf = (password: string) => { const classes = [/[a-z]/, /[A-Z]/, /\d/, /[^\w]/].filter((re) => re.test(password), ).length; return Math.min(1, (Math.min(password.length, 12) / 12 + classes / 4) / 2); }; /** * A three-step registration: account details with a live password meter, a * one-time code, then a short profile. Back and Next keep every value, and the * Stepper lets the reader jump back to a step already done. */ export default function SignUp() { const { theme } = useTheme(); const [step, setStep] = useState(0); const [done, setDone] = useState(false); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [code, setCode] = useState(""); const [name, setName] = useState(""); const [role, setRole] = useState("dev"); const [interests, setInterests] = useState<string[]>(["web"]); const [terms, setTerms] = useState(false); const strength = strengthOf(password); const strengthLabel = strength > 0.75 ? "Strong" : strength > 0.45 ? "Good" : "Weak"; const strengthColor = strength > 0.75 ? theme.colors.primary : strength > 0.45 ? theme.colors.tertiary : theme.colors.error; const canContinue = [ /^\S+@\S+\.\S+$/.test(email) && password.length >= 8, code.length === 6, name.trim().length > 0 && terms, ][step]; if (done) { return ( <Box p="xl" radius="extraLarge" bg="surface" borderWidth={1} borderColor="outlineVariant" > <EmptyState icon="party-popper" title={`Welcome aboard, ${name.split(" ")[0]}!`} description="Your workspace is ready. We sent a summary to your inbox." action={{ label: "Start over", iconName: "restart", onPress: () => { setDone(false); setStep(0); }, }} /> </Box> ); } return ( <Box p="l" gap="l" radius="extraLarge" bg="surface" borderWidth={1} borderColor="outlineVariant" > <VStack spacing="xs"> <Typography variant="headlineSmall" style={{ color: theme.colors.onSurface }} > Create your account </Typography> <Typography variant="bodyMedium" style={{ color: theme.colors.onSurfaceVariant }} > Step {step + 1} of {STEPS.length} · takes about a minute </Typography> </VStack> <Stepper steps={STEPS} activeStep={step} onStepPress={(index) => index < step && setStep(index)} /> {step === 0 && ( <VStack spacing="m"> <Input label="Work email" value={email} onChangeText={setEmail} leadingIcon="email-outline" required /> <Input label="Password" value={password} onChangeText={setPassword} leadingIcon="lock-outline" secureTextEntry required /> <VStack spacing="xs"> <LinearProgress progress={strength} color={strengthColor} height={6} /> <Typography variant="labelMedium" style={{ color: theme.colors.onSurfaceVariant }} > Password strength: {password ? strengthLabel : "—"} </Typography> </VStack> </VStack> )} {step === 1 && ( <VStack spacing="m" align="center"> <Typography variant="bodyMedium" style={{ color: theme.colors.onSurfaceVariant, textAlign: "center", }} > We sent a 6-digit code to {email}. Enter it below. </Typography> <PinInput value={code} onChangeText={setCode} length={6} helperText="Any six digits will do in this demo" /> <Button mode="text" iconName="refresh" onPress={() => setCode("")}> Resend code </Button> </VStack> )} {step === 2 && ( <VStack spacing="m"> <Input label="Full name" value={name} onChangeText={setName} leadingIcon="account-outline" required /> <Select label="Role" value={role} onSelect={setRole} options={ROLES} /> <VStack spacing="s"> <Typography variant="labelLarge" style={{ color: theme.colors.onSurface }} > What are you building? </Typography> <ChipGroup multiSelect wrap accessibilityLabel="Interests" options={[...INTERESTS]} value={interests} onValueChange={setInterests} /> </VStack> <Checkbox checked={terms} onValueChange={setTerms} label="I agree to the Terms and the Privacy Policy" /> </VStack> )} <HStack justify="space-between" align="center"> <Button mode="text" iconName="arrow-left" disabled={step === 0} onPress={() => setStep(step - 1)} > Back </Button> {step < STEPS.length - 1 ? ( <Button iconName="arrow-right" iconPosition="right" disabled={!canContinue} onPress={() => setStep(step + 1)} > Continue </Button> ) : ( <Button iconName="check" disabled={!canContinue} onPress={() => setDone(true)} > Create account </Button> )} </HStack> </Box> ); }