- Node v18 or higher
-
Run
npm create vite@latest- Select a project name (eg:
vite-example) - Select a framework (
Vue) - Select a variant (
Javascript)
- Select a project name (eg:
-
Go to the project folder and install initial packages
cd [project-name](eg:vite-example)npm install
-
Customize your initial screen
- Rename
src/components/HelloWorldtosrc/components/Homeand customize contents. - Modify
src/App.vue- Import
Homeinstead ofHelloWorld - Change other component references from
HelloWorldtoHome
- Import
- Rename
-
Runing the dev server
- Run
npm run devto start the dev server - Go to
http://localhost:5173/to see your web app - Try changing something in
src/components/Hometo test HMR (Hot Module Reload). Any changes will be automatically applied to the web version. (Eg: change<code>components/HelloWorld.vue</code>to<code>components/Home.vue</code>)
- Run
Vue Router is the official client-side routing solution for Vue.
First, organize router views in a specific folder src/views
- Create
src/views/Home.vue
<template>
<h1>Home</h1>
</template>- Or move
src/components/Home.vuetosrc/views
Setup Vue-router
- Run
npm install vue-router - Create
src/router/index.js
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [{ path: '/', component: Home }]
const router = createRouter({
history: createWebHashHistory(),
routes,
})
export default router- Import router in
src/main.js
import router from './router'- Add
use(router)to the Vue app insrc/main.js
createApp(App)
.use(router)
.mount('#app')- Setup a
RouterViewin your main App containersrc/App.vueto render the view selected fromroutes.
<script setup>
import { RouterView } from 'vue-router';
</script>
<template>
<div>
<RouterView />
</div>
</template>You can use createWebHistory instead of createWebHashHistory to use regular URL paths to improve SEO if needed. However, tHis needs additional server configuration to make it work. See https://router.vuejs.org/guide/essentials/history-mode#Example-Server-Configurations for more details.
Pinia replaces Vuex as the recommended state management library.
- Run
npm install pinia - Create a store file in
src/stores/folder (eg:src/stores/counter.js)
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: {
increment() {
this.count++
},
},
})- Import
createPiniainsrc/main.js
import { createPinia } from 'pinia'- Add pinia to the vue app in
src/main.js
app
.use(createPinia())
...Import and declare the store.
import { useCounterStore } from '../stores/counter'
const counterStore = useCounterStore()You can use the store actions (eg: counterStore.increment()) and properties (eg: counterStore.count).
For example, you can use counter example in the Home view (from HelloWorld default component) to use the store instead of ref(value)
Remove count constant
// const count = ref(0)Replace button @click action (count++) and current count display ({ count }) with a call to increment() action and count store property.
<button type="button" @click="counterStore.increment()">count is {{ counterStore.count }}</button>You can also define custom getters in the store, to return specific information from the current state.
- Import sass as a dev dependency:
npm add -D sass - Now you can import
.scssfiles
import './styles.scss'- Or you can declare
type="sass"in style sections of components to use scss syntax:
<style lang="scss">
div {
color: blue;
&:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
}
</style>Import the svg logo in the <script> section
import vueLogo from '../assets/vue.svg'Include the logo in the html template
<p>
<img :src="vueLogo" class="vueLogo" alt="Vue logo" />
</p>Declare .vueLogo class and :hover state in the <style> section, using type="sass"
<style scoped lang="scss">
.vueLogo {
height: 4em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
&:hover {
filter: drop-shadow(0 0 1em #42b883aa);
}
}
</style>See https://tailwindcss.com/docs/installation/using-postcss
Install the dev dependencies
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest @tailwindcss/postcssCreate a basic /tailwind.config.js file for yourself:
export default {
purge: [],
darkMode: 'media', // or 'false' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}Create a basic /postcss.config.js file:
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}Modify src/style.css to import tailwindcss styles. Add this line to the start and remove any unused styles. The second line is to force dark mode when the dark class is present.
@import 'tailwindcss';
@custom-variant dark (&:where(.dark, .dark *));Modify src/views/Home.vue to use Tailwind CSS classes in elements. For example
text-centerfor centered textflex flex-col items-center justify-centerto center elements horizontally and verticallybg-white dark:bg-gray-900for light and dark mode backgroundtext-gray-900 dark:text-gray-400for light and dark mode text colorhover:underline text-blue-800for links
Allow the user or default browser setting to activate dark mode.
- Install plugin
npm install vue-plugin-darkmode - Import and use the plugin in
src/main.js
import darkMode from 'vue-plugin-darkmode'
createApp(App)
.use(darkMode)
.mount('#app');- Add to your view a button to call
$setDarkMode(boolean)to enable/disable dark mode:
<button
type="button"
@click="$setDarkMode(!$isDarkMode())"
class="mb-2 rounded-full bg-gray-400 px-6 py-2 text-black transition-shadow hover:shadow-lg"
>
Toggle Dark Mode
</button>- Install vue-i18n
npm install vue-i18n - Create a language file
src/lang/index.js
import { createI18n } from 'vue-i18n'
const messages = {
en: {
Hello: 'Hello'
},
es: {
Hello: 'Hola'
},
}
const i18n = createI18n({
locale: 'en',
fallbackLocale: 'en',
messages })
export default i18n- Import and use the plugin in
src/main.js
import i18n from './lang'
createApp(App)
.use(i18n)
.mount('#app');- Add entries in the component that will be automatically translated to the current locale using the
$t(key)method.
<h1>{{ $t('Hello')}}</h1>- You can set the current language using the exposed
$i18nobject:
$i18n.locale = 'es'`- For pluralization see: https://vue-i18n.intlify.dev/guide/essentials/pluralization.html
- For date/time formatting see: https://vue-i18n.intlify.dev/guide/essentials/datetime.html
- For number formatting: https://vue-i18n.intlify.dev/guide/essentials/number.html
- Install Axios library:
npm install axios - Import axios in your code and retrieve content:
import axios from 'axios'
axios.get('/api/data').then(response => {
console.log(response.data)
})Request and response example: getting current weather temperature.
- Add this
<script>section tosrc/views/Home.vue
<script>
export default {
data() {
return {
weatherData: null,
}
},
mounted() {
console.log('Home.vue mounted')
axios.get('https://api.open-meteo.com/v1/forecast?latitude=49.2497&longitude=-123.1193¤t=temperature_2m&timezone=America%2FLos_Angeles&forecast_days=1')
.then(response => {
console.log('Weather data:', response.data);
this.weatherData = response.data.current.temperature_2m + ' ' + response.data.current_units.temperature_2m;
})
.catch(error => {
console.error('Error fetching weather data:', error);
});
}
}
</script>- Then use
{{ weatherData }}in your content.
<p>Current temperature is {{ weatherData }}</p>More information about Axios in the official page: https://axios-http.com/docs/intro
Use Vue-Query along with Axios library to automate reactive fetch operations with cache and auto-refresh.
- Install:
npm install vue-query - Import and use in Vue App (
src/main.js)
import { VueQueryPlugin, QueryClient } from '@tanstack/vue-query'
const queryClient = new QueryClient()
createApp(App)
.use(VueQueryPlugin, { queryClient })
.mount('#app')- Import and call
useQuery()to create a managed query, and populate a local object.
import { useQuery } from '@tanstack/vue-query'
import { ref } from 'vue'
const currentItem = ref(null)
const itemId = ref(null)
const itemQuery = useQuery({
queryKey: ['item', itemId.value],
queryFn: (context) => axios.get('https//myitems/item/'+itemId.value).then(res => res.data),
select: (data) => {
console.log('Result data:', data)
currentItem.value = data
return data
},
})- Now you can:
- Change
itemIdvalue to request a different result. Results are hashed byqueryKey - Call to
itemQuery.refetch()to reresh the result - See https://tanstack.com/query/v4/docs/framework/vue/reference/useQuery for more useQuery parameters
- Change
VueUse is a pack of useful utilities to load into your Vue project. It includes: State, Elements, Browser, Sensors, Network, Animation, Component, Watch, Reactivity, Array, Time, Utilities. Also useful add-ons like: Electron, Firebase, Head, Integrations, Math, Motion, Router, RxJS, SchemaOrg, Sound.
Reference: https://vueuse.org/core/useInfiniteScroll/
Example:
- Define the load logic for
dataarray and limits
import { useInfiniteScroll } from '@vueuse/core'
import { ref, useTemplateRef } from 'vue'
const data = ref([])
useInfiniteScroll(
useTemplateRef('scrollContainer'),
() => {
data.value.push(...[1,2,3,4,5,6,7,8,9,10].map(i => i + data.value.length))
},
{
distance: 100, // distance in pixels from the bottom of the scroll container to trigger loading more data
canLoadMore: () => {
return data.value.length < 5000
},
}
)- Create the scroll container embedding the
datain a loop
<h3>Infinite Scroll Example</h3>
<div ref="scrollContainer" class="w-32 max-h-32 overflow-y-auto border rounded-lg bg-gray-100 p-2 ">
<div v-for="item in data">
{{ item }}
</div>
</div>Reference: https://vueuse.org/core/useMagicKeys/
Example:
- Define keys or combinations to watch
import { useMagicKeys } from '@vueuse/core'
const keys = useMagicKeys({ reactive: false })
const shiftCtrlA = keys['Shift+Ctrl+A']
// non reactive, use watches
watch(shiftCtrlA, (v) => {
if (v)
console.log('Shift + Ctrl + A have been pressed')
})- Use
{ reactive: true }to use it directly in your components:
<h3>Magic Keys Example</h3>
<div v-if="keys.down">Down is pressed</div>
<div v-if="keys.up">Up is pressed</div>You can edit vite.config.js to configure some options for compiling and building the project.
For example, use @/views/Home.vue instead of ../views/Home.vue
- Add the import of
fileURLToPathandURL:
import { fileURLToPath, URL } from 'node:url'- Define a resolve alias entry:
export default defineConfig({
// ...
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
})- Then you can use
@instead of a relative path in imports:
import Home from '@/views/Home.vue'This will allow to build the app to be served in a subfolder. All the content is loaded relative to that folder, instead of a fixed root path (/).
- Define the
basepath, use'./'or''
export default defineConfig({
base: './',
// ...
})vite-plugin-pwa
Plugin to setup a PWA (Progressive Web Application). A PWA consists of a web application manifest to give the browser information about your app, and a service worker to manage the offline experience. More info on PWAs here: https://web.dev/learn/pwa/
- Install as a dev package:
npm install -D vite-plugin-pwa - Modify
vite.config.jsto add it to thepluginssection:
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate',
})
],
})vue3-lottie
Lottie animation support for Vue 3 [https://app.lottiefiles.com/]
- Install calling
npm install vue-lottie - Create and download a Lottie json animation
- Import library and .json object:
import { Vue3Lottie } from 'vue3-lottie'
import myAnimation from '../lotties/animation1.json'- Include the element in your component
<Vue3Lottie :width="200" :height="200" :animationData="myAnimation" />vuelidate
Model-based validation library for Vue 3
- Install calling
npm install @vuelidate/core @vuelidate/validators - Use Vuelidate, create rules, personalize messages, and validate against rules
import useVuelidate from '@vuelidate/core'
import { required, minLength } from '@vuelidate/validators'
import { reactive } from 'vue'
import { helpers } from '@vuelidate/validators'
const requiredName = helpers.withMessage('Name cannot be empty', required)
const minLengthName = helpers.withMessage('Name must be at least 2 characters long', minLength(2))
const formData = reactive({ name: '' })
const rules = { name: { requiredName, minLengthName } }
const v$ = useVuelidate(rules, formData)- Validate dynamically while data changes
<div class="flex flex-col items-center" >
<input v-model="formData.name" type="text" placeholder="Enter your name" />
<p class="text-red-500" v-if="v$.name.$invalid">
{{ v$.name.$silentErrors ? v$.name.$silentErrors[0].$message : 'Invalid' }}
</p>
<p v-else class="text-green-700">
Name is Valid
</p>
</div>@unhead/vue
Manage <head> content like title, meta and SEO tags.
- Install using
npm install @unhead/vue - Import and use in app
src/main.js:
import { createHead } from '@unhead/vue/client'
createApp(App)
.use(createHead())
.mount('#app')- Call to useHead() and/or useSeoMeta() on your vue App (eg:
src/App.vue)
import { useHead, useSeoMeta } from '@unhead/vue'
useHead({
title: 'Vue 3 + Vite + Libraries Starter',
meta: [
{ name: 'description', content: 'A starter template for Vue 3 with Vite and commonly used libraries.' },
],
})
useSeoMeta({
title: 'Vue 3 + Vite + Libraries Starter',
description: 'A starter template for Vue 3 with Vite and commonly used libraries.',
ogDescription: 'A starter template for Vue 3 with Vite and commonly used libraries.',
ogTitle: 'Vue 3 + Vite + Libraries Starter',
ogImage: 'https://example.com/image.png',
})This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 <script setup> SFCs, check out the script setup docs to learn more.
Learn more about IDE Support for Vue in the Vue Docs Scaling up Guide.