X-Change Guide

Frontend overview

The frontend is a Next.js 15 app with React 19 and Tailwind. It has one special job that most websites don't: it holds your private key and signs things in the browser.

The stack

Next.js 15

App Router framework. Each page is a folder with a page.tsx.

React 19 + TypeScript

Strict types everywhere; pages are client components.

Tailwind CSS

Utility-first styling with a shadcn-style dark green theme.

Zustand

Tiny client store for wallet + auth state (persisted to localStorage).

React Query

Fetches + polls server data (balances, order book, blocks) every few seconds.

@noble/curves + @noble/hashes

ed25519 + SHA-3/SHA-256 in pure TypeScript. The crypto brain.

The key insight: self-custody

The private key never touches the server. It's generated in the browser and stored in localStorage. Every mutating action — login, sending coins — is signed client-side with that key. This is the "your keys, your coins" model.

Browser
secret key lives here
signs challenge · signs transactions
only signatures go over the wire
Node
never sees the key
verifies signatures, stores state
This is your strongest defense point
Being able to explain why keys stay in the browser — and the trade-offs (if you lose the key, you lose the coins; there's no "reset password") — shows real understanding.

Project structure

apps/web — folder layout
1app/
2├── layout.tsx # root layout: providers, dark theme, fonts
3├── globals.css # Tailwind + CSS variables (dark green theme)
4├── (marketing)/page.tsx # landing page /
5├── (auth)/signin/page.tsx # create/import wallet + sign in
6├── (dashboard)/
7 ├── layout.tsx # sidebar + top bar shell
8 ├── dashboard/page.tsx # balances, chain height, faucet, recent txs
9 ├── wallet/page.tsx # send XCH (client-side signing)
10 ├── trade/page.tsx # order book + place orders
11 ├── explorer/page.tsx # block explorer + search
12 ├── staking/page.tsx # stake/unstake, validators
13 ├── multisig/page.tsx # N-of-M wallets
14 └── admin/page.tsx # chain metrics + charts
15├── api/health/route.ts # edge function proxying node /health
16components/ # layout + UI kit + feature components
17lib/
18├── api.ts # fetch wrapper + typed endpoints
19├── crypto-utils.ts # THE mirror of the node's crypto
20└── utils.ts # formatting helpers (shortAddress, etc.)
21store/
22├── wallet.ts # zustand: wallet + secret key (localStorage)
23└── auth.ts # zustand: JWT tokens (localStorage)

Why parentheses around (dashboard)?

(dashboard), (auth) and (marketing) are route groups — they organise code without adding to the URL. app/(dashboard)/dashboard/page.tsx is served at /dashboard. The group also lets the dashboard pages share one layout (the sidebar shell).

Client vs server components

  • The root layout.tsx and landing page are server components (no interactivity needed).
  • Every dashboard/auth page is "use client" — they need hooks, state, and crypto.
  • The only real "server" logic is app/api/health/route.ts, an edge function that proxies the node's health endpoint.

Where the real work is documented