Skip to content

A production-ready Next.js 16 starter template with passwordless auth, i18n, Azure Cosmos DB, and HeroUI

License

Notifications You must be signed in to change notification settings

DikaVer/skeleton-web-next

ACME — Next.js Starter Template

A production-ready Next.js 16 starter template with passwordless authentication, internationalization, Azure Cosmos DB, and a modern UI stack.

Features

  • Passwordless Auth — OTP + magic link login with session-based security
  • Internationalization — English and Dutch out of the box via next-intl
  • Azure Cosmos DB — Typed collections for users, sessions, and verification tokens
  • Dark Mode — Theme switching with next-themes
  • Modern UI — HeroUI components, Tailwind CSS v4, Framer Motion animations
  • Turbopack — Fast dev and build with Next.js Turbopack
  • Docker Ready — Multi-stage Dockerfile with standalone output

Tech Stack

Layer Technology
Framework Next.js 16 (App Router, React 19)
Language TypeScript (strict mode)
Styling Tailwind CSS v4, HeroUI, Framer Motion
Auth Passwordless OTP + magic links, session cookies
Database Azure Cosmos DB
Email Resend
i18n next-intl (en, nl)
Icons Lucide React, Iconify
Package Manager pnpm

Quick Start

Prerequisites

  • Node.js 20+
  • pnpm 10.22+
  • Azure Cosmos DB account (or emulator)
  • Resend account (for sending emails)

Environment Variables

Create a .env.local file in the project root:

LOG_LEVEL=DEBUG | INFO | WARN | ERROR

# Database
NEXT_PRIVATE_COSMOS_DB_URI=your-cosmos-db-uri
NEXT_PRIVATE_COSMOS_DB_KEY=your-cosmos-db-key
NEXT_PRIVATE_COSMOS_DB_NAME=your-database-name

# Email (Resend)
NEXT_PRIVATE_RESEND_API_KEY=your-resend-api-key
EMAIL_FROM=noreply@yourdomain.com

# App
NEXT_PUBLIC_APP_URL=http://localhost:3000

Install & Run

pnpm install
pnpm dev

Open http://localhost:3000.

Other Commands

pnpm build     # Production build with Turbopack
pnpm start     # Start production server
pnpm lint      # Run ESLint

Project Structure

src/
├── app/
│   ├── [locale]/           # i18n pages (en, nl)
│   │   ├── page.tsx        # Landing page
│   │   ├── landing-page.tsx
│   │   └── layout.tsx      # Root layout with providers
│   └── api/auth/           # Auth API routes
│       ├── send-otp/
│       ├── verify-otp/
│       ├── resend-otp/
│       ├── magic-link/
│       ├── session/
│       └── signout/
├── components/
│   ├── auth/               # LoginModal, UserDropdown, OTPInput
│   ├── ui/                 # LanguageSwitcher, LanguageModal
│   ├── navbar.tsx
│   ├── footer.tsx
│   ├── theme-switcher.tsx
│   └── providers.tsx       # Theme, HeroUI, Auth, i18n providers
├── contexts/
│   └── auth-context.tsx    # AuthProvider + useAuth() hook
├── i18n/
│   ├── routing.ts          # next-intl routing config
│   └── request.ts          # next-intl request config
├── lib/
│   ├── actions/            # Server actions (session, user, email)
│   ├── auth/               # Auth helpers (validateRequest, requireAuth)
│   ├── helper/             # Logger, rate limiting, request context
│   ├── db.ts               # Cosmos DB client + containers
│   └── utils.ts            # cn() utility
├── proxy.ts                # i18n locale detection proxy (Next.js 16)
├── styles/
│   └── globals.css         # Tailwind config, CSS utilities
└── types/                  # TypeScript type definitions
messages/
├── en.json                 # English translations
└── nl.json                 # Dutch translations

