DEV Community

Cover image for How to Structure Vue Projects
Alexander Opalic
Alexander Opalic

Posted on • Edited on • Originally published at alexop.dev

How to Structure Vue Projects

Table of Contents

  1. Introduction
  2. Some General Rules
  3. Flat Approach
  4. Atomic Design
  5. Modules
  6. Feature Sliced Design
  7. Microfrontends
  8. Conclusion

Introduction

When initiating a Vue project, it's crucial to think about its structure. The primary consideration is the anticipated scale of the project. In this blog post, I will explore various structures suited to Vue projects of differing sizes. This consideration aligns well with Conway's Law:

"Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations." - Mel Conway

In essence, Conway's Law suggests that the architecture of your Vue application will inherently reflect the architecture of your organization, influencing how you should plan your project's structure.

Image description

Some General Rules

Before we start with differnt strucutres for your Projects. I want to highlight some general Rules that you can apply for every Strucutre this mostly come from the offical Vue Styleguide.

Base Component Names

Use a Prefix for your UI Components

Bad



components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue


Enter fullscreen mode Exit fullscreen mode

Good



components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue


Enter fullscreen mode Exit fullscreen mode

Tightly coupled component names

Group Tightly coupled component Names Together

Bad



components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue


Enter fullscreen mode Exit fullscreen mode

Good



components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue


Enter fullscreen mode Exit fullscreen mode

Order of words in component names

Component names should start with the highest-level (often most general) words and end with descriptive modifying words.

Bad



components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue


Enter fullscreen mode Exit fullscreen mode

Good



components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue


Enter fullscreen mode Exit fullscreen mode

Tests

Deciding how to structure your tests and where to place them could be a topic for its own blog post. In this article, we will explore having a separate folder for tests, where each test file reflects the source code. Alternatively, you could place the test files next to the files they test. Both approaches are valid.

Approach 1: Separate Test Folder



/vue-project
|-- /src
|   |-- /components
|   |   |-- MyComponent.vue
|   |-- /views
|   |   |-- HomeView.vue
|-- /tests
|   |-- /components
|   |   |-- MyComponent.spec.js
|   |-- /views
|   |   |-- HomeView.spec.js
|-- package.json
|-- ...


Enter fullscreen mode Exit fullscreen mode

Approach 2: Inline Test Files



/vue-project
|-- /src
|   |-- /components
|   |   |-- MyComponent.vue
|   |   |-- MyComponent.spec.js
|   |-- /views
|   |   |-- HomeView.vue
|   |   |-- HomeView.spec.js
|-- package.json
|-- ...


Enter fullscreen mode Exit fullscreen mode

Flat Approach

When launching a small-scale Vue project such as a Proof of Concept, you might prefer a straightforward folder structure to avoid complexity:



/src
|-- /components
|   |-- BaseButton.vue
|   |-- BaseCard.vue
|   |-- PokemonList.vue
|   |-- PokemonCard.vue
|-- /composables
|   |-- usePokemon.js
|-- /utils
|   |-- validators.js
|-- /layout
|   |-- DefaultLayout.vue
|   |-- AdminLayout.vue
|-- /plugins
|   |-- translate.js
|-- /views
|   |-- Home.vue
|   |-- PokemonDetail.vue
|-- /router
|   |-- index.js
|-- /store
|   |-- index.js
|-- /assets
|   |-- /images
|   |-- /styles
|-- /tests
|   |-- ...
|-- App.vue
|-- main.js


Enter fullscreen mode Exit fullscreen mode

Atomic Design

For larger Vue applications, employing the Atomic Design methodology can be advantageous. This approach organizes components into a hierarchy from simplest to most complex:

  • Atoms: Basic elements (e.g., buttons, icons)
  • Molecules: Groups of atoms (e.g., search bars)
  • Organisms: Complex components (e.g., navigation bars)
  • Templates: Layouts displaying component structure
  • Pages: Actual UI screens with real data

This method ensures scalability and maintainability, facilitating the transition between simple and complex components smoothly.

Image description



