After 100 days of building with Vue.js and Nuxt, the picture becomes clear: these frameworks work because they respect how developers think about state, components, and full-stack concerns. We've learned what scales, what breaks, and what matters when shipping to production.
This recap distills the lessons that actually stick—the ones that will shape how we write applications long after the series ends.
Reactivity is the core — Understand refs, computed and watchers
Vue's reactivity system is its superpower, but it's also where most confusion lives. After 100 days, one truth emerges: everything in Vue orbits around tracking which pieces of state change and which pieces depend on them. That's not philosophy—it's a contract we sign with the framework.
Refs are the foundation. They wrap primitive values so Vue can track them. Without ref(), a number is just a number. With it, Vue watches for mutations and re-renders when needed. That distinction matters.
import { ref, computed, watch } from 'vue'
// Refs track individual values
const count = ref(0)
const userName = ref('')
// Computed derives new state reactively
const greeting = computed(() => {
return `Hello, ${userName.value}!`
})
// Watch responds when dependencies change
watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`)
})
Computed properties are where elegance happens. They're cached, they're lazy, and they only recalculate when their dependencies shift. In production, computed properties prevent unnecessary calculations better than manually wiring up watchers ever could. If you find yourself writing watchers when a computed would do, stop—that's technical debt in waiting.
Watchers are for side effects. When data changes and you need to fetch new data, log analytics, or update the DOM imperatively, watchers are your tool. But they're not for deriving new values. That's computed's job.
The mistake we see repeatedly: mixing these concerns. Teams use watchers to transform state instead of computed, or they create computed properties that trigger side effects. After 100 days, the pattern should feel automatic: derive with computed, respond with watch, store with ref.
Composables scale — Reuse logic cleanly across components
Composables are how Vue applications grow without collapsing under their own weight. They're not hooks, not mixins, not render functions—they're just functions that return reactive state and methods. The simplicity is deceptive.
We've learned that composables work because they make logic portable. A form composable encapsulates validation, submission, and error handling once, then dozens of components use it without repeating a line of code. That's not just DRY—it's a guarantee that behavior stays consistent.
import { ref, computed } from 'vue'
export function useForm(initialValues) {
const values = ref({ ...initialValues })
const errors = ref({})
const isSubmitting = ref(false)
const isValid = computed(() => Object.keys(errors.value).length === 0)
const setField = (name, value) => {
values.value[name] = value
// Clear error when user fixes it
if (errors.value[name]) delete errors.value[name]
}
const submit = async (onSubmit) => {
isSubmitting.value = true
try {
await onSubmit(values.value)
} catch (e) {
errors.value = e.fieldErrors || {}
} finally {
isSubmitting.value = false
}
}
return { values, errors, isValid, isSubmitting, setField, submit }
}
The pattern that scales: one composable per concern. useForm handles forms. useFetch handles data loading. useLocalStorage handles persistence. Each is small, testable, and reusable. When you find yourself copying logic between components, stop and extract a composable instead.
After 100 days, we know that composables work best when they're minimal and focused. A composable that does five things is a composable that will break five ways. Keep them narrow, keep them pure, and compose them together in components.
Nuxt adds full-stack power — SSR, server routes and SEO
Nuxt is Vue's answer to a question we've all asked: how do we ship this thing and not worry about infrastructure? The answer is opinionated, but the opinions are good ones.
Server-side rendering stops being theoretical on day one of a Nuxt project. Every page renders on the server before reaching the browser. That means search engines see real HTML, users see content immediately, and performance metrics improve without extra effort. The tradeoff—components run twice, in Node and in the browser—forces you to think about universal code. But that's a feature, not a bug.
Server routes let us write backend logic without leaving the project. A route handler in /server/api/users.ts becomes an endpoint accessible from client code. No separate API server, no CORS headaches, no authentication plumbing scattered across two repositories.
// server/api/users.ts
export default defineEventHandler(async (event) => {
// This runs only on the server
const users = await db.users.findAll()
return users
})
// components/UserList.vue
const { data: users } = await useFetch('/api/users')
SEO becomes automatic. Nuxt renders metadata, handles Open Graph tags, manages redirects—all declaratively. A page component can define its own metadata using defineRouteRules and useHead. Search engines see the real content, social media cards populate correctly, and we don't maintain a separate SEO layer.
The lesson: Nuxt lets us think of applications as full-stack from day one. The client-server boundary is there, but it's not a chasm we have to bridge with separate projects and deployment pipelines. After 100 days, that coherence matters more than any individual feature.
Production habits — Types, tests, performance and secure deployments
This is where the real learning lives. Features are easy. Production is hard.
Types are not optional. TypeScript catches mistakes before they ship. In Vue and Nuxt, types flow through templates when we use <script setup lang="ts">. Props are type-safe, emits are type-safe, and refactoring becomes a compiler pass instead of a debugging session. After 100 days, codebases without TypeScript feel reckless.
Tests aren't about coverage percentage. They're about confidence. We test the paths users take, the edge cases that break in production, and the integrations where systems meet. Unit tests for composables, integration tests for pages, snapshot tests for generated HTML. Test pyramid, not pyramid scheme.
Performance is a feature. Code splitting, image optimization, lazy-loading routes—these aren't nice-to-haves, they're requirements. Nuxt handles most of it automatically, but we still need to measure. Lighthouse isn't a vanity metric; it's user experience quantified. After 100 days, the teams that ship fast are the ones that profiled early.
Secure deployments mean environment variables stay off GitHub, dependencies are audited, and we know what's running in production. Docker, if we're deploying to custom infrastructure. Vercel or Netlify, if we're taking the platform route. Either way, the principle is the same: reproducible builds, signed artifacts, automatic deployments from a protected branch.
Common mistakes
- Computed properties with side effects: They run during render. If you're fetching data or logging, use a watcher or
watchinside a lifecycle hook instead. - Refs everywhere: Not every piece of state needs to be a ref. If it never changes, it doesn't need reactivity. If it's derived, use computed.
- Composables that assume a component context: A composable that calls
useRouteroutside a setup function will fail in SSR. Keep composables framework-agnostic where possible, or document their constraints. - Skipping server-side route protection: Just because Nuxt has server routes doesn't mean they're automatically secure. Validate permissions, check auth tokens, and never trust client-side logic for security boundaries.
- Performance ignored until launch: Measure early, fix continuously. A 5-second page load penalty caught in week two is easier to fix than caught in week 12.
Key takeaways
- Vue's reactivity model—refs, computed, watchers—is the mental model everything else builds on. Master it first, then everything else follows.
- Composables are how Vue scales beyond single-file components. Extract logic early, test it independently, reuse it everywhere.
- Nuxt removes the client-server separation as a source of complexity. Think full-stack, deploy once, scale naturally.
- Production Vue means TypeScript, tests, performance measurement, and security from the start. These aren't afterthoughts, they're requirements.
- After 100 days, the pattern becomes clear: start small, measure everything, refactor fearlessly, and let the framework handle the infrastructure so we focus on user experience.






Comments
Loading comments…