A comprehensive Nuxt 3 template with modern development tools, state management, database integration, and TypeScript support. This template provides everything you need to build scalable, production-ready web applications.
- Nuxt 3.17.4 - Full-stack Vue.js framework with SSR, auto-imports, and file-based routing
- Drizzle ORM 0.44.2 - TypeScript ORM with SQL-like syntax and excellent TypeScript support
- Drizzle Kit 0.31.1 - CLI companion for schema management, migrations, and introspection
- PostgreSQL - Robust, open-source relational database
- @supabase/supabase-js 2.50.0 - Supabase client for authentication, real-time, and storage
- shadcn-nuxt 2.2.0 - Vue port of shadcn/ui components with Reka UI and Tailwind CSS
- Tailwind CSS 4.1.8 - Utility-first CSS framework for rapid UI development
- @tailwindcss/vite 4.1.8 - Official Tailwind CSS integration for Vite
- @nuxtjs/color-mode 3.5.2 - Dark/light mode with auto detection and system preference support
- @nuxt/icon 1.13.0 - Access to 200,000+ icons from Iconify with SSR support
- @iconify-json/lucide - Beautiful & consistent icon toolkit
- @iconify-json/simple-icons - Popular brand icons
- Pinia 3.0.3 - TypeScript-first Vue state management library
- @pinia/nuxt 0.11.1 - Official Nuxt integration for Pinia
- Better Auth 1.2.x - Email/password, email verification, password reset, Google OAuth
- Middleware:
auth,admin, optionalemail-verified - SSR-friendly session refresh with cookie forwarding
- Zod 3.25.53 - TypeScript-first schema validation with static type inference
- TypeScript 5.8.3 - Static type checking and enhanced IDE support
- @antfu/eslint-config 4.13.3 - Opinionated ESLint preset with auto-fix formatting
- @nuxt/eslint 1.4.1 - Official Nuxt ESLint integration
- ESLint 9.28.0 - Pluggable linting utility for JavaScript and TypeScript
- pnpm - Fast, disk space efficient package manager
- Hot Module Replacement - Instant updates during development
- Auto-imports - Automatic imports for Vue composables, utilities, and shadcn-vue components
- File-based routing - Automatic route generation from file structure
nuxt-starter-template/
βββ components/
β βββ ui/ # shadcn-vue components (auto-generated)
βββ assets/
β βββ css/ # Global styles and Tailwind CSS
βββ pages/ # File-based routing pages
βββ composables/ # Vue composables (auto-imported)
βββ layouts/ # Page layouts
βββ middleware/ # Route middleware
βββ plugins/ # Nuxt plugins
βββ lib/
β βββ db/ # Database configuration and schema
β β βββ migrations/ # Database migration files
β β βββ schema/ # Drizzle schema definitions
β βββ env/ # Environment variable validation
β βββ types/ # TypeScript type definitions
β βββ utils.ts # Utility functions
βββ scripts/
β βββ db/ # Database seeding and utility scripts
βββ server/ # Server-side API routes
β βββ api/ # API endpoints
βββ public/ # Static assets
βββ utils/ # Utility functions (auto-imported)
βββ .cursor/rules/ # Cursor AI rules for development
βββ nuxt.config.ts # Nuxt configuration
βββ drizzle.config.ts # Drizzle ORM configuration
βββ package.json # Dependencies and scripts
βββ tsconfig.json # TypeScript configuration
βββ eslint.config.ts # ESLint configuration
Make sure to install the dependencies:
# pnpm (recommended)
pnpm install
# npm
npm install
# yarn
yarn install
# bun
bun install-
Set up your database (PostgreSQL or Supabase)
-
Configure environment variables in
.env:DATABASE_URL="your-postgresql-connection-string" # For Supabase (optional) SUPABASE_URL="your-supabase-url" SUPABASE_ANON_KEY="your-supabase-anon-key" SUPABASE_SERVICE_ROLE_KEY="your-supabase-service-key"
-
Generate and run migrations:
# Generate migration files from schema changes pnpm run db:generate # Apply migrations to database pnpm run db:migrate # Or push schema directly (development) pnpm run db:push
-
Seed the database (optional):
# Seed with sample data pnpm run db:seed # Seed with sample users pnpm run db:seed:users
Start the development server on http://localhost:3000:
# pnpm (recommended)
pnpm run dev# Development
pnpm run dev # Start development server
# Building
pnpm run build # Build for production
pnpm run generate # Generate static site
pnpm run preview # Preview production build
# Database
pnpm run db:generate # Generate migration files from schema
pnpm run db:migrate # Run database migrations
pnpm run db:push # Push schema to database (development)
pnpm run db:studio # Open Drizzle Studio (database GUI)
pnpm run db:seed # Seed database with sample data
pnpm run db:seed:users # Seed database with sample users
pnpm run db:delete-all # Delete all data from database
# Code Quality
pnpm run lint # Run ESLint
pnpm run lint:fix # Run ESLint with auto-fix
pnpm run typecheck # Run TypeScript type checking
pnpm run check # Run both linting and type checking
# Setup
pnpm run postinstall # Prepare Nuxt (auto-run after install)This template includes a global Toaster using vue-sonner, styled via shadcn-vue.
- Placement: top-center, offset under the sticky navbar. One toast at a time.
- Progress: custom CSS progress bar driven by
--sonner-duration. - Accessibility: pointer events enabled to remain interactive over dialogs.
Usage in components:
// composables/useToasts.ts
const { info, success, warning, error, promise } = useToasts();
info("Heads up!", "Informational toast");Demo buttons are available on the home page (pages/index.vue).
Server (Pino):
// Inside a server handler
export default defineEventHandler((event) => {
event.context.logger.info({ msg: "users:list" });
});Client (debug):
import { createClientLogger } from "~/lib/logger/client";
const log = createClientLogger("feature:example");
log("clicked with payload %o", payload);Environment:
# .env
LOG_LEVEL=info
LOG_PRETTY=true # pretty logs in dev; set false in prod
LOG_REQUESTS=true # request start/finish logs
LOG_REDACT=["headers.authorization","headers.cookie","body.password","body.token"]
NUXT_PUBLIC_DEBUG=app:*,feature:*Notes:
- Request correlation via
x-request-id(echoed in response headers) - Toggle client logs at runtime:
localStorage.debug = 'app:*,feature:*'; location.reload()
This template ships with Better Auth fully wired-up for email/password, email verification, password reset, and Google OAuth, with SSR-friendly session handling.
- Email/password auth with email verification and password reset
- Social login: Google
- SSR-safe session refresh and cookie forwarding
- Route protection via middleware:
auth,admin, optionalemail-verified
- Login/Signup/Logout: handled via
authClientin UI forms anduseAuth()composable - Authenticated routes: add page meta
// pages/any-protected-page.vue
definePageMeta({ middleware: "auth" });
// Admin-only route
definePageMeta({ middleware: "admin" });
// Require verified email (optional)
definePageMeta({ middleware: ["auth", "email-verified"] });- Session refresh (SSR-friendly):
useAuth().refresh()forwards cookies on server
// useAuth() excerpt showing SSR cookie forwarding for session
// Server: forwards incoming Cookie header to /api/auth/session
// Client: uses credentials: 'include'- Better Auth handler:
server/api/auth/[...all].tsβ proxies all Better Auth endpoints under/api/auth/* - Session endpoint:
server/api/auth/session.get.tsβ returns{ user, success }, augmentsuser.admin
Set these in .env (see .env.example for placeholders):
- Core:
NODE_ENV,DATABASE_URL - Better Auth:
BETTER_AUTH_SECRET,BETTER_AUTH_URL - OAuth (Google):
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET - SMTP (Ethereal/dev):
SMTP_HOST,SMTP_PORT,SMTP_SECURE,SMTP_USER,SMTP_PASSWORD,SMTP_FROM_NAME,SMTP_FROM_EMAIL
Production guidance:
- Set
BETTER_AUTH_URLto your canonical HTTPS domain - Review secure cookie options in Better Auth config before deploying
- Seed users:
pnpm db:seed:users - Test credentials: see
scripts/docs/test-credentials.md - Admin:
admin@example.com/admin123
Build the application for production:
# pnpm (recommended)
pnpm run buildLocally preview production build:
# pnpm (recommended)
pnpm run preview- Nuxt 3 Documentation - Learn about Nuxt features and API
- Better Auth Documentation - Authentication library used in this template
- Drizzle ORM Documentation - TypeScript ORM for SQL databases
- Drizzle Kit Documentation - Migration and schema management tools
- Supabase Documentation - Open source Firebase alternative
- shadcn-vue Documentation - Vue port of shadcn/ui component library
- shadcn-vue Components - Browse all available components
- shadcn-vue CLI - Command line tools for component management
- Tailwind CSS Documentation - Utility-first CSS framework guide
- Pinia Documentation - State management patterns and best practices
- Zod Documentation - Schema validation and type inference
- Deployment Guide - Deploy your Nuxt application
- Nodemailer - Email sending library used for dev SMTP
- Ethereal Email - Fake SMTP service for testing emails
- Google OAuth 2.0 - OAuth flow used for Google sign-in
- Clone this template and install dependencies with
pnpm install - Set up your database and configure environment variables
- Run database migrations with
pnpm run db:pushorpnpm run db:migrate - Start development server with
pnpm run dev - Add shadcn-vue components with
pnpm dlx shadcn-vue@latest add [component-name] - Explore the
/pagesdirectory to add your routes - Check
/lib/db/schemafor database schema definitions - Add icons using the Nuxt Icon module
- Manage state with Pinia stores
- Validate data using Zod schemas
# Initialize shadcn-vue (if not already done)
pnpm dlx shadcn-vue@latest init
# Add specific components
pnpm dlx shadcn-vue@latest add button
pnpm dlx shadcn-vue@latest add card dialog
# View all available components
pnpm dlx shadcn-vue@latest addComponents are automatically imported in your Vue files:
<template>
<div>
<Button variant="default">
Click me
</Button>
<Card>
<CardHeader>
<CardTitle>Hello World</CardTitle>
</CardHeader>
<CardContent>
<p>Your content here</p>
</CardContent>
</Card>
</div>
</template>Define your schema in lib/db/schema/:
import { createId } from "@paralleldrive/cuid2";
// lib/db/schema/users.ts
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: text("id").primaryKey().$defaultFn(() => createId()),
name: text("name").notNull(),
email: text("email").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});Use in your API routes:
// server/api/users.get.ts
import { db } from "~/lib/db";
import { users } from "~/lib/db/schema";
export default defineEventHandler(async (event) => {
const allUsers = await db.select().from(users);
return allUsers;
});Happy coding! π