/src
|-- /components
|   |-- /atoms
|   |   |-- AtomButton.vue
|   |   |-- AtomIcon.vue
|   |-- /molecules
|   |   |-- MoleculeSearchInput.vue
|   |   |-- MoleculePokemonThumbnail.vue
|   |-- /organisms
|   |   |-- OrganismPokemonCard.vue
|   |   |-- OrganismHeader.vue
|   |-- /templates
|   |   |-- TemplatePokemonList.vue
|   |   |-- TemplatePokemonDetail.vue
|-- /pages
|   |-- PageHome.vue
|   |-- PagePokemonDetail.vue
|-- /composables
|   |-- usePokemon.js
|-- /utils
|   |-- validators.js
|-- /layout
|   |-- LayoutDefault.vue
|   |-- LayoutAdmin.vue
|-- /plugins
|   |-- translate.js
|-- /router
|   |-- index.js
|-- /store
|   |-- index.js
|-- /assets
|   |-- /images
|   |-- /styles
|-- /tests
|   |-- ...
|-- App.vue
|-- main.js


Enter fullscreen mode Exit fullscreen mode

Modules

As your project scales, consider a modular monolithic architecture. This structure encapsulates each feature or domain, enhancing maintainability and preparing for potential evolution towards microservices:



/src
|-- /core
|   |-- /components
|   |   |-- BaseButton.vue
|   |   |-- BaseIcon.vue
|   |-- /models
|   |-- /store
|   |-- /services
|   |-- /views
|   |   |-- DefaultLayout.vue
|   |   |-- AdminLayout.vue
|   |-- /utils
|   |   |-- validators.js
|-- /modules
|   |-- /pokemon
|   |   |-- /components
|   |   |   |-- PokemonThumbnail.vue
|   |   |   |-- PokemonCard.vue
|   |   |   |-- PokemonListTemplate.vue
|   |   |   |-- PokemonDetailTemplate.vue
|   |   |-- /models
|   |   |-- /store
|   |   |   |-- pokemonStore.js
|   |   |-- /services
|   |   |-- /views
|   |   |   |-- PokemonDetailPage.vue
|   |   |-- /tests
|   |   |   |-- pokemonTests.spec.js
|   |-- /search
|   |   |-- /components
|   |   |   |-- SearchInput.vue
|   |   |-- /models
|   |   |-- /store
|   |   |   |-- searchStore.js
|   |   |-- /services
|   |   |-- /views
|   |   |-- /tests
|   |   |   |-- searchTests.spec.js
|-- /assets
|   |-- /images
|   |-- /styles
|-- /scss
|-- App.vue
|-- main.ts
|-- router.ts
|-- store.ts
|-- /tests
|   |-- ...
|-- /plugins
|   |-- translate.js



Enter fullscreen mode Exit fullscreen mode

Feature Sliced Design

Feature-Sliced Design is a way to organize big and long-term projects so they are easier to manage and grow. This approach breaks the application into different layers, each with a specific role:

  • App: Global settings, styles, and providers.
  • Pages: Builds full pages using entities, features, and widgets.
  • Widgets: Combines entities and features into cohesive UI blocks, like IssueList or UserProfile.
  • Features: Handles user interactions that add value, such as sending comments, adding to cart, or searching users.
  • Entities: Represents core business models like User, Product, and Order.
  • Shared: Provides reusable utilities and components unrelated to specific business logic, like UIKit, libraries, and APIs.


/src
|-- /app
|   |-- App.vue
|   |-- main.js
|   |-- app.scss
|-- /processes
|-- /pages
|   |-- Home.vue
|   |-- PokemonDetailPage.vue
|-- /widgets
|   |-- UserProfile.vue
|   |-- PokemonStatsWidget.vue
|-- /features
|   |-- pokemon
|   |   |-- CatchPokemon.vue
|   |   |-- PokemonList.vue
|   |-- user
|   |   |-- Login.vue
|   |   |-- Register.vue
|-- /entities
|   |-- user
|   |   |-- userService.js
|   |   |-- userModel.js
|   |-- pokemon
|   |   |-- pokemonService.js
|   |   |-- pokemonModel.js
|-- /shared
|   |-- ui
|   |   |-- BaseButton.vue
|   |   |-- BaseInput.vue
|   |   |-- Loader.vue
|   |-- lib
|   |   |-- api.js
|   |   |-- helpers.js
|-- /assets
|   |-- /images
|   |-- /styles
|-- /router
|   |-- index.js
|-- /store
|   |-- index.js
|-- /tests
|   |-- featureTests.spec.js


