Transitioning to Component-Based UI Architecture with Vue
How I shifted from page-centric thinking to component-centric thinking, and how Vue.js makes that shift practical.
For a long time, my mental model for frontend development was built around the concept of a “page.” There’s a page, there’s a form on it, something happens when the form is submitted. I’d write jQuery event listeners, update the DOM by hand, and everything lived together in one place. For small projects, that was enough. But as pages grew and the same UI pieces started appearing in multiple spots, the question “where does this code belong?” started going unanswered.
After I started working with Vue.js, I stopped asking that question — because I no longer think in terms of “pages.” My unit of thinking is now the “component.”
What is a component?
A component is a reusable UI unit that encapsulates its own template, logic, and styles. A button, a form field, a card, a dropdown — each of these can be its own component.
Vue’s Single File Component (SFC) format makes this separation very concrete. When you open a .vue file, you see the template, the JavaScript logic, and the CSS all in one place:
<template>
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
</div>
</template>
<script>
export default {
props: {
user: {
type: Object,
required: true
}
}
}
</script>
<style scoped>
.user-card {
padding: 1rem;
border: 1px solid #ddd;
}
</style>
And I can use this component anywhere like this:
<UserCard :user="currentUser" />
Component hierarchy, not pages
The biggest shift that comes with component-centric thinking is this: instead of designing a page, I design the hierarchy of components that will make up that page.
Take a user list page as an example. In the old days, I’d write everything in a single HTML file. Now:
UserListPage— the page component, fetches the dataUserList— the component that renders the listUserCard— the card that displays a single user
These three are separate files. I can reuse UserCard on another page, and when I need to change it, I change it in exactly one place.
Data flow: props and events
Communication between components confused me at first. Vue’s approach is straightforward: data flows down (via props), events bubble up (via emit).
A parent component passes data down to a child via props. When a child wants to change something, it doesn’t mutate the data directly — it emits an event.
<!-- Child component: DeleteButton.vue -->
<template>
<button @click="handleClick">Delete</button>
</template>
<script>
export default {
methods: {
handleClick() {
this.$emit('delete')
}
}
}
</script>
<!-- Parent component -->
<DeleteButton @delete="removeUser(user.id)" />
This rule feels overly formal at first. But once you’re working with a few nested components, being able to clearly see where data changes is a genuine relief.
What does reusability actually gain you in practice?
On a project I worked on last month, I used the same card design across four different pages. When I needed to change the color theme, I updated a single file and all four pages changed instantly. The old way, I’d have made that change in four separate files — and almost certainly missed one of them.
Reusability sounds like an abstract concept, but in practice it just means “change it in one place, not four.” That small difference becomes increasingly significant as the project grows.
When should you split into a component?
Splitting everything into components isn’t right either. Extracting something too small or too specific into its own component creates unnecessary complexity. My current rule of thumb is simple: if a UI piece will be used in more than one place, or if it has meaningful logic of its own, make it a component.
If something is one-off and page-specific, it can stay in the page component’s template. Refactoring is always possible — I prefer to wait for the need to emerge rather than abstracting prematurely.
The migration process
Starting a brand-new project is one thing, but adding Vue to an existing project is also doable incrementally. You can mount Vue onto a single DOM node where you need dynamic behavior, rather than taking over the entire page. That way, I can keep existing Blade templates intact and convert only the parts that actually need it into components.
Thinking in components takes some time to internalize. But once it clicks, looking at a page as a composition of interchangeable pieces rather than a monolithic block feels far more natural.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.