Frontend Architecture Document

Frontend Architecture Document 
 
 Project: Drop — Fintech Payment App
 Version: 0.1.0
 Date: 2026-02-23
 Author: John (AI Director, ALAI)
 Status: In Review
 Reviewers: Alem Bašić (CEO) 
 
 Document History 
 
 
 
 Version 
 Date 
 Author 
 Changes 
 
 
 
 
 0.1 
 2026-02-23 
 John 
 Initial draft from source code analysis 
 
 
 
 
 1. Purpose & Scope 
 This document defines the frontend architecture for Drop , a fintech payment app for all residents of Norway/Scandinavia. It covers the Next.js web application ( src/drop-app/ ) and the static marketing site ( landing/ ). 
 Intended readers: Frontend engineers, mobile engineers, architects, product managers. 
 In scope: 
 
 Next.js App Router web application ( src/drop-app/ ) 
 Static HTML marketing landing page ( landing/index.html ) 
 Expo React Native mobile app ( src/drop-mobile/ ) — details in mobile-architecture.md 
 
 Out of scope: Backend API (Hono v4), infrastructure, CI/CD pipelines. 
 
 2. Framework Choice & Rationale 
 
 
 
 Criterion 
 Weight 
 Next.js 15 
 Nuxt 3 
 SvelteKit 
 Remix 
 
 
 
 
 Team expertise 
 30% 
 9 
 5 
 4 
 6 
 
 
 Ecosystem maturity 
 25% 
 9 
 7 
 6 
 7 
 
 
 Performance 
 20% 
 9 
 8 
 9 
 8 
 
 
 DX / tooling 
 15% 
 9 
 7 
 7 
 7 
 
 
 License / cost 
 10% 
 10 
 10 
 10 
 10 
 
 
 
 Selected Framework: Next.js 15 (App Router) + React 19 
 Rationale: 
Next.js was selected because the entire team (AI-driven) operates in TypeScript + React. The App Router supports a hybrid rendering model essential for Drop: Server Components for the marketing landing page (fast, SEO-friendly), and Client Components for the authenticated payment flows (real-time, user-specific). Code sharing between the Next.js web app and the Expo mobile app is maximized — shared hooks, types, and validation logic. The Vercel deployment model provides zero-config CDN, edge middleware for auth protection, and ISR for marketing pages without managing infrastructure. 
 Node/runtime version: Node.js 20 LTS 
 Package manager: pnpm 
 
 3. Project Folder Structure 
 src/drop-app/