Auth Flow

  1. User enters email in the login modal
  2. Server generates a 6-digit OTP and magic link, sends via Resend
  3. User enters OTP or clicks the magic link
  4. Server verifies the token, creates/finds user, issues a session cookie
  5. Session token is SHA256-hashed, base32-encoded, stored as httpOnly secure cookie
  6. Sessions expire after 30 days with auto-refresh at the 50% mark
  7. Client polls /api/auth/session every 5 minutes to stay in sync

Internationalization

  • Locales: en (default, no URL prefix) and nl (prefixed /nl/)
  • Translation files in messages/en.json and messages/nl.json
  • Use routing helpers from @/i18n/routing (not next/navigation)
  • Proxy (src/proxy.ts) handles locale detection and routing

Setting Up Resend (Email)

Resend is used to send OTP codes and magic link emails.

  1. Create an account at resend.com
  2. Go to API Keys in the dashboard and click Create API Key
  3. Give it a name (e.g. acme-web), select Sending access, and choose your verified domain (or use the default onboarding@resend.dev for testing)
  4. Copy the generated key — this is your NEXT_PRIVATE_RESEND_API_KEY
  5. Go to Domains and add your domain (e.g. yourdomain.com). Follow the DNS verification steps (add the MX, SPF, and DKIM records Resend provides)
  6. Once verified, set EMAIL_FROM to your sender address (e.g. noreply@yourdomain.com)
NEXT_PRIVATE_RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxx
EMAIL_FROM=noreply@yourdomain.com

For local development, you can use onboarding@resend.dev as EMAIL_FROM without domain verification — emails will be sent to the Resend dashboard only.

Setting Up Azure Cosmos DB

Azure Cosmos DB is the database for users, sessions, and verification tokens.

Option A: Azure Portal

  1. Go to the Azure Portal and create a new Azure Cosmos DB for NoSQL account
  2. Once provisioned, go to Keys in the left sidebar
  3. Copy the URI and PRIMARY KEY — these are your NEXT_PRIVATE_COSMOS_DB_URI and NEXT_PRIVATE_COSMOS_DB_KEY
  4. Go to Data Explorer and create a new database (e.g. acme-db) — this is your NEXT_PRIVATE_COSMOS_DB_NAME
  5. Inside the database, create three containers with these exact names and partition keys:
Container Partition Key
users /email
sessions /userId
verificationTokens /userId
NEXT_PRIVATE_COSMOS_DB_URI=https://your-account.documents.azure.com:443/
NEXT_PRIVATE_COSMOS_DB_KEY=your-primary-key-here
NEXT_PRIVATE_COSMOS_DB_NAME=acme-db

Option B: Azure Cosmos DB Emulator (Local Development)

  1. Download and install the Azure Cosmos DB Emulator
  2. Start the emulator — it runs on https://localhost:8081 by default
  3. Open the emulator's Data Explorer at https://localhost:8081/_explorer/index.html
  4. The emulator uses a well-known key for local development:
NEXT_PRIVATE_COSMOS_DB_URI=https://localhost:8081
NEXT_PRIVATE_COSMOS_DB_KEY=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==
NEXT_PRIVATE_COSMOS_DB_NAME=acme-db
  1. Create the database and three containers (same names and partition keys as above) via the Data Explorer

Database Schema

Container Partition Key Purpose
users /email User accounts and profile metadata
sessions /userId Active auth sessions (30-day expiry)
verificationTokens /userId OTP codes and magic link tokens

Partition keys are critical for query performance. Lookups by id instead of the partition key require cross-partition queries, which are slower and more expensive.

Docker

docker compose up --build

Multi-stage build targeting Node 20-alpine with standalone output. Runs as non-root nextjs user on port 3000. Docker Compose allocates 4 CPU / 4-8 GB RAM.

Required environment variables must be set in the host environment or a .env file for Docker Compose to pass them to the container.

License

MIT

About

A production-ready Next.js 16 starter template with passwordless auth, i18n, Azure Cosmos DB, and HeroUI

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published