customer-accounts
Customer Accounts
Overview
Customer accounts let shoppers save addresses for faster checkout, view order history, track shipments, and manage their profile. Every major e-commerce platform has this built in — Shopify, WooCommerce, and BigCommerce all provide account registration, login, address books, and order history without any custom development. The main decisions are: whether to make accounts optional or required, and whether to extend the platform's default account pages with additional functionality.
When to Use This Skill
- When enabling customer registration and login on a new storefront
- When customizing the account dashboard with order history and tracking
- When adding an address book to speed up returning customer checkout
- When converting guest checkout users into registered customers
- When adding wishlist or saved-for-later functionality
Core Instructions
Step 1: Determine platform and enable customer accounts
| Platform | Account System | Recommended Extension |
|---|---|---|
| Shopify | Native customer accounts (classic or new) | Customer Accounts Concierge or Flits for enhanced account pages |
| WooCommerce | My Account page (built-in, customizable) | YITH WooCommerce Wishlist; WooCommerce Memberships for gated content |
| BigCommerce | Customer account portal (built-in) | Customer Groups for tiered access; LoyaltyLion for loyalty integration |
| Custom / Headless | Build with JWT sessions and bcrypt password hashing | Required for complete control over authentication and account UX |
Step 2: Platform-specific setup
Shopify
Shopify offers two account experiences: Classic accounts and the newer New customer accounts (available on all plans).
Enable accounts and choose the experience:
- Go to Settings → Customer accounts
- Choose between:
- Classic accounts: traditional email + password login with customizable account pages via Liquid theme
- New customer accounts: passwordless login via email link; Shopify-hosted pages that Shopify controls (faster to set up, less customizable)
- Set accounts as Optional (recommended) or Required
Classic accounts setup:
- Enable Classic accounts in Settings → Customer accounts
- The account page appears at
/account— your theme controls the layout - Customize the account page in Online Store → Themes → Customize → Customer account pages
- Key pages: Login, Register, Account overview, Order detail, Addresses
Making accounts optional (strongly recommended):
- Always allow guest checkout — never force registration before purchase
- After checkout, show a "Create account to save your details" prompt
- Shopify handles the "convert guest to account" flow automatically when a customer registers with the same email used for a past order
Extending account pages:
For enhanced account functionality (wishlist, loyalty points, social login, recent orders with tracking):
- Install Flits from the App Store — comprehensive account page customization
- Or install Customer Accounts Concierge — adds wishlist, recently viewed, reorder functionality
WooCommerce
WooCommerce has a built-in My Account page that includes order history, addresses, and profile management.
Set up My Account:
- Go to WooCommerce → Settings → Accounts & Privacy
- Configure:
- Guest checkout: check "Allow customers to place orders without an account"
- Account creation: optionally auto-create accounts during checkout
- Account erasure: enable "Allow customers to request account deletion"
- The My Account page is created automatically at
/my-account/
Customize My Account tabs:
The default tabs are: Dashboard, Orders, Downloads, Addresses, Account details, Logout. Add or remove tabs:
- Add custom tabs by using the
woocommerce_account_menu_itemsfilter in your child theme's functions.php, or install a plugin like YITH WooCommerce Customize My Account Page - Reorder tabs by modifying the array in the filter
Address book:
- Go to WooCommerce → Settings → Accounts → Allow customers to store multiple addresses
- Customers can add/edit multiple addresses in My Account → Addresses
- WooCommerce pre-fills checkout with the default address automatically
Wishlist:
- Install YITH WooCommerce Wishlist (free/premium)
- Adds a "Add to Wishlist" button on product pages and a wishlist page in My Account
Converting guest to registered after purchase:
WooCommerce shows a "Create account" prompt in order confirmation emails automatically when accounts are enabled but optional.
BigCommerce
BigCommerce has a built-in customer portal.
Enable and configure:
- Go to Store Setup → Store Settings → Display → Customer Account Access
- Set to "Optional" to allow guest checkout
- Account pages are managed by your theme — customize in the Stencil theme editor
Customer groups:
Use customer groups for tiered access, B2B pricing, or member-only categories:
- Go to Customers → Customer Groups → Add Group
- Set group-specific pricing, category visibility, or shipping rules
- Assign customers to groups manually or auto-assign based on purchase history
Address book:
- Built-in under the customer account portal
- Customers can save multiple addresses and select them at checkout
Custom / Headless
For headless storefronts, build a complete account system with secure authentication:
// lib/auth.ts
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { z } from 'zod';
const registerSchema = z.object({
email: z.string().email(),
password: z.string().min(8).max(128),
firstName: z.string().min(1).max(100),
lastName: z.string().min(1).max(100),
acceptsMarketing: z.boolean().default(false),
});
// POST /api/customers/register
export async function register(req: Request, res: Response) {
const input = registerSchema.parse(req.body);
const existing = await db.customers.findByEmail(input.email.toLowerCase());
if (existing) return res.status(409).json({ error: 'An account with this email already exists' });
const passwordHash = await bcrypt.hash(input.password, 12); // Cost factor 12 minimum
const customer = await db.customers.create({ ...input, email: input.email.toLowerCase(), passwordHash });
await sendVerificationEmail(customer);
const token = jwt.sign({ sub: customer.id, type: 'customer' }, process.env.JWT_SECRET!, { expiresIn: '7d' });
res.status(201).json({ customer: omit(customer, ['passwordHash']), token });
}
// POST /api/customers/login
export async function login(req: Request, res: Response) {
const { email, password } = req.body;
const customer = await db.customers.findByEmail(email.toLowerCase());
// Use the same error for both "not found" and "wrong password" to prevent email enumeration
if (!customer || !customer.passwordHash) return res.status(401).json({ error: 'Invalid email or password' });
if (customer.status === 'disabled') return res.status(403).json({ error: 'This account has been disabled' });
const valid = await bcrypt.compare(password, customer.passwordHash);
if (!valid) return res.status(401).json({ error: 'Invalid email or password' });
const token = jwt.sign({ sub: customer.id, type: 'customer' }, process.env.JWT_SECRET!, { expiresIn: '7d' });
res.json({ customer: omit(customer, ['passwordHash']), token });
}
// Address book CRUD — GET /api/customers/me/addresses
export async function listAddresses(req: AuthRequest, res: Response) {
const addresses = await db.customerAddresses.findMany({ where: { customerId: req.customerId } });
res.json({ addresses });
}
// POST /api/customers/me/addresses
export async function addAddress(req: AuthRequest, res: Response) {
const existing = await db.customerAddresses.findMany({ where: { customerId: req.customerId } });
const input = { ...addressSchema.parse(req.body), customerId: req.customerId };
if (input.isDefault || existing.length === 0) {
await db.customerAddresses.updateMany({ where: { customerId: req.customerId }, data: { isDefault: false } });
input.isDefault = true;
}
const address = await db.customerAddresses.create({ data: input });
res.status(201).json({ address });
}
// GET /api/customers/me/orders — paginated order history with tracking
export async function listOrders(req: AuthRequest, res: Response) {
const page = parseInt(req.query.page as string) || 1;
const limit = Math.min(parseInt(req.query.limit as string) || 10, 50);
const [orders, total] = await Promise.all([
db.orders.findMany({ where: { customerId: req.customerId }, skip: (page - 1) * limit, take: limit, orderBy: { createdAt: 'desc' }, include: { lineItems: true, shipments: true } }),
db.orders.count({ where: { customerId: req.customerId } }),
]);
res.json({ orders, pagination: { page, limit, total, totalPages: Math.ceil(total / limit) } });
}
Step 3: Configure post-checkout account creation
The highest-converting moment to ask for account creation is immediately after a successful first purchase, not before.
Shopify: The order confirmation page includes a "Create account" button automatically when customer accounts are enabled but optional. Customize the message in Online Store → Themes → Customize → Order status page.
WooCommerce: Customize the "Thank you" page message in WooCommerce → Settings → Accounts or use the Checkout Field Editor plugin to add a post-checkout account creation prompt.
What to say: "Save your details for faster checkout next time" is more compelling than "Create an account" — focus on the benefit, not the action.
Best Practices
- Always make accounts optional — never force registration before purchase; post-purchase conversion rates are much higher than pre-purchase
- Offer social login where possible (Google, Facebook) — reduces friction significantly for mobile users; use apps like Single Sign-On (SSO) for Shopify or WooCommerce Social Login for WooCommerce
- Auto-populate checkout from saved addresses — the primary value of accounts is faster checkout; ensure the default address pre-fills automatically
- Send a re-engagement email 24 hours after checkout to guest buyers inviting them to create an account — timing matters
- Enable GDPR/CCPA account deletion — every platform supports this; make sure the "delete my account" option is easy to find; see your platform's documentation for the data erasure workflow
Common Pitfalls
| Problem | Solution |
|---|---|
| Customers can't see past orders after creating an account | On Shopify, past orders from guest checkout are linked when the customer registers with the same email; verify the account linking is enabled in Settings → Customer accounts |
| Address validation fails for international customers | Don't mark state/province as required — many countries don't have them; WooCommerce handles this with country-dependent field visibility |
| JWT tokens too long-lived | Use 15-minute access tokens with refresh tokens for better security, or use server-side sessions with a secure, HttpOnly cookie |
| No way to merge duplicate customer records | Shopify and WooCommerce both support customer merge in the admin; for custom builds, build a merge tool that consolidates orders and addresses |
Related Skills
- @customer-segmentation
- @customer-lifetime-value
- @personalization-engine