Enter fullscreen mode Exit fullscreen mode

This setup is great for large projects because it makes them easier to scale up and keep tidy. For more details on how these layers work, check out the official Feature-Sliced Design documentation.

Feature Sliced Design Diagram

Microfrontends

Microfrontends take the idea of Microservices and apply it to the front-end part of web apps. This means different teams can work on different sections of a web app without interfering with each other. Each section, or "Microfrontend," works on its own and can change independently. Here's a basic overview for an SPA. Please note that I won't delve into the details of how Microfrontends actually work in this blog post.

  • Application Shell: This is the main controller that handles the basic layout and routing for the site. It connects all the Microfrontends together.
  • Decomposed UIs: Each Microfrontend focuses on a specific part of the application. They can be developed with different technologies and updated separately.

Image description

The main advantage is that Microfrontends let teams update parts of the app without waiting for others, which can speed up development. However, this setup can make the app more complex to manage and keep consistent.

Useful Resources:

This strategy is ideal for large, complex projects with multiple development teams. Each team can focus on specific business requirements without affecting the work of others, potentially using the technology that best suits their part of the application.


Conclusion

Image description

I hope it's now clear that you should select a structure that reflects your organization's size and complexity. Additionally, more advanced structures would be worth their own blog post; I just wanted to provide you with a good overview. In general, the larger and more complex your team—or the more teams you have—the more you should aim for a structure that better separates these concepts. Essentially, the structure of your team will guide you in determining the best project structure for your needs.

Approach Description Pros Cons
Flat Approach Simple structure, ideal for small projects or proof of concept. - Easy to implement
- Minimal setup
- Not scalable
- Clutters as project grows
Atomic Design Hierarchical structure based on component complexity. - Scalable
- Organized
- Reusable components
- Overhead in managing layers
- Complex setup
Modules Modular structure that encapsulates features. - Scalable
- Encapsulated features
- Potential duplication
- Can become complex
Feature-Sliced Design Organizes project into functional layers and slices. - High cohesion
- Clear feature separation
- Initial complexity
- Requires thorough planning
Microfrontends Each part of the application is deployed separately. - Independent deployments
- Scalable
- High complexity
- Coordination needed between teams

Enjoyed this post? Follow me on X for more Vue and TypeScript content:

@AlexanderOpalic

Top comments (11)

Collapse
 
drfcozapata profile image
Francisco Zapata

Although my 'organization' is small (just me) most of my projects are monoliths developed with Vue, Laravel and Inertia, and I prefer the feature sliced design structure for always thinking about scalability of projects and ease of maintenance.
Excellent post, thanks for sharing it!

Collapse
 
devmount profile image
Andreas

Seeing those example file structures is extremely helpful! In a way, I already used the Atomic Design approach without the molecules level. Depending on the project size, I guess it's fine to skip one level.

Thank you for this post!

Collapse
 
davestewart profile image
Dave Stewart • Edited

Great article!

Had not seen Feature-Sliced Design before, but I really like what I see.

So much so I've linked to this post from my recent Nuxt Layers article:

Thanks!

Collapse
 
alexanderop profile image
Alexander Opalic

Awesome, thank you! Yeah, Feature-Sliced Design is not very popular, but it looks really interesting. In general, the frontend world lacks good content about structure and architecture.

Collapse
 
sujitmohanty profile image
Sujit Mohanty

Thanks so much for this!

Collapse
 
dipenparmar12 profile image
Dipen Parmar

A truly remarkable blog

Collapse
 
dvalin99 profile image
Domenico Tenace

Good article, thanks for share!
The Atomic Design is very powerful and amazing, I will try it in the future project :)

Collapse
 
vilan profile image
coco

this is article give very gain for me!

Collapse
 
sujitmohanty profile image
Sujit Mohanty

Thanks so much for this! It was extremely insightful.

Collapse
 
dipenparmar12 profile image
Dipen Parmar

One of the amazing blogs

Collapse
 
nurulid profile image
Nurul ID

This is so helpful.
Thanks for sharing!