A modern Vue 3 starter template with TypeScript
- 🚀 Vue 3 with TypeScript
- 🎯 VueUse collection of Vue Composition Utilities
- ⚡️ Vite for fast development and building
- 🔄 Pinia for state management
- 🌐 Vue Router with auto-generated routes
- 🌍 Vue i18n for internationalization
- 🎨 Tailwind CSS for styling
- 💨 Winduum utility-first CSS framework
- ✨ Reka UI Vue 3 component library
- 🧪 ESLint and Stylelint for code linting
- 📊 Sentry integration for error tracking
- 🔌 Hey API Fetch client for type-safe API calls
- 🔄 Auto-generated SDK and Pinia Colada query/mutation options
- 🔒 HTTPS support for local development
- Node.js >=22.18.0
- npm >=11.0.0
npm installConfigure the project by editing the .env file:
VITE_BASE_URL=/ # Base URL for the application
VITE_SENTRY_DSN= # Sentry DSN for error tracking
VITE_API_URL= # API base URL; schema is read from /open-api-spec/
Hey API regenerates src/client during dev and build. Run npm run openapi to regenerate it explicitly.
Keep Pinia Colada queries in src/queries and mutations in src/mutations. Components should only import the exported composables.
// src/queries/countries.ts
import { defineQuery } from '@pinia/colada'
import { getCountriesQuery } from '@/client/@pinia/colada.gen'
import { staleTimes } from '@/utils/staleTimes'
export const useCountriesQuery = defineQuery({
...getCountriesQuery({}),
refetchOnWindowFocus: false,
staleTime: staleTimes.never,
})import { useCountriesQuery } from '@/queries/countries'
const { data: countries, isPending } = useCountriesQuery()// src/queries/countries-search.ts
import { useQuery } from '@pinia/colada'
import { toValue, type MaybeRefOrGetter } from 'vue'
import { searchCountriesQuery } from '@/client/@pinia/colada.gen'
import { staleTimes } from '@/utils/staleTimes'
type SearchCountriesRequest = Parameters<typeof searchCountriesQuery>[0]
export const useCountriesSearchQuery = (
request: MaybeRefOrGetter<SearchCountriesRequest>,
) => useQuery(() => ({
...searchCountriesQuery(toValue(request)),
refetchOnWindowFocus: true,
staleTime: staleTimes.default,
}))import { useCountriesSearchQuery } from '@/queries/countries-search'
const { data: countries, isPending } = useCountriesSearchQuery(() => ({
query: {
page: page.value,
search: search.value || undefined,
},
}))Use useMutation() for isolated state per caller. Use defineMutation() only when the mutation state should be shared.
// src/mutations/todos-create.ts
import { defineMutation, useMutation } from '@pinia/colada'
import { createTodoMutation } from '@/client/@pinia/colada.gen'
export const useCreateTodoMutation = () => useMutation(createTodoMutation())
export const useSharedCreateTodoMutation = defineMutation(createTodoMutation())import { useCreateTodoMutation } from '@/mutations/todos-create'
const { mutate: createTodo, isLoading } = useCreateTodoMutation()
createTodo({
body: {
text: 'Learn Pinia Colada',
},
})To mount the app as a custom HTML element, enable customElement: true in your vite.config.ts. When using this setup, use the index.ce.html template instead of the default index.html.
VITE_NAME=x-app # Your app name, custom element in index.ce.html
# Start development server
npm run dev
# Build for production
npm run build
# Preview production build
npm run preview
# Lint code
npm run lint
# Run ESLint
npm run eslint
# Fix ESLint issues
npm run eslint-fix
# Run Stylelint
npm run stylelint
# Fix Stylelint issues
npm run stylelint-fix
# Type check
npm run tsc
# Regenerate the API client
npm run openapistart/
├── openapi-ts.config.ts # Hey API code generation
├── public/ # Static assets
├── src/
│ ├── assets/ # Static assets
│ ├── client/ # Generated Fetch client, SDK and Colada options
│ ├── app.api.ts # Runtime API URL and headers
│ ├── components/ # Vue components
│ ├── composables/ # Vue composables
│ ├── layouts/ # Vue layouts
│ ├── locales/ # i18n translation files
│ ├── middleware/ # Middleware for vue-router
│ ├── mutations/ # Mutations for @pinia/colada
│ ├── pages/ # Pages for vue-router via unplugin-vue-router
│ ├── queries/ # Queries for @pinia/colada
│ ├── stores/ # Pinia state stores
│ ├── styles/ # CSS styles
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions
│ ├── app.css # Main CSS file
│ ├── app.ts # Main application entry
│ ├── app.vue # Root Vue component
│ └── index.html # HTML template
├── .env # Environment variables
├── .env.local # Environment variables (local)
├── .env.production # Environment variables (production)
├── .stylelintrc # Stylelint configuration
├── eslint.config.ts # Eslint configuration
├── package.json # Project dependencies and scripts
├── tsconfig.json # TypeScript configuration
└── vite.config.ts # Vite configuration