How to Structure a Large Scale Vue.js Application

Part 1 of 3 in our How to Structure a Large Scale Vue.js Application series.
Written by Daniel Kelly

What is the best way to structure a Vue.js application so that it scales and remains maintainable and extendable the more it grows? This is a question that I've heard on numerous occasions and I think one answer to that question lies in the principle of predictability. When it comes to creating a scalable project you want everything about it to be as predictable as possible.

What exactly do I mean by predictability? At it's simplest, it's the ability to intuitively go from a feature request or bug report to the location in the codebase where said task can be addressed. Furthermore, it's the ability to quickly know what tools you have access to at that location in the codebase in order to complete the task at hand.

Why is this important? Well, like me, you've probably had the experience of inheriting or being introduced to an existing project and then on that first task, opening up the codebase and thinking: "I don't even know where to start!".

You might have even been dealing with a codebase for a while and had the same thought! A predictable codebase alleviates this experience as much a possible, making introducing developers to projects easier and continued work more efficient.

I think it's worth noting here that, while predictability is possible, no project will ever be 100% predictable. Every project, new or existing will have at least a slight learning curve. Also, know that predictability doesn't mean that the codebase or application is quickly understandable as a whole. Many large scale applications are simply too complex for this to be possible and they'll take time to grasp in their entirety. Thus, predictability isn't about seeing the complete finished puzzle but more like knowing the shape of a certain piece and being able to quickly see where it fits. In fact, the nature of a good codebase lends itself to being understandable a piece at a time and shouldn't require its developers to ever have to think about the whole at once.

So how do we accomplish predictability in a codebase? The answer: standards, plain and simple. Maybe that's not the answer you're looking for but it's true. The best way to make anything predictable is to make it follow a set of standards. For example, I can predict with almost 100% certainty that the new full size sheets that I bought just today, will fit my bed even though I've never dressed it with those sheets before. Why? Because of the standard sizing system for bed sheets.

Community-Wide Standards for Predictability

So this begs the question, what kind of standards exist for the Vue.js community at large? I'd say that there are 4 sources of standards available.

  1. The Vue.js style guide
  2. The scaffolding generated by the Vue CLI
  3. the official Vue.js libraries (found under Ecosystem > Official Projects on the Vue.js website)
  4. and more loosely, the most popular component frameworks like Vuetify or Quasar

While some of these are more officially meant to be standards over others, I think they all provide the opportunity to have some common patterns between projects and developers resulting in more predictable codebases.

Official Libraries and Component Libraries

Let's start by talking about the standardization that official libraries and popular component libraries bring. While the primary purpose of such libraries is to bring functionality to the table, a side affect of this is that shared standards are adopted. If you use Vue Router, for instance, you're not only benefiting from the functionality of the package itself, but you end up implementing routes in one project much the same way as you implement them in another project and much the same way as Vue.js developers all over the world implement them.

The Vuex library in fact embraces and touts this fact as a top feature calling itself a "state management pattern + library".

