diff --git a/frontend/.env.example b/frontend/.env.example
new file mode 100644
index 0000000..527c421
--- /dev/null
+++ b/frontend/.env.example
@@ -0,0 +1,11 @@
+# Public app URL used for canonical links and callback generation.
+NEXT_PUBLIC_APP_URL=http://localhost:3000
+
+# Backend API that powers payment intent + claim verification requests.
+NEXT_PUBLIC_API_BASE_URL=http://localhost:4000
+
+# Chain/network selector used by the reference claim UI.
+NEXT_PUBLIC_CRYPTO_NETWORK=stellar-testnet
+
+# Human support destination shown on error fallback states.
+NEXT_PUBLIC_SUPPORT_EMAIL=support@example.com
diff --git a/frontend/README.md b/frontend/README.md
new file mode 100644
index 0000000..0f8c21c
--- /dev/null
+++ b/frontend/README.md
@@ -0,0 +1,52 @@
+# Bridgelet Frontend
+
+Reference Next.js UI for initiating and claiming crypto payments.
+
+## Tech stack
+
+- Next.js 16 (App Router)
+- TypeScript 5 in strict mode
+- Tailwind CSS 4
+
+## Local setup
+
+1. Install dependencies:
+
+ ```bash
+ cd frontend
+ npm install
+ ```
+
+2. Configure environment variables:
+
+ ```bash
+ cp .env.example .env.local
+ ```
+
+3. Start development server:
+
+ ```bash
+ npm run dev
+ ```
+
+4. Open [http://localhost:3000](http://localhost:3000).
+
+## Routes
+
+- `/` homepage placeholder
+- `/send` sender flow placeholder
+- `/claim/[token]` recipient claim placeholder
+
+## Quality checks
+
+- Type-check only:
+
+ ```bash
+ npm run typecheck
+ ```
+
+- Production build:
+
+ ```bash
+ npm run build
+ ```
diff --git a/frontend/app/claim/[token]/page.tsx b/frontend/app/claim/[token]/page.tsx
new file mode 100644
index 0000000..fd85f96
--- /dev/null
+++ b/frontend/app/claim/[token]/page.tsx
@@ -0,0 +1,28 @@
+import { PageShell } from '@/components/page-shell';
+import { publicEnv } from '@/lib/env';
+
+type ClaimPageProps = {
+ params: Promise<{ token: string }>;
+};
+
+export default async function ClaimPage({ params }: ClaimPageProps) {
+ const { token } = await params;
+
+ return (
+
+
+
+
- Claim Token
+ - {token}
+
+
+
Support Contact
+ {publicEnv.NEXT_PUBLIC_SUPPORT_EMAIL}
+
+
+
+ );
+}
diff --git a/frontend/app/globals.css b/frontend/app/globals.css
new file mode 100644
index 0000000..c37a02e
--- /dev/null
+++ b/frontend/app/globals.css
@@ -0,0 +1,9 @@
+@import "tailwindcss";
+
+:root {
+ color-scheme: light;
+}
+
+body {
+ @apply min-h-screen bg-slate-50 text-slate-900 antialiased;
+}
diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx
new file mode 100644
index 0000000..b454340
--- /dev/null
+++ b/frontend/app/layout.tsx
@@ -0,0 +1,20 @@
+import type { Metadata } from 'next';
+import type { ReactNode } from 'react';
+import './globals.css';
+
+export const metadata: Metadata = {
+ title: 'Bridgelet Payments',
+ description: 'Reference UI for sending and claiming crypto payments.'
+};
+
+type RootLayoutProps = {
+ children: ReactNode;
+};
+
+export default function RootLayout({ children }: RootLayoutProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx
new file mode 100644
index 0000000..769f0c3
--- /dev/null
+++ b/frontend/app/page.tsx
@@ -0,0 +1,29 @@
+import Link from 'next/link';
+import { PageShell } from '@/components/page-shell';
+
+export default function HomePage() {
+ return (
+
+
+
Select a flow to continue.
+
+
+
+ );
+}
diff --git a/frontend/app/send/page.tsx b/frontend/app/send/page.tsx
new file mode 100644
index 0000000..8be47d4
--- /dev/null
+++ b/frontend/app/send/page.tsx
@@ -0,0 +1,22 @@
+import { PageShell } from '@/components/page-shell';
+import { publicEnv } from '@/lib/env';
+
+export default function SendPage() {
+ return (
+
+
+
+
- API Base URL
+ - {publicEnv.NEXT_PUBLIC_API_BASE_URL}
+
+
+
- Network
+ - {publicEnv.NEXT_PUBLIC_CRYPTO_NETWORK}
+
+
+
+ );
+}
diff --git a/frontend/components/page-shell.tsx b/frontend/components/page-shell.tsx
new file mode 100644
index 0000000..d473a4c
--- /dev/null
+++ b/frontend/components/page-shell.tsx
@@ -0,0 +1,19 @@
+import type { ReactNode } from 'react';
+
+type PageShellProps = {
+ title: string;
+ description: string;
+ children?: ReactNode;
+};
+
+export function PageShell({ title, description, children }: PageShellProps) {
+ return (
+
+
+ {title}
+ {description}
+
+
+
+ );
+}
diff --git a/frontend/lib/env.ts b/frontend/lib/env.ts
new file mode 100644
index 0000000..0505446
--- /dev/null
+++ b/frontend/lib/env.ts
@@ -0,0 +1,21 @@
+type PublicEnv = {
+ NEXT_PUBLIC_APP_URL: string;
+ NEXT_PUBLIC_API_BASE_URL: string;
+ NEXT_PUBLIC_CRYPTO_NETWORK: string;
+ NEXT_PUBLIC_SUPPORT_EMAIL: string;
+};
+
+function readPublicEnv(name: keyof PublicEnv): string {
+ const value = process.env[name];
+ if (!value || value.trim().length === 0) {
+ return 'not-configured';
+ }
+ return value;
+}
+
+export const publicEnv: PublicEnv = {
+ NEXT_PUBLIC_APP_URL: readPublicEnv('NEXT_PUBLIC_APP_URL'),
+ NEXT_PUBLIC_API_BASE_URL: readPublicEnv('NEXT_PUBLIC_API_BASE_URL'),
+ NEXT_PUBLIC_CRYPTO_NETWORK: readPublicEnv('NEXT_PUBLIC_CRYPTO_NETWORK'),
+ NEXT_PUBLIC_SUPPORT_EMAIL: readPublicEnv('NEXT_PUBLIC_SUPPORT_EMAIL')
+};
diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts
new file mode 100644
index 0000000..52eb120
--- /dev/null
+++ b/frontend/next-env.d.ts
@@ -0,0 +1,4 @@
+///
+///
+
+// NOTE: This file should not be edited.
diff --git a/frontend/next.config.js b/frontend/next.config.js
new file mode 100644
index 0000000..91ef62f
--- /dev/null
+++ b/frontend/next.config.js
@@ -0,0 +1,6 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: true,
+};
+
+module.exports = nextConfig;
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 0000000..2d76cfd
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "bridgelet-frontend",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "next": "^16.0.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4.0.0",
+ "@types/node": "^22.0.0",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "tailwindcss": "^4.0.0",
+ "typescript": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=20.10.0"
+ }
+}
diff --git a/frontend/postcss.config.mjs b/frontend/postcss.config.mjs
new file mode 100644
index 0000000..6ebbee3
--- /dev/null
+++ b/frontend/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ '@tailwindcss/postcss': {}
+ }
+};
+
+export default config;
diff --git a/frontend/public/.gitkeep b/frontend/public/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 0000000..c748abb
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,33 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./*"]
+ },
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "types": ["node"],
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}