This is a modern Svelte 5 template featuring Tailwind CSS, PostgreSQL, and robust authentication (username/password, TOTP, passkey). It includes a manual i18n implementation, database migration tooling, and a comprehensive set of pre-styled UI components based on shadcn-svelte.
Dockerfile and docker-compose.yaml for containerized development and deployment with PostgreSQL and Valkey.npx sk-template create
Follow the prompts to configure your project name, website name, and origin URL.
cd your-project-name
bun install
# Start database and cache (for local development)
docker compose -f docker-compose.dev.yaml up -d
bun run db:migrate
bun run dev
Your app is now running at http://localhost:5173.
sk-template/
├── src/
│ ├── routes/ # SvelteKit routes & pages
│ │ ├── auth/ # Authentication pages (login, signup, etc.)
│ │ ├── app/ # Protected app routes
│ │ ├── api/ # API endpoints
│ │ └── +layout.svelte # Root layout
│ │
│ ├── lib/
│ │ ├── components/ # Reusable UI components
│ │ │ ├── ui/ # shadcn-svelte components
│ │ │ ├── Toast/ # Toast notification system
│ │ │ └── ... # Other components
│ │ │
│ │ ├── server/ # Server-only code
│ │ │ ├── auth.ts # JWT & auth utilities
│ │ │ ├── db/ # Database access objects (DAOs)
│ │ │ ├── mail/ # Email sending
│ │ │ ├── oauth/ # OAuth/OIDC handling
│ │ │ ├── totp/ # TOTP 2FA logic
│ │ │ └── db.ts # Database pool
│ │ │
│ │ ├── i18n/ # Internationalization
│ │ │ ├── config.ts # i18n configuration
│ │ │ ├── messages/ # Translation files
│ │ │ └── i18n.svelte.ts # i18n class
│ │ │
│ │ ├── types/ # TypeScript type definitions
│ │ ├── utils/ # Utility functions
│ │ └── assets/ # Images
│ │
│ ├── app.css # Global styles
│ ├── app.d.ts # Global type declarations
│ └── hooks.server.ts # SvelteKit server hooks
│
├── scripts/ # Utility scripts
│ ├── db/ # Database scripts
│ │ ├── migrate.ts # Run migrations
│ │ ├── createMigration.ts # Create new migration
│ │ └── makeAdmin.ts # Promote user to admin
│ └── checkTranslations.ts # Validate i18n keys
│
├── sql/
│ ├── init.sql # Initial database schema
│ └── migrations/ # Migration files
│
├── config.json # App configuration
├── .env.example # Environment template
├── docker-compose.yaml # Production compose
├── docker-compose.dev.yaml # Development compose
└── package.json
{
"project_name": "my-app",
"website": {
"name": "My App",
"description": "My awesome app"
},
"origin": "http://localhost:5173"
}
Key settings:
project_name: Used in emails, OAuth, and TOTP setupwebsite.name: Displayed in UI and email templatesorigin: Your app's URL (used for OAuth redirects and security)loaders: Supported languages and text directionsThe template supports 4 authentication methods:
Files:
src/routes/auth/sign-up and src/routes/auth/log-inusers table (hashed with bcryptjs)How it works:
/appFiles:
Setup:
Login with TOTP:
speakeasy libraryFiles:
How it works:
Files:
Supports:
Configuration:
Set OAUTH_ENABLED=true in .env and provide OAuth provider details.
Token storage:
/)Protected routes:
All routes under /app require authentication. Check hooks.server.ts for the auth middleware.
Key tables:
bun run db:create-migration
Creates: sql/migrations/migration.<TIMESTAMP>.sql
-- Migration created at 2025-01-01T12:00:00Z
ALTER TABLE users ADD COLUMN last_login TIMESTAMP;
CREATE INDEX idx_users_last_login ON users(last_login);
bun run db:migrate
The system tracks applied migrations in the migrations table.
Data Access Objects handle all database queries. Examples:
UserDAO: User CRUD operationsPasskeyDAO: Passkey managementValkey: Cache operationsUsage example:
import { UserDAO } from '$lib/server/db/user';
// Get user by username
const user = await UserDAO.getUserByUsername('john_doe');
// Create user
await UserDAO.createUser({
username: 'jane_doe',
email: '[email protected]',
passwordHash: hashedPassword,
});
Database connection pooling is configured in index.ts using the pg library.
Create src/lib/i18n/messages/fr.json (copy from en.json and translate)
Edit config.ts:
export const config: Config<Translations> = {
defaultLocale: 'en',
loaders: [
{
locale: 'en',
loader: async () => (await import('./messages/en.json')).default,
},
{
locale: 'fr',
dir: 'ltr', // or 'rtl' for right-to-left languages
loader: async () => (await import('./messages/fr.json')).default,
},
],
};
bun run i18n:check
This ensures all keys are present in all language files.
In Svelte components:
<script lang="ts">
import i18n from '$lib/i18n';
</script>
<h1>{i18n.t('auth.logIn.title')}</h1><p>{i18n.t('auth.welcome', { name: 'John' })}</p>
Switching languages:
<button onclick={() => i18n.setLocale('fr')}>Français</button>
The locale is saved in a cookie and persists across sessions.
The template uses Tailwind CSS v4 with a custom theme. Theme variables are defined in CSS custom properties:
Main theme file: sleek-black.css
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
/* ... more variables ... */
}
Switching themes: Edit the import in app.css:
@import './css/sleek-black'; /* Change this */
Pre-styled components from shadcn-svelte are in ui.
Common components:
Usage:
<script lang="ts">
import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
</script>
<Card.Root>
<Card.Header>
<Card.Title>Hello</Card.Title>
</Card.Header>
<Card.Content>
<Button>Click me</Button>
</Card.Content>
</Card.Root>
Use the Toaster class for notifications:
import { Toaster } from '$lib/components/Toast/toast';
Toaster.success('Saved successfully!');
Toaster.error('Something went wrong');
Toaster.warning('Are you sure?');
Toaster.info('FYI...');
# Start dev server
bun run dev
# Build for production
bun run build
# Preview production build
bun run preview
# Format code
bun run format
# Lint & check code quality
bun run lint
# Type checking
bun run check
# Check translations for missing keys
bun run i18n:check
# Create database migration
bun run db:create-migration
# Run pending migrations
bun run db:migrate
# Promote user to admin
bun run db:make-admin <USERNAME>
Pre-commit hooks (via Husky):
Commits are blocked if:
docker build -t your-username/my-app:latest .
docker compose up -d
Services:
web: SvelteKit app (port 4173)db: PostgreSQL (port 5432)valkey: Valkey cache (port 6379)Update .env with production values:
# Use a strong JWT secret (32+ chars)
JWT_SECRET=your-production-secret-key-minimum-32-characters
# Database
POSTGRES_HOST=db # Use service name when in Docker
POSTGRES_USER=...
POSTGRES_PASSWORD=...
# Valkey
VALKEY_HOST=valkey
# Email
SMTP_HOST=your-smtp-server
SMTP_PORT=587
SMTP_USER=...
SMTP_PASSWORD=...
# OAuth (if enabled)
OAUTH_ENABLED=true
OAUTH_CLIENT_ID=...
# ... rest of OAuth config
docker psdocker logs dbbun run db:migrate shows full errorRun bun run i18n:check to see which translations are missing, then add them to all language files.
OAUTH_ENABLED=true in .envORIGIN matches OAuth redirect URIdocker logs valkey)Good luck!