Next.js 15 + TypeScript + Tailwind CSS frontend for blockchain-based vaccination records on Stellar.
VacciChain allows healthcare providers to issue soulbound, non-transferable vaccination NFTs to patients via Soroban smart contracts. Third parties (schools, employers, border agencies) can verify status on-chain without accessing personal details.
- Wallet Integration: Freighter wallet support with Stellar network detection
- SEP-10 Authentication: Secure challenge-response authentication with JWT tokens
- Patient Dashboard: View all vaccination records chronologically
- Issuer Dashboard: Multi-step form to mint vaccination records
- Public Verification: Search any wallet to verify vaccination status
- Type-Safe: Full TypeScript with Zod validation
- Dark Medical-Tech Theme: Tailwind CSS with blue/green accents
- Framework: Next.js 15 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS
- State Management: React Hooks
- Validation: Zod + React Hook Form
- Wallet: Freighter API
- Blockchain: Stellar SDK
- Auth: SEP-10 Web Auth
- Node.js 20+ (14MB of dependencies)
- npm or yarn
- Freighter wallet browser extension
- Stellar testnet account with XLM
# Clone and navigate
git clone <repo>
cd vaccichain-web
# Install dependencies
npm install
# Configure environment
cp .env.example .env.local
# Edit .env.local with your contract ID and backend URL# Stellar Network (testnet or mainnet)
NEXT_PUBLIC_STELLAR_NETWORK=testnet
# Soroban RPC Endpoint
NEXT_PUBLIC_SOROBAN_RPC_URL=https://soroban-testnet.stellar.org
# Deployed VacciChain Contract
NEXT_PUBLIC_CONTRACT_ID=C...
# Backend API Base URL
NEXT_PUBLIC_BACKEND_URL=http://localhost:4000# Start dev server (http://localhost:3000)
npm run dev
# Build for production
npm run build
# Start production server
npm start
# Run linter
npm run lintapp/
├── layout.tsx # Root layout
├── page.tsx # Landing page
├── dashboard/
│ └── page.tsx # Patient dashboard
├── issuer/
│ └── page.tsx # Issuer dashboard
└── verify/
└── page.tsx # Public verification page
components/
├── WalletButton.tsx # Connect/disconnect wallet
├── VaccinationCard.tsx # Display vaccination record
├── VerificationBadge.tsx # Status badge
└── MintForm.tsx # Issuer minting form
hooks/
├── useFreighter.ts # Wallet & SEP-10 auth
└── useVaccination.ts # API calls & verification
lib/
├── types.ts # TypeScript interfaces
├── env.ts # Environment & API routes
├── validation.ts # Zod schemas
└── stellar-utils.ts # Stellar helpers
Manages Freighter wallet connection and SEP-10 authentication:
- Detects Freighter availability
- Handles SEP-10 challenge-response flow
- Stores JWT in secure cookie
- Provides auth state and connect/disconnect methods
const { auth, connect, disconnect, loading, error } = useFreighter();Handles all vaccine-related API calls:
fetchPatientRecords(wallet, jwt)- Get authenticated patient recordsverifyPublic(wallet)- Public verification (no auth)mintVaccination(formData, jwt)- Issue new record (issuer only)
const { verificationResult, records, loading, error, verifyPublic, mintVaccination } = useVaccination();- Project overview and feature highlights
- Call-to-action buttons based on auth state
- Navigation to dashboard/issuer/verify pages
- Requires authentication
- Displays all vaccination records in timeline
- Shows vaccine name, date, issuer, and status
- Indicates revoked records
- Restricted to authenticated issuers
- Multi-step form with validation:
- Patient wallet address (Zod validated)
- Vaccine name
- Date administered
- Issuer wallet (auto-populated)
- Success/error feedback
- Instructions for issuing records
- Public access (no auth required)
- Search by wallet address
- Display vaccination status badge
- Show active/inactive status
- Optional: Display full records if vaccinated
1. User connects Freighter wallet
2. Request SEP-10 challenge from backend
3. Sign challenge with Freighter
4. Submit signed challenge to backend
5. Backend verifies & returns JWT + role
6. Store JWT in secure cookie
7. Include JWT in authenticated API calls
- JWT tokens stored in secure, HTTP-only cookies
- SEP-10 challenge-response prevents replay attacks
- Zod validation on all forms
- No hardcoded secrets
- HTTPS only in production
- Freighter handles key management
All API calls require a backend running on NEXT_PUBLIC_BACKEND_URL:
POST /auth/sep10- Get challengePOST /auth/verify- Verify signed challengeGET /vaccination/:wallet- Get patient records (auth required)POST /vaccination/issue- Mint record (issuer only)GET /verify/:wallet- Public verification (no auth)
Test flows:
- Connect wallet - Use Freighter testnet account
- Patient flow - View dashboard after auth
- Issuer flow - Mint a record to test wallet
- Verify flow - Search for vaccinated wallet
- Public verify - Search without authentication
| Issue | Solution |
|---|---|
| Freighter not detected | Install Freighter extension, refresh page |
| Network mismatch | Ensure Freighter is on Testnet |
| API errors | Check backend is running on NEXT_PUBLIC_BACKEND_URL |
| Auth fails | Clear cookies, reconnect wallet |
| Build fails | Ensure Node 20+, run npm install |
# Push to GitHub
git push
# Deploy from Vercel dashboard
# Set environment variables in Vercel settingsFROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]# Configure next.config.js for static export
npm run build
# Output in .next/standalone- Create feature branch
- Follow TypeScript/Tailwind conventions
- No
anytypes - Test on testnet
- Submit PR
MIT © VacciChain