Chapter 20: Vue First SFC Page
Step 1 – What is a “First SFC Page” in Vue 3?
In beginner tutorials you often see only App.vue — that’s the root component.
But in real applications (even medium-sized ones), the structure looks like this:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
src/ ├── App.vue ← root wrapper (usually just contains <router-view>) ├── main.ts ← entry point ├── router/ │ └── index.ts ← where routes are defined └── views/ ← ←← this folder is very important ├── HomeView.vue ← your first real “page” ├── AboutView.vue └── DashboardView.vue |
HomeView.vue (or sometimes just Home.vue) is what most people mean by “first SFC page”.
It is:
- A full-screen component
- Mapped to the / route (home page)
- Usually contains the main landing content / hero section / welcome message
Step 2 – Create a Fresh Vite + Vue + TypeScript Project
If you haven’t already, run this in your terminal (takes ~20 seconds):
|
0 1 2 3 4 5 6 7 8 9 |
npm create vite@latest first-vue-page -- --template vue-ts cd first-vue-page npm install npm run dev |
You now have a running app at http://localhost:5173
Step 3 – Your Very First SFC Page → HomeView.vue
-
Create the folder & file
text0123456src/views/HomeView.vue -
Paste this clean, modern, 2026-style code:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
<!-- src/views/HomeView.vue --> <template> <div class="home-page"> <header class="hero"> <h1>Welcome to My First Vue App</h1> <p class="subtitle"> Built with Vue 3 • Vite • Composition API • TypeScript </p> <div class="cta"> <button class="primary" @click="startJourney"> Get Started {{ count > 0 ? `({count})` : '' }} </button> <router-link to="/about" class="secondary"> Learn More → </router-link> </div> </header> <section class="features"> <h2>Why You'll Love Vue</h2> <div class="grid"> <div class="card"> <h3>Simple & Familiar</h3> <p>Feels like enhanced HTML + JS — no huge learning curve</p> </div> <div class="card"> <h3>Lightning Fast</h3> <p>~20 KB runtime + Vite = instant dev & tiny production builds</p> </div> <div class="card"> <h3>Scales Beautifully</h3> <p>From small widget → full SPA → SSR with Nuxt</p> </div> </div> </section> <footer> <p>© {{ currentYear }} • Made with ❤️ in Hyderabad</p> </footer> </div> </template> <script setup lang="ts"> import { ref, computed } from 'vue' import { useRouter } from 'vue-router' // Reactive state const count = ref(0) const currentYear = computed(() => new Date().getFullYear()) // Router access (we'll set up router next) const router = useRouter() // Methods function startJourney() { count.value++ if (count.value >= 3) { alert('You really want to start! 🚀') router.push('/dashboard') // example navigation } } </script> <style scoped> .hero { text-align: center; padding: 6rem 2rem 4rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 0 0 2rem 2rem; } h1 { font-size: 3.8rem; margin-bottom: 1rem; line-height: 1.1; } .subtitle { font-size: 1.5rem; opacity: 0.9; max-width: 600px; margin: 0 auto 2.5rem; } .cta { display: flex; gap: 1.5rem; justify-content: center; flex-wrap: wrap; } .primary { background: white; color: #4f46e5; padding: 1rem 2.5rem; border-radius: 9999px; font-weight: 600; font-size: 1.1rem; border: none; cursor: pointer; transition: all 0.2s; } .primary:hover { transform: translateY(-2px); box-shadow: 0 10px 25px rgba(0,0,0,0.15); } .secondary { background: transparent; border: 2px solid white; color: white; padding: 1rem 2.5rem; border-radius: 9999px; font-weight: 600; text-decoration: none; font-size: 1.1rem; transition: all 0.2s; } .secondary:hover { background: rgba(255,255,255,0.15); } .features { padding: 5rem 2rem; text-align: center; max-width: 1200px; margin: 0 auto; } .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 2rem; margin-top: 3rem; } .card { background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.08); transition: transform 0.2s; } .card:hover { transform: translateY(-8px); } footer { text-align: center; padding: 2rem; color: #666; border-top: 1px solid #eee; margin-top: 4rem; } </style> |
Step 4 – Connect It to Routing (Very Important!)
-
Install vue-router if not already there
Bash0123456npm install vue-router@4 -
Create src/router/index.ts
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// src/router/index.ts import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', name: 'home', component: () => import('@/views/HomeView.vue') // lazy-loaded }, { path: '/about', name: 'about', // example placeholder route component: () => import('@/views/AboutView.vue') } // add more routes later... ] }) export default router |
- Update src/main.ts
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
// src/main.ts import { createApp } from 'vue' import App from './App.vue' import router from './router' // ← connect router const app = createApp(App) app.use(router) // ← tell Vue to use it app.mount('#app') |
- Make sure App.vue has <router-view>
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!-- src/App.vue --> <template> <router-view /> <!-- ← this is where pages appear --> </template> <script setup lang="ts"> // usually empty or global layout wrapper </script> <style> /* global reset or theme if you want */ * { margin:0; padding:0; box-sizing:border-box; } body { font-family: system-ui, sans-serif; line-height: 1.5; } </style> |
Now run npm run dev → visit http://localhost:5173 You should see your beautiful HomeView page!
Summary – What You Just Built
You created your first real SFC page:
- Lives in views/ folder
- Uses modern <script setup lang=”ts”>
- Has reactive state (ref, computed)
- Uses router navigation (useRouter, <router-link>)
- Has scoped styles
- Feels like a proper landing page
This pattern is used in 99% of medium+ Vue 3 projects in 2026.
Next natural steps after this:
- Create AboutView.vue and add /about route
- Add a simple Pinia store for global user state
- Build a todo list or dashboard inside a new view
Any part you want me to explain more slowly?
- Why lazy-load components with () => import()?
- How to create AboutView right now?
- Add dark mode toggle on this home page?
- Folder structure debate (views vs pages)?
Just tell me — we’ll build the next piece together 🚀
Happy first-page coding from Hyderabad! 💙