├── src/
│ ├── app/ # Next.js App Router (file-based routing)
│ │ ├── page.tsx # / — Marketing home (Server Component)
│ │ ├── layout.tsx # Root layout — fonts, PWA, cookie consent
│ │ ├── login/page.tsx # /login — Client Component
│ │ ├── register/page.tsx # /register — Multi-step onboarding
│ │ ├── dashboard/page.tsx # /dashboard — Auth required
│ │ ├── accounts/page.tsx # /accounts — Bank account linking
│ │ ├── transactions/page.tsx # /transactions — Transaction history
│ │ ├── scan/page.tsx # /scan — QR payment
│ │ ├── send/page.tsx # /send — Remittance flow
│ │ ├── profile/ # /profile and sub-routes
│ │ ├── notifications/page.tsx # /notifications
│ │ ├── cards/page.tsx # /cards — Feature-flagged (future)
│ │ ├── fees/page.tsx # /fees — Public fee disclosure
│ │ ├── privacy/page.tsx # /privacy — GDPR policy
│ │ ├── terms/page.tsx # /terms — Terms of service
│ │ ├── complaints/page.tsx # /complaints — Finansavtaleloven §3-53
│ │ ├── withdrawal/page.tsx # /withdrawal — Angrerettloven
│ │ └── api/ # API routes (BFF layer)
│ │ ├── auth/ # login, logout, me, register
│ │ ├── transactions/ # remittance, qr-payment
│ │ ├── recipients/ # CRUD recipients
│ │ ├── rates/ # Exchange rates
│ │ ├── cards/ # Feature-flagged
│ │ ├── merchants/ # Merchant dashboard
│ │ ├── settings/ # User preferences
│ │ ├── notifications/ # Push notifications
│ │ └── consents/ # Cookie consent
│ ├── components/
│ │ ├── ui/ # shadcn/ui primitives (Radix-based)
│ │ ├── bottom-nav.tsx # Fixed bottom navigation (5 tabs)
│ │ ├── drop-logo.tsx # DropLogo, DropWordmark, DropLogoFull, DropAppIcon
│ │ ├── drop-icons.tsx # 9 custom domain-specific icons
│ │ ├── cookie-consent.tsx # GDPR cookie consent banner + modal
│ │ ├── pre-payment-disclosure.tsx # PSD2 pre-payment disclosure modal
│ │ └── pwa-register.tsx # Service Worker registration
│ ├── lib/
│ │ ├── use-auth.ts # useAuth() — auth hook with redirect
│ │ ├── feature-flags.ts # Feature flag system (env-var based)
│ │ └── middleware/ # auth-middleware, error-handler, validation
│ └── middleware.ts # Next.js middleware (auth protection)
├── public/
│ ├── sw.js # Service worker (PWA)
│ └── manifest.json # Web app manifest
├── landing/ # Static marketing site (separate from Next.js)
│ ├── index.html # Main landing page (~646 lines, pure HTML/CSS/JS)
│ └── pages/ # 12 sub-pages (priser, personvern, vilkar, etc.)
├── globals.css # CSS custom properties (brand tokens)
├── next.config.ts
└── package.json
 
 
 4. Rendering Strategy 
 
 
 
 Page Type 
 Strategy 
 Rationale 
 
 
 
 
 / — Marketing home 
 Server Component (SSG) 
 SEO-critical, no auth, static content 
 
 
 /fees , /privacy , /terms , /complaints , /withdrawal 
 Client Component (CSR) 
 Public pages, no auth required 
 
 
 /login , /register 
 Client Component (CSR) 
 Form state, client-side validation 
 
 
 /dashboard , /accounts , /transactions 
 Client Component (CSR) 
 Auth-gated, personalized, real-time data 
 
 
 /scan , /send 
 Client Component (CSR) 
 Multi-step flows, camera access, form state 
 
 
 /profile , /notifications , /cards 
 Client Component (CSR) 
 Auth-gated, user-specific 
 
 
 /api/** 
 API Routes 
 BFF layer — cookie-to-bearer proxy 
 
 
 
 Hydration approach: Full hydration. Server Components used for the marketing page ( page.tsx ). All authenticated app pages are Client Components using "use client" directive. 
 Note: The marketing landing page ( landing/index.html ) is a completely separate pure HTML/CSS/JS file served statically. It does not use the Next.js framework. 
 
 5. Routing Architecture 
 5.1 Route Organization 
 / → Marketing home (Server Component)
/login → Login (Client)
/register → Registration / Onboarding (Client, 4-step)
/dashboard → Dashboard (Client, auth required)
/accounts → Bank accounts via AISP (Client, auth required)
/transactions → Transaction history (Client, auth required)
/scan → QR scanner (Client, auth required)
/send → Remittance flow (Client, auth required)
/profile → Profile (Client, auth required)
/profile/personal → Personal info — BankID-verified, read-only
/profile/security → Security settings — 2FA, active devices
/profile/notifications → Notification preferences
/profile/language → Language selection (nb, en, bs, sq)
/notifications → Notifications center (Client, auth required)
/cards → Card management (Client, auth required, feature-flagged)
/fees → Fee disclosure (Public)
/privacy → Privacy policy / GDPR (Public)
/terms → Terms of service (Public)
/complaints → Complaint form — Finansavtaleloven §3-53 (Public)
/withdrawal → Right of withdrawal — Angrerettloven (Public)
/api/** → API routes (BFF — cookie auth proxy to Hono backend)
 
 5.2 Route Guards / Middleware 
 Auth is enforced at two levels: 
 Level 1 — useAuth() hook (per-page): 
 // src/lib/use-auth.ts
// Default: redirectIfUnauthenticated = true
const { user, loading } = useAuth();
// On 401: redirects to /login
 
 Level 2 — Middleware (future): 
 Middleware execution order:
1. Request logging
2. Authentication check (JWT cookie validation)
3. Redirect unauthenticated → /login
4. Feature flag gate (returns 404 if flag disabled)
 
 Protected routes: All routes under /dashboard , /accounts , /transactions , /scan , /send , /profile/** , /notifications , /cards . 
 Public routes: / , /login , /register , /fees , /privacy , /terms , /complaints , /withdrawal . 
 
 6. Build & Bundle Configuration 
 6.1 Key Config Options 
 
 
 
 Option 
 Value 
 Reason 
 
 
 
 
 Output 
 standalone 
 Docker-optimized deployment 
 
 
 Image optimization 
 next/image 
 WebP conversion, lazy loading 
 
 
 Bundle analyzer 
 ANALYZE=true 
 On-demand local analysis 
 
 
 Source maps 
 Production: hidden 
 Upload to Sentry only 
 
 
 Compression 
 gzip + brotli 
 CDN-level compression 
 
 
 PWA 
 Service Worker at /sw.js 
 Offline capability (basic) 
 
 
 
 6.2 Code Splitting Strategy 
 
 Route-level splitting: Automatic per App Router segment 
 Component-level splitting: dynamic() for heavy components (Dialog, QR scanner UI) 
 Feature flags: Cards page components lazy-loaded (all flags default to false ) 
 
 
 7. Performance Budget 
 
 
 
 Metric 
 Target 
 Tool 
 
 
 
 
 LCP (Largest Contentful Paint) 
 < 2.5s 
 Lighthouse, CrUX 
 
 
 INP (Interaction to Next Paint) 
 < 200ms 
 CrUX 
 
 
 CLS (Cumulative Layout Shift) 
 < 0.1 
 Lighthouse 
 
 
 TTFB (Time to First Byte) 
 < 600ms 
 WebPageTest 
 
 
 Total JS bundle (compressed) 
 < 200 KB 
 Bundlesize CI check 
 
 
 First page load (mobile 4G) 
 < 3s 
 WebPageTest 
 
 
 Lighthouse Performance Score 
 ≥ 90 
 Lighthouse CI 
 
 
 
 Note: The marketing page ( / ) is the primary SEO and performance target. The app pages (dashboard, send) accept slightly higher bundle sizes due to shadcn/ui + Radix dependencies. 
 
 8. Asset Management 
 8.1 Images 
 
 Format: WebP primary, JPEG fallback, SVG for vectors (logos, icons) 
 Optimization: next/image with defined domains 
 Lazy loading: Native loading="lazy" via next/image 
 
 8.2 Fonts 
 
 Fraunces (variable, 400-900) — loaded via next/font/google in layout.tsx 
 DM Sans (variable, 400-700) — loaded via next/font/google in layout.tsx 
 Geist Mono — loaded via next/font/google in layout.tsx 
 CSS variables: --font-fraunces , --font-dm-sans , --font-geist-mono 
 font-display: swap on all faces (handled by next/font ) 
 
 8.3 Icons 
 
 Primary library: lucide-react (inline SVG via React components) 
 Custom icons: components/drop-icons.tsx — 9 domain-specific icons (IconSendMoney, IconQrScan, IconVirtualCard, IconShield, IconFastTransfer, IconCorridors, IconWallet, IconHistory, IconTopUp) 
 Social auth icons: Inline SVG (BankID green, Vipps orange) 
 Delivery: Inline SVG via component (no icon font, no sprite) 
 
 
 9. Internationalization (i18n) Strategy 
 
 
 
 Item 
 Decision 
 
 
 
 
 Library 
 None (Phase 1 — Norwegian only) 
 
 
 Default locale 
 nb (Norwegian Bokmål) 
 
 
 Supported locales 
 nb (launch), en , bs , sq (Phase 2) 
 
 
 Language selection 
 /profile/language — PATCH /api/settings with { language: string } 
 
 
 Translation format 
 TBD — requires i18n library selection 
 
 
 RTL support 
 No 
 
 
 
 Phase 1: All UI text hardcoded in Norwegian Bokmål. Language setting stored in user preferences ( /api/settings ) but not yet applied to UI translation. 
 Phase 2: Introduce next-intl or i18next with per-locale JSON translation files for nb , en , bs , sq . 
 
 10. Error Boundary Strategy 
 
 
 
 Level 
 Scope 
 Behavior 
 
 
 
 
 Global error.tsx 
 Full page crash 
 Show branded error page 
 
 
 Auth failure 
 API 401 
 useAuth() redirects to /login 
 
 
 Form submission 
 Mutation failure 
 Inline error message ( text-[#EF4444] ) + retry 
 
 
 Data fetch 
 useEffect fetch failure 
 Caught in .catch() , silent or empty state 
 
 
 Feature flag 
 Flag disabled 
 Returns 404 or "Feature not available" message 
 
 
 
 Error reporting: TBD — Sentry integration planned for production. 
 
 11. Environment Configuration 
 
 
 
 Variable 
 Dev 
 Staging 
 Prod 
 Description 
 
 
 
 
 NEXT_PUBLIC_API_URL 
 http://localhost:3000/api 
 https://drop-app-staging.vercel.app/api 
 https://drop-app.vercel.app/api 
 Backend API base URL (mobile uses this) 
 
 
 NEXT_PUBLIC_APP_ENV 
 development 
 staging 
 production 
 Runtime environment flag 
 
 
 NEXT_PUBLIC_FF_VIRTUAL_CARDS 
 false 
 false 
 false 
 Cards feature flag 
 
 
 NEXT_PUBLIC_FF_PHYSICAL_CARDS 
 false 
 false 
 false 
 Physical cards flag 
 
 
 NEXT_PUBLIC_FF_NOTIFICATIONS 
 true 
 true 
 true 
 Notifications feature flag 
 
 
 NEXT_PUBLIC_FF_MERCHANT_DASHBOARD 
 true 
 true 
 true 
 Merchant dashboard flag 
 
 
 SENTRY_DSN 
 optional 
 required 
 required 
 Error reporting (server-side) 
 
 
 
 Feature flag convention: NEXT_PUBLIC_FF_ + SCREAMING_SNAKE_CASE flag name. 
 Secrets management: All non-public secrets in Vaultwarden ( vault.basicconsulting.no ). Never committed. 
 
 12. Dependency Management Policy 
 
 
 
 Rule 
 Detail 
 
 
 
 
 Allowed licenses 
 MIT, Apache-2.0, ISC, BSD-2, BSD-3 
 
 
 Security audit 
 pnpm audit in CI — fail on high/critical 
 
 
 Update cadence 
 Monthly Renovate PRs for minor/patch; major = manual 
 
 
 Banned patterns 
 No moment.js (use date-fns ), no lodash (use native) 
 
 
 
 Key dependencies: 
 
 next@15 , react@19 — framework 
 shadcn/ui + @radix-ui/* — component primitives 
 lucide-react — icon library 
 tailwindcss — styling 
 class-variance-authority , clsx , tailwind-merge — class utilities 
 
 
 13. Architecture Diagram 
 graph TB
 subgraph "Client Browser / Mobile"
 Browser["User Browser (PWA)"]
 Mobile["Expo Mobile App"]
 end

 subgraph "Frontend — Next.js 15 App Router"
 CDN["Vercel CDN (Static Assets + Edge)"]
 Marketing["Marketing Page (Server Component, SSG)"]
 AppPages["App Pages (Client Components, CSR)"]
 BFF["BFF API Routes (/api/*)\nCookie → Bearer proxy"]
 Middleware["Next.js Middleware\n(Auth guard)"]
 Components["shadcn/ui + Custom Components\nBottomNav, DropLogo, drop-icons"]
 AuthHook["useAuth() Hook\nGET /api/auth/me"]
 FeatureFlags["Feature Flags\nenv-var based"]
 end

 subgraph "Backend — Hono v4"
 HonoAPI["Hono REST API\n/v1/* — Bearer auth"]
 OpenBanking["Open Banking (PSD2)\nAISP + PISP"]
 BankID["BankID OIDC\nAuthentication"]
 end

 Browser --> CDN
 Browser --> Marketing
 Browser --> Middleware
 Middleware --> AppPages
 AppPages --> Components
 AppPages --> AuthHook
 AppPages --> FeatureFlags
 AuthHook --> BFF
 BFF --> HonoAPI
 HonoAPI --> OpenBanking
 HonoAPI --> BankID
 Mobile --> HonoAPI
 
 
 Approval 
 
 
 
 Role 
 Name 
 Date 
 Signature 
 
 
 
 
 Author 
 John (AI Director) 
 2026-02-23 
 
 
 
 Tech Lead 
 
 
 
 
 
 Architect 
 
 
 
 
 
 Engineering Manager