Stop wasting time on boilerplate. Start building features.
A battle-tested, feature-rich Nuxt 3 template that includes everything you need: modern design system, reusable UI components, i18n support, and CI/CD ready to deploy. Build beautiful, production-ready interfaces in minutes, not weeks.
Quick Start β’ Features β’ Components β’ Deploy
- Tailwind CSS 4.1.18 - Modern utility-first CSS with custom design tokens
- Custom Theme System - Pre-configured theme with CSS variables for easy customization
- Typography Utilities - 9 ready-to-use text styles (
ty-app-hero,ty-app-title, etc.) - Color Palette - Semantic color system (main, surface, accent, contrast, muted, error)
- Responsive Design - Mobile-first with smooth transitions on all breakpoints
- Dark Mode Ready - Theme structure prepared for dark mode implementation
- @nuxtjs/i18n 10.2.1 - Complete i18n solution
- English & Italian included (easily add more)
- URL Strategy -
prefix_except_default(e.g.,/pagefor EN,/it/pagefor IT) - Translation Files - JSON-based translations in
i18n/locales/
TheHeader- Responsive navigation with mobile drawerTheFooter- Multi-column footer with social linksTheNotificationBanner- Toast notifications (success, error, warning, info)BaseIconMenu- Floating dropdown menu with keyboard navigationBaseCloseButton- Accessible close button
Build modern interfaces in minutes. This template includes a complete library of production-ready, accessible, and fully customizable UI components designed to accelerate your development workflow.
Full-featured button component with multiple variants and states.
Variants: primary, secondary, outline
Types: button, submit, reset, link
States: loading, disabled
Features: Icon support, accessible, keyboard navigation
<BaseButton variant="primary" :is-loading="false">
<Icon name="lucide:send" class="size-5 mr-2" />
Send Message
</BaseButton>
<BaseButton variant="outline" type="link" to="/about">
Learn More
</BaseButton>Text input with label, hints, error states, and prefix icons.
Types: text, email, password, number
Features: Validation states, accessible, auto-focus, prefix icons
<BaseInput
id="email"
v-model:input="email"
type="email"
label="Email Address"
placeholder="user@example.com"
prefix-icon="lucide:mail"
hint="We'll never share your email"
:error="emailError"
/>Multi-line text input with character counter.
Features: Character limit, auto-resize, validation states
<BaseTextarea
id="message"
v-model:input="message"
label="Your Message"
:max-length="500"
:rows="4"
placeholder="Type your message..."
/>Custom checkbox with label slot.
Features: Custom styling, accessible, label slot
<BaseCheckbox id="terms" v-model:input="acceptTerms">
I agree to the terms and conditions
</BaseCheckbox>Advanced select component with single/multiple selection.
Types: single, multiple
Features: Searchable, keyboard navigation, floating UI, custom icons
<BaseCombobox
id="country"
v-model:input="selectedCountries"
type="multiple"
:items="countries"
label="Select Countries"
prefix-icon="lucide:globe"
placeholder="Choose countries..."
/>Compact label component for tags, badges, and status indicators.
Variants: accent, primary, secondary
Features: Icon support, rounded design, responsive sizing
<!-- Simple chip -->
<BaseChip text="Featured" variant="accent" />
<!-- Chip with icon -->
<BaseChip
icon="lucide:star"
text="Premium"
variant="accent"
/>
<!-- Use cases -->
<BaseChip icon="lucide:code" text="Vue.js" variant="accent" />
<BaseChip icon="lucide:check-circle" text="Active" variant="accent" />
<BaseChip icon="lucide:folder" text="Design" variant="primary" />Flexible card container with slots for header, body, and footer.
Variants: dark, light, dark-hover, light-hover
Alignment: left, center, right
Slots: card-header, card-body, card-footer, default
<BaseCard
title="Card Title"
subtitle="Card Subtitle"
paragraph="Card description text"
variant="dark-hover"
align="center"
>
<template #card-header>
<Icon name="lucide:star" class="size-6 text-app-accent" />
</template>
<template #card-footer>
<BaseButton variant="primary">Action</BaseButton>
</template>
</BaseCard>
<!-- Full custom content -->
<BaseCard :full-custom-content="true">
<div class="custom-layout">
<!-- Your custom content -->
</div>
</BaseCard>Modal dialog with size variants and custom slots.
Sizes: sm, md, lg, full
Features: Scroll lock, keyboard (ESC), accessible, click outside to close
<BaseDialog
:is-open="isDialogOpen"
size="md"
title="Dialog Title"
subtitle="Dialog subtitle"
@close="isDialogOpen = false"
>
<template #header>
<div class="custom-header">
<!-- Optional custom header content -->
</div>
</template>
<p>Dialog content goes here</p>
<template #footer>
<BaseButton variant="outline" @click="isDialogOpen = false">
Cancel
</BaseButton>
<BaseButton variant="primary" @click="handleSave">
Save
</BaseButton>
</template>
</BaseDialog>Dropdown menu with floating UI and keyboard navigation.
Features: Floating positioning, keyboard support, click outside to close
<BaseIconMenu
icon="lucide:menu"
:items="menuItems"
:selected-item-id="selectedId"
@select="handleSelect"
/>
<script setup>
const menuItems = [
{ code: 'profile', label: 'Profile', icon: 'lucide:user', iconType: 'nuxt-icon' },
{ code: 'settings', label: 'Settings', icon: 'lucide:settings', iconType: 'nuxt-icon' },
]
</script>Toast notification system with multiple types.
Types: success, error, warning, info
Features: Auto-dismiss, manual close, stacking
<script setup>
const { notify } = useAppNotifications()
notify({
type: 'success',
title: 'Success!',
description: 'Your changes have been saved.',
duration: 5000,
})
</script>β Accessible - ARIA attributes, keyboard navigation, focus management β Responsive - Mobile-first design with smooth transitions β Customizable - Full theme support with CSS variables β Type-Safe - Complete TypeScript support β Production-Ready - Battle-tested in real projects β i18n Ready - Internationalization support built-in
See all components in action: Run npm run dev and visit the homepage for a complete showcase.
- TypeScript - Full type safety
- ESLint 9.39.1 - Code quality with stylistic rules
- VueUse 14.1.0 - Vue composition utilities
- Floating UI 1.1.9 - Tooltips, popovers, dropdowns
- Auto-imports - Components and composables automatically imported
- Hot Module Replacement - Lightning-fast development
- @nuxt/image 2.0.0 - Automatic image optimization
- Multiple Formats - WebP, AVIF, PNG support
- Responsive Images - Automatic srcset generation
- Cloudinary Ready - Pre-configured provider
- @nuxt/icon 2.1.0 - SVG icon system with Lucide icon collection
- Icon Library - 1000+ Lucide icons ready to use
- GitHub Actions - Pre-configured workflows
- Automated Testing - Lint and build checks on PR
- Release Please - Automated versioning and changelog
- Netlify Preset - Optimized for Netlify deployment (easily switch to Vercel, Cloudflare, etc.)
- Node.js β₯ 24.11.0
- npm / pnpm / yarn
Option 1: Use as GitHub Template (Recommended)
- Click "Use this template" on GitHub
- Clone your new repository:
git clone https://github.com/your-username/your-project.git
cd your-projectOption 2: Clone Directly
git clone https://github.com/stefanoBid/sb-nuxt-template.git my-project
cd my-project
rm -rf .git && git init # Start fresh# Install dependencies
npm install
# (Optional) Reset version to 0.1.0
echo "# Changelog" > CHANGELOG.md
npm version 0.1.0 --no-git-tag-version
# Start development server
npm run devVisit http://localhost:3000 π
sb-template-nuxt/
βββ app/
β βββ assets/
β β βββ css/
β β βββ main.css # Main CSS entry point
β β βββ theme.css # CSS variables & design tokens
β β βββ typography.css # Typography utilities (ty-app-*)
β β βββ animations.css # Animation classes
β β βββ utilities.css # Custom utility classes
β β
β βββ components/
β β βββ base/
β β β βββ button/ # BaseButton.vue - Multi-variant button
β β β βββ card/ # BaseCard.vue - Flexible card container
β β β βββ checkbox/ # BaseCheckbox.vue - Custom checkbox
β β β βββ chip/ # BaseChip.vue - Tag/badge component
β β β βββ close-button/ # BaseCloseButton.vue - Accessible close button
β β β βββ combobox/ # BaseCombobox.vue - Select dropdown
β β β βββ dialog/ # BaseDialog.vue - Modal dialog
β β β βββ icon-menu/ # BaseIconMenu.vue - Dropdown menu
β β β βββ input/ # BaseInput.vue - Text input with validation
β β β βββ textarea/ # BaseTextarea.vue - Multi-line input
β β βββ the-footer/ # TheFooter.vue
β β βββ the-header/ # TheHeader.vue & TheHeaderMenuToggle.vue
β β βββ the-notification/ # TheNotificationBanner.vue & Box
β β
β βββ composables/
β β βββ useAppNotifications.ts # Notification system
β β βββ useFloatingUi.ts # Floating UI helper
β β βββ useLockScroll.ts # Scroll lock utility
β β βββ useSanitize.ts # HTML sanitization
β β
β βββ layouts/
β β βββ default.vue # Default page layout
β β
β βββ pages/
β β βββ index.vue # Homepage
β β
β βββ plugins/
β β βββ scrollToTop.client.ts # Auto-scroll on route change
β β
β βββ types/
β β βββ global.d.ts # Global TypeScript types
β β
β βββ utils/
β β βββ generateUuid.ts # UUID generator
β β
β βββ app.vue # Root component
β βββ error.vue # Error page
β
βββ i18n/
β βββ locales/
β βββ en.json # English translations
β βββ it.json # Italian translations
β
βββ public/
β βββ favicon.ico
β βββ robots.txt
β βββ sitemap.xml # SEO sitemap template
β
βββ nuxt.config.ts # Nuxt configuration
βββ tsconfig.json # TypeScript configuration
βββ eslint.config.mjs # ESLint configuration
βββ package.json
| Command | Description |
|---|---|
npm run dev |
Start development server at http://localhost:3000 |
npm run build |
Build for production (outputs to .output/) |
npm run generate |
Generate static site (SSG mode) |
npm run preview |
Preview production build locally |
npm run lint |
Check code quality with ESLint |
npm run lint:fix |
Auto-fix ESLint issues |
Edit app/assets/css/theme.css to change colors, fonts, and spacing:
@theme {
/* Fonts */
--font-app-primary: 'Your Font', system-ui, sans-serif;
--font-app-secondary: 'Your Mono Font', monospace;
/* Colors */
--color-app-main: #f8f9fa; /* Background */
--color-app-accent: #2563eb; /* Primary accent */
--color-app-contrast: #1a1a1a; /* Text color */
--color-app-error: #dc2626; /* Error state */
/* ...more colors */
}π‘ Tip: All CSS variables are prefixed with
app-to avoid conflicts.
Pre-configured typography classes in app/assets/css/typography.css:
| Class | Usage | Example |
|---|---|---|
ty-app-hero |
Hero text (60px) | Landing page headlines |
ty-app-title-xl |
Extra large title (48px) | Page titles |
ty-app-title-lg |
Large title (36px) | Section headers |
ty-app-title |
Regular title (24px) | Card headers |
ty-app-subtitle-lg |
Large subtitle (20px) | Subheadings |
ty-app-subtitle |
Regular subtitle (18px) | Small subheadings |
ty-app-paragraph |
Body text (16px) | Paragraph content |
ty-app-label |
Small label (14px) | Form labels, captions |
ty-app-btn-label |
Button text (16px) | Button labels |
Example:
<template>
<h1 class="ty-app-hero">Welcome to My App</h1>
<p class="ty-app-paragraph">This is a paragraph with proper styling.</p>
</template>Use pre-defined animation classes from app/assets/css/animations.css:
fade-in/fade-outslide-in-up/slide-in-downbounce-in
1. Register the locale in nuxt.config.ts:
i18n: {
locales: [
{ code: 'en', iso: 'en-US', name: 'English', file: 'en.json' },
{ code: 'it', iso: 'it-IT', name: 'Italiano', file: 'it.json' },
{ code: 'fr', iso: 'fr-FR', name: 'FranΓ§ais', file: 'fr.json' }, // β
New
],
}2. Create translation file i18n/locales/fr.json:
{
"welcome": "Bienvenue",
"nav": {
"home": "Accueil",
"about": "Γ propos"
}
}3. Use translations in components:
<template>
<h1>{{ $t('welcome') }}</h1>
<NuxtLink :to="localePath('/')">{{ $t('nav.home') }}</NuxtLink>
</template><script setup>
const { locale, setLocale } = useI18n()
function changeLanguage(lang: string) {
setLocale(lang)
}
</script>
<template>
<button @click="changeLanguage('it')">Italiano</button>
<button @click="changeLanguage('en')">English</button>
</template><script setup>
const { notify } = useAppNotifications()
function showSuccess() {
notify({
type: 'success',
title: 'Success!',
description: 'Your action was completed.',
duration: 5000, // Auto-dismiss after 5s
})
}
function showError() {
notify({
type: 'error',
title: 'Error!',
description: 'Something went wrong.',
duration: 0, // Manual dismiss only
})
}
</script>
<template>
<button @click="showSuccess">Show Success</button>
<button @click="showError">Show Error</button>
</template>Notification Types: success, error, warning, info
<template>
<BaseIconMenu>
<template #trigger="{ toggle }">
<button @click="toggle">
<Icon name="mdi:menu" />
</button>
</template>
<template #content>
<div class="p-4 bg-white rounded-lg shadow-lg">
<a href="/profile">Profile</a>
<a href="/settings">Settings</a>
<button @click="logout">Logout</button>
</div>
</template>
</BaseIconMenu>
</template>Manage toast notifications.
const { notify, removeNotification, notifications } = useAppNotifications()
// Show notification
notify({
type: 'success',
title: 'Title',
description: 'Description',
duration: 5000, // ms (0 = manual dismiss)
})
// Remove specific notification
removeNotification(id)Position floating elements (tooltips, dropdowns).
const reference = ref<HTMLElement>()
const floating = ref<HTMLElement>()
const { floatingStyles } = useFloatingUi(reference, floating, {
placement: 'bottom-start',
offset: 8,
})Lock/unlock body scroll (useful for modals).
const { lockScroll, unlockScroll } = useLockScroll()
// Lock scroll when modal opens
onMounted(() => lockScroll())
// Unlock when modal closes
onUnmounted(() => unlockScroll())Sanitize HTML to prevent XSS attacks.
const { sanitizeHtml } = useSanitize()
const cleanHtml = sanitizeHtml('<script>alert("xss")</script><p>Safe content</p>')
// Result: '<p>Safe content</p>'<template>
<!-- Local images (from public/) -->
<NuxtImg
src="/images/hero.jpg"
alt="Hero image"
width="800"
height="600"
format="webp"
loading="lazy"
/>
<!-- Cloudinary images -->
<NuxtImg
provider="cloudinary"
src="/sample.jpg"
alt="Sample"
width="400"
height="300"
/>
<!-- External images (requires domain whitelisting) -->
<NuxtImg
src="https://example.com/image.jpg"
alt="External"
width="600"
height="400"
/>
</template>Configure external domains in nuxt.config.ts:
image: {
domains: [
'https://api.example.com',
'https://cdn.yoursite.com',
],
}<template>
<!-- MDI icons -->
<Icon name="mdi:home" size="24" />
<Icon name="mdi:account-circle" size="32" color="blue" />
<!-- Flag icons -->
<Icon name="flagpack:it" size="24" />
<Icon name="flagpack:us" size="24" />
</template>Browse available icons:
Create a .env file in the project root:
# API Configuration
NUXT_PUBLIC_API_URL=https://api.yoursite.com
# Cloudinary
NUXT_PUBLIC_CLOUDINARY_BASE=https://res.cloudinary.com/your-cloud-name
# CMS (Strapi, etc.)
NUXT_PUBLIC_STRAPI_URL=https://cms.yoursite.com
# Analytics
NUXT_PUBLIC_GA_ID=G-XXXXXXXXXXAccess in your app:
const config = useRuntimeConfig()
const apiUrl = config.public.apiUrl
β οΈ Important: Only variables prefixed withNUXT_PUBLIC_are exposed to the client.
This template is pre-configured for Netlify deployment.
1. Build your app:
npm run build2. Deploy to Netlify:
- Connect your GitHub repository to Netlify
- Build command:
npm run build - Publish directory:
.output/public
3. Environment variables:
Add your .env variables in Netlify dashboard under Site settings β Environment variables.
1. Change preset in nuxt.config.ts:
nitro: {
preset: 'vercel',
}2. Deploy:
npm run build
vercel deploy1. Change preset:
nitro: {
preset: 'cloudflare-pages',
}2. Build and deploy:
npm run build
# Deploy .output/public to Cloudflare Pages1. Change preset:
nitro: {
preset: 'node-server',
}2. Build and run:
npm run build
node .output/server/index.mjsπ More deployment options: Nuxt Deployment Docs
This template includes GitHub Actions workflows for automated testing and releasing.
Triggers:
- Push to
main,develop,release/**branches - Pull requests to
main
Actions:
- β Checkout code
- β
Install dependencies (
npm ci) - β
Run linter (
npm run lint) - β
Build app (
npm run build)
Smart Skip: The workflow automatically skips heavy steps for Release Please PRs (only marks as passed).
Workflow file: .github/workflows/ci.yml
Trigger: Push to main branch
What it does:
- Analyzes conventional commits (e.g.,
feat:,fix:,chore:) - Creates/updates a Release PR with:
- Auto-generated changelog
- Version bump in
package.json
- When merged, creates a GitHub Release with tags
Commit Message Format:
# Features
git commit -m "feat: add dark mode support"
# Bug fixes
git commit -m "fix: resolve navigation issue on mobile"
# Breaking changes
git commit -m "feat!: redesign authentication system"
# Chores (no release)
git commit -m "chore: update dependencies"π‘ Tip: Use conventional commits to automate versioning!
Workflow file: .github/workflows/release-please.yml
If you want your project to start from version 0.1.0 instead of inheriting the template's version:
# Clear changelog
echo "# Changelog" > CHANGELOG.md
# Reset version
npm version 0.1.0 --no-git-tag-version
# Commit changes
git add .
git commit -m "chore: initialize project from template"This template is perfect for:
β Landing Pages - Fast, SEO-optimized, multi-language β Marketing Sites - With optimized images and analytics ready β Documentation Sites - Custom styling and navigation β SaaS Applications - Notification system and user dashboards β Portfolio Websites - Dynamic routing and image galleries β E-commerce Stores - Product pages with i18n support
- Components: Use
PascalCasefor component filenames - Composables: Prefix with
use(e.g.,useAuth.ts) - Utils: Pure functions in
app/utils/ - Types: Global types in
app/types/global.d.ts
- Follow ESLint rules (run
npm run lint:fixbefore committing) - Use TypeScript for type safety
- Prefer composition API over options API
- Use auto-imports instead of manual imports
- Lazy load components with
<ClientOnly>when not needed for SSR - Use
<NuxtImg>instead of<img>for automatic optimization - Prerender static pages in
nuxt.config.tsβrouteRules - Keep bundle size small (check with
npm run build -- --analyze)
Issue: Cannot find module 'isomorphic-dompurify'
Solution: This is expected during server builds. The module is excluded in nuxt.config.ts β nitro.externals.
Issue: Icons not showing
Solution: Make sure icon collections are installed: npm i @iconify-json/mdi @iconify-json/flagpack
Issue: i18n routes not working
Solution: Check nuxt.config.ts β i18n.strategy and ensure locale files exist in i18n/locales/
Issue: Images not loading from external domains
Solution: Add domain to nuxt.config.ts β image.domains
Contributions are welcome! Here's how you can help:
- Report bugs - Open an issue with details
- Suggest features - Share your ideas
- Submit PRs - Fork, create a branch, and submit a pull request
Development Setup:
git clone https://github.com/stefanoBid/sb-nuxt-template.git
cd sb-nuxt-template
npm install
npm run devThis project is open source and available under the MIT License.
Feel free to use it for personal or commercial projects.
Stefano Biddau
π§ Email: biddau.stefano99@gmail.com
π GitHub: @stefanoBid
π Website: stefanobiddau.com
Looking for more starter templates? Check out the SB Templates Project collection!
π Browse All Templates
Visit the website and navigate to the SB TEMPLATES PROJECT section to discover more production-ready templates for different frameworks and use cases.
If this template helped you build faster, give it a βοΈ on GitHub!
Your support motivates me to maintain and improve this project.
- Nuxt 3 Documentation
- Vue 3 Documentation
- Tailwind CSS Documentation
- VueUse Documentation
- Nuxt i18n Documentation
Happy coding! π
Built with β€οΈ by developers, for developers.
