This episode guides you through creating a real Vue project with Vite, understanding the src/components and src/views folder structure, making the most of the dev server with hot module replacement, and setting up ESLint, Prettier, and lint-staged for a tidy development workflow.

The first two episodes built up conceptual understanding. Now it's time to get your hands dirty: creating a real Vue project that will serve as the foundation for hands-on practice throughout the series. In episode 3 we'll cover how to start a project with Vite, understand the generated folder structure, take advantage of the dev server with hot module replacement, and then install ESLint, Prettier, and lint-staged.
Vite is more than just scaffolding — it's the modern build tool that has become Vue's official standard. It brings a very fast dev server with native ES modules and no bundling during development, plus an optimized production build powered by Rollup. Every subsequent coding episode will run on top of the project you create here.
Use create-vue, Vue's official scaffolder that produces a complete Vite project with options for ESLint, TypeScript, and Router:
npm create vue@latest belajar-vue
cd belajar-vue
npm install
npm run devAnswer the interactive prompts according to your needs. For this series, enable TypeScript, Vue Router, and Pinia — we'll use all of them in later episodes. After npm run dev, open http://localhost:5173 and you'll see the Vue welcome page with its signature green logo.
The generated project has an organized structure:
belajar-vue/
├── index.html
├── vite.config.ts
├── package.json
└── src/
├── main.ts
├── App.vue
├── assets/
├── components/
├── views/
└── router/src/main.ts is the entry point that creates the application instance, src/App.vue is the root component, and src/components holds reusable components.
A common convention in the Vue ecosystem: src/components holds small reusable components (buttons, cards, form inputs), while src/views holds page-level components that represent a single route.
src/components/AppButton.vue
src/components/ProductCard.vue
src/views/HomeView.vue
src/views/ProductListView.vueThis distinction isn't a hard rule, but a convention that helps things scale: views assemble the layout and page data, while components hide presentation details. We'll cover deeper architecture details in episode 18.
Create a simple component and use it in App.vue:
<script setup>
defineProps({ judul: String, deskripsi: String });
</script>
<template>
<article class="card">
<h2>{{ judul }}</h2>
<p>{{ deskripsi }}</p>
</article>
</template>
<style scoped>
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
}
</style>Save it as src/components/InfoCard.vue and import it in App.vue. Notice the auto-import: Volar detects components used in templates without an explicit declaration.
Vite takes advantage of native ES modules in the browser. When you open the application, Vite only serves the modules that are actually requested and transforms them on the fly with esbuild. There's no full bundling during development, so the server starts almost instantly and file changes are reflected in milliseconds.
When you save a change, HMR swaps out the changed part without refreshing the page and without losing application state. Try changing text in a component — you'll see the update instantly. This is far more pleasant than manual build-and-refresh cycles.
npm run dev -- --port 4000The --port flag in npm run dev -- --port 4000 forces the dev server to run on port 4000. In production, you can add the VITE_PORT environment variable for more flexible configuration.
Code quality is guarded by three tools: ESLint catches errors and anti-patterns, Prettier enforces consistent formatting, and lint-staged runs both only on the files that changed before committing:
npm install -D eslint prettier lint-staged
npm install -D eslint-plugin-vueFor projects created from create-vue, the ESLint configuration is already available. Make sure the lint script exists in package.json, then add a pre-commit hook through husky:
npx husky init
echo "bunx lint-staged" > .husky/pre-commitDefine lint-staged in package.json so formatting and linting run before each commit:
{
"lint-staged": {
"*.{vue,ts,tsx,js}": ["eslint --fix", "prettier --write"]
}
}With the *.{vue,ts,tsx,js} configuration in lint-staged, every staged .vue or .ts file is automatically fixed by ESLint and then formatted by Prettier before a commit succeeds. This keeps messy code out of Git history.
Warning
Run npm run lint before pushing. The lint-staged hook only checks files changed in a new commit; old files with issues still need to be checked periodically.
Episode 3 took you from zero to a running Vue project: scaffolding with Vite, the src/components and src/views folder structure, a dev server with instant hot module replacement, and a code quality foundation made of ESLint, Prettier, and lint-staged.
Key takeaways:
npm create vue@latest is the official way to start a Vue project.src/components for small units; src/views for route pages.In the next episode 4, we'll dive into template and rendering — Vue template syntax, the v-bind, v-if, and v-for directives, event handling with v-on, conditional and list rendering, plus template refs and dynamic attributes that form the everyday language of writing Vue UIs.