This may seem obvious, but there is a point to be made. If there is an existing popular or recommended solution for a problem in Vue.js (and even more so if it's an official solution), I would think long and hard before using something else. I'm just as happy as the next guy, DIYing my own components, stores, etc but often times it really pays off in the long run to use the tried and true solution not just because of the functionality, test coverage, and documentation they offer but also because of the standardization they bring. (And in the world of JavaScript can't we all just use a bit more standardization 😆).

When it comes to choosing to use these more standardized solutions it's important to remember what it is your building. Are you building a scalable reusable component? Then maybe the standard library isn't for you because a new standard library is kind what you're attempting to build. However that's probably not what most of us are building. Most of us are probably building an application and if that's the case then it's probably better to use the standard (or at least semi-standard) pieces that already exist as your building blocks.

The Beginnings of a Standard File Structure

When it comes to project standards, file structure is an often talked about topic and while Vue has no documentation specifying a particular structure, it does provide a good starting place from the codebase generated with Vue CLI.

screenshot of vue cli generated file structure

Most of us are probably familiar with this structure and that is awesome! That means we're a step closer to predictability! So, the point here is, don't overthink it. Stick with what Vue gives you out of the box and don't stray from it until you have a (really) good reason.

I certainly think there are additions that can be wisely made here (and we'll talk more about those in a minute) but there is no real reason to make changes to what's already there. With this auto generated file structure we have a predictable place for application assets, pages, components, routes, store logic, and a clear entry point. Don't mess with a good predictable thing.

Component Recommended Rules

vue style guide screenshot

Now, focusing on the component's directory, the Vue style guide has some further advice for us to make our file structure more predictable. Among other things, the style guide encourages the following when it comes to defining components:

  • When possible each component should be defined in its own dedicated file (SFC)
  • Single File components should be named in PascalCase
  • Base components should all start with the same prefix (like Base or App)
    • You can think of base components as your app-wide reusable components like a button or a modal
    • This groups them together and declares their global, reusable nature
  • Component names should always be multi-worded to not conflict with any existing or future HTML elements. Don't create a Table or a Button component.
  • Single instance components should begin with the prefix The
    • For example a site header or footer
    • This groups them together and declares them as single use
  • Tightly coupled child components should be prefixed with their parent component's name
    • For instance a TodoListItem in a TodoList
    • This groups them together and declares them related
  • Component names should begin with the most top level (usually general) words and end with the most specific
    • Such as SearchWidgetInput, SearchWidgetResultsList, SearchWidget
    • This groups related components together in the file structure

Hold tight if these don't completely make sense, there's an image coming in a minute that might help 🙂

Besides these, the full style guide has a number of other standards that will help your project be more predictable to a community-wide audience of developers. I won't regurgitate them all here but highly recommend you read and follow the style guide yourself.

Some Recommended Personal/Team-Wide Standards for Predictability

While there are some great standards set in place for the Vue.js community at large by official sources, there are other patterns not so widely adopted that, in my experience, can be just as helpful and made into standards for you or your team's projects. Such standards are necessary as community wide ones aren't 100% comprehensive but just beware and be strict when it comes to how team standards are decided upon and maintained... it can be a rabbit hole of ever changing rules if you're not careful. That said here are some of my recommendations for your Vue.js project standards.

A Flat Component Directory

You might have noticed a common thread amongst most of the component rules from the Vue Style Guide earlier. The naming conventions always help group related components together in the file system. Because of this, combined with reasons below, I suggest adopting the standard of a flat component directory. A flat component directory has the following benefits:

  • Quickly and easily go from spotting a component in Vue devtools to finding the file in the codebase (the filename and the component name are the same)
  • Use your IDE's quick find or file jumping feature to filter files based on their most general attribute down to the more specific
  • Remove analysis paralysis when it comes to deciding how to organize components into sub directories
  • Be able to see all your components at once in a single list
  • Get rid of the redundancy of keywords in filenames AND in the directory (that is if you're following the style guide (and you should be) and you're using nested directories) (ie. post/PostList.vue, post/PostFeature.vue, etc)
  • Remove the temptation to use short one word component names which is easier to do with nested directories (ie. post/List.vue, post/Feature.vue ) and violates the style guide
  • Eliminate surfing the file structure in and out of directories to find a component
  • Simplify importing components (will always be import SomeComponent from "@/SomeComponent")

So what does a flat structure that follows the style guide look like? Here's a good example.

flat component structure example

While your large scale application will obviously have many more files, each one is just another component name in a single well organized list so while the scope of the file structure may expand, the complexity does not.

Standardized Route/Page Naming Convention

Another practice that makes sense is a standardized way of naming our routes and page components. In your typical CRUD application you have the following different pages for each resource:

  1. a list of all the resources
  2. a view of a single resource
  3. a form to create the resource
  4. and a form to edit the resource

While some of these may end up being a nested route (like viewing the single resource in a modal overlay from within the list page), they usually end up having a dedicated route with a corresponding page.

Since I have a background in the PHP framework Laravel, when it came to naming routes and defining their paths in a predictable manner I intuitively fell back on the standards that Laravel already had in place. This made it easier for my Laravel experienced team to more quickly and intuitively work with Vue. Using a "user" resource as an example, the naming convention prescribed by Laravel and adapted for Vue that I recommend is as follows:

Path Route and Component Name What it Does
/users UsersIndex List all the users
/users/create UsersCreate Form to create the user
/users/{id} UsersShow Display the users details
/users/{id}/edit UsersEdit Form to edit the user

While tempted to name the route in a more traditional Laravel manor like users.index instead of UsersIndex, I've found that using the PascalCase works just as well and has the added benefit of matching the component name.

For further consistency and flexibility you should also always reference your routes via their name when using them in router-links and when referencing them programmatically. For example

<router-link :to="{name: 'UsersIndex'}">Users</router-link>

Also it's worth noting that not all routes will fit this pattern exactly as some routes will be more "CRUDdy" than others. For those that don't fit the pattern my only recommendation is that you continue to use PascalCase for your route name for consistency.

A More Comprehensive File Structure

Besides the basic file structure that Vue CLI gives you out of the box I suggest standardizing the following for better predictability.

screenshot of file structure with extra standard directories/files

The added directories here are docs, helpers, layouts, mixins, and plugins. You'll notice 4 out of 5 have a fancy icon next to them provided by the VS Code extension Material Icon Theme. That's because at one time or another, for some frameworks or languages, these directory conventions were common enough to get their own icon in the eyes of the extension developer. That's no coincidence!

I've also added a single file globals.js.

So, what's the reasoning behind these file structure standards? Well, I'm glad you asked!

docs

The purpose of this one is obvious but the more important thing is that it's included and is sitting there staring your team right in the face every time they open the codebase. It'll be more likely that certain aspects of the project are documented if the developer never has to leave their IDE. I've also discovered (it was a pleasant surprise) that writing docs first before coding out a reusable class or component usually helps me better design the interface or API of said code. Go ahead, I dare you to give it a try!

Also, besides the docs directory, I've found it helpful to provide a README.md in the root of each standardized directory explaining the purpose of the directory and any rules for what should be included in it. This is especially helpful for those standards that aren't community-wide.

helpers

This is a common directory in many frameworks for basic input-output kind of functions that are reusable throughout the project. They are typically easy to unit test and usually end up being used more than once. I like to start with a single index.js file and then as the helpers grow, break them up into more grouped files like https.js, cache.js, time.js, etc. Everything in this directory can just be imported and used on demand and if a function ends up never being used at all it can be easily tree shaken from the production bundle.

layouts

I pulled this convention from Nuxt as well as Laravel. It can be handy to not only define page components but also layout components that can be reused across multiple pages. Instead of defining the contents of the page, as the name suggests, these components define more the general layout. For instance, is it a one column or a 2 column page? Does it have a left sidebar or right sidebar? Does the layout include the typical header and footer or is it a completely blank layout maybe with the page content absolutely centered? Usually there are only 2 or 3 of these layout components but nonetheless they can be handy abstraction to have.

mixins

This directory is for organizing all your Vue.js mixins. I think it's important to still append the word Mixin to the end of every file name (like ResourceTableMixin.js) for easy searching in your file switcher. Though I haven't had the chance to really work on a larger scale Vue 3 project yet, I assume this will probably quickly change to a composables directory in preference of extracting reactive data/methods with the Composition API instead of with mixins. Or at least a composables directory might be added to my standard file structure in addition to the mixins directory.

plugins

The last directory I like to include for all my Vue projects is the plugins directory. In a world of packages and libraries we sometimes end up doing more configuring and registering than we do actual coding. That's what this directory is for, including and setting up all the third party Vue stuff. While it's called plugins I don't always necessarily use the term in the strictest sense. In other words, it doesn't have be a third party lib registered via the Vue .use() method. Often times it is, but other times it uses alternate methods of setting up the lib with Vue (such as .component()). For libs that take a one or 2 line setup, I'll write it in a plugins/index.js file. For those that take a more involved setup, I like to create a dedicated file for them in the plugins directory and then import it into the plugins/index.js.

globals.js

This is the only standard file I really ever add. It's purpose is to add a limited number of global variables to the Vue app and, for SPAs that are client side only, usually the window.

This is the comment that usually adorns the top of this file.

/**
 * Use this file to register any variables or functions that should be available globally
 * ideally you should make it available via the window object
 * as well as the Vue prototype for access throughout the app
 * (register globals with care, only when it makes since to be accessible app wide)
 */

In Vue 2 this could be done like so:

Vue.prototype.$http = () => {}

In Vue 3 it looks like this:

const app = createApp({})
app.config.globalProperties.$http = () => {}

Though constantly warned of the dangers of globals, I read once that a "small smattering of global variables" is a very handy thing and it has proven useful for me ever since. This file makes it easy for me to know what those globals are and allows me not to have to think when wanting to add more.

Predictability Summarized

While there are some community-wide standards that you would do well not to ignore, there are also a number of standards you can make for you or your team in order to make your code bases more predictable. While some of the standards mentioned above have proven useful for me there might be others that work well for you or your team. The kicker is sticking to them across projects so they will serve their purpose.

While standards for predictability are a great benefit for your large scale Vue.js applications, there's still more that can be done. Be sure to checkout the next article in this series where we dive into linting and formatting tools like ESLint and Prettier to keep your code clean, error free, and consistent.

Looking into leveling up your business?

We help companies all around the world build Vue applications more efficiently. Do you feel your team would benefit from professional Vue training or consulting? Or perhaps you need some extra hands developing your next milestone?
We are happy to hear your business needs and figure how we can help you out. Check out our business pricing for team training or contact us for business inquires any time!

Learn Vue.js 3 With Vue School

Leave a Reply

Your email address will not be published. Required fields are marked *

Up Next:

Building a Tag Input Component with the Vue 3 Composition API

Building a Tag Input Component with the Vue 3 Composition API