What should a real-world Vue 3 app look like?
Introduction
Two months ago I started a new role building and maintaining a Vue 2 application. As we set our sights on 2023 - we’ve started to ask ourselves what a next-gen Vue 3 application might look like. What has changed in the industry over the past few years? What are the industry’s best practices?
During my research, I noticed a handful of patterns and practices that stood out - especially from the perspective of organization:
- The entrypoint is split into separate files
- It follows casing standards religiously
- Its type organization is predictable
- It uses next-gen state management (hint: Pinia)
- It makes heavy use of composables
1. The entrypoint is split into separate files
In Vue, the entrypoint is responsible for bootstrapping the app and setting up the main Vue instance. It might be named main.ts
, index.ts
, app-setup.ts
, etc. In a large and complex application, it can get a little out of hand to keep adding bootstrap functions to this single file as your application and its dependencies grow.
In the examples I looked at, it was common practice to split this file into smaller files organized by functionality/dependency. Generally, this allows for better readability, easier debugging, and an overall cleaner structure.
I found the naming convention for where these were located to be interesting. In some instances it was /plugins/
while in others it was /modules/
. I’d love to find more examples to solidify my own preference, but in the end it’s all personal/team preference.
Examples
- vue3-realworld-example-app/src/plugins at master · mutoe/vue3-realworld-example-app
- vitesse/src/modules at main · antfu/vitesse
- hoppscotch/packages/hoppscotch-common/src/modules at main · hoppscotch/hoppscotch
2. It follows casing standards religiously
camelCase, kebab-case, and PascalCase are casing patterns used for naming folders and files within an application. Vue does a good job of documenting best-practices in this area.
According to the Vue docs on **Single-file component filename casing:
Filenames of single-file components should either be always PascalCase or always kebab-case.
PascalCase works best with autocompletion in code editors, as it’s consistent with how we reference components in JS(X) and templates, wherever possible. However, mixed case filenames can sometimes create issues on case-insensitive file systems, which is why kebab-case is also perfectly acceptable.
In the examples I found, there was a mix of casing standards. Most of them favored PascalCase SFC with camelCase JS/TS. Although, my own preference after seeing all of them is the kebab-case everywhere that Directus uses (something about it just felt more cohesive).
PascalCase SFC, camelCase JavaScript/TypeScript, and kebab-case directories
- hoppscotch/packages/hoppscotch-common/src at main · hoppscotch/hoppscotch
- vitesse/src at main · antfu/vitesse
- slidev/packages/client at main · slidevjs/slidev
PascalCase SFC and kebab-case everything else
Kebab-case everywhere
3. Type organization is predictable
By placing your global types in a central location, you can easily import and use them throughout your application. For example, if you have a global type for a user object, you can import it into any component that needs to work with user data.
This not only helps with code organization, but it also makes it easier to maintain and update types as your project evolves. Additionally, keeping your types organized in a separate folder can help to improve the readability and understanding of your code for other developers working on the project.
Finally - local types are colocated alongside or inside the files that need them.
Examples
- directus/app/src/types at main · directus/directus
- slidev/types.ts at main · slidevjs/slidev
- vue3-realworld-example-app/src/types at master · mutoe/vue3-realworld-example-app
4. It uses next-gen state management (hint: Pinia)
Using a state management system can help improve the organization and maintainability of your code, especially in larger applications with many components that need to share data. It can also make it easier to debug your application and understand how different pieces interact with one another.
In the applications that I looked at (specifically for Vue 3), I found that most projects were using Pinia. While digging around on opinions, I found a tweet that provides some context around the direction of Vue’s preferred state management library:
Pinia is de facto Vuex 5! At this point it’s really a naming/branding issue.
— Evan You (@youyuxi) November 24, 2021
You can also read further about this directly in the Vue documentation (which I found later 😀).
Examples
- vue3-realworld-example-app/src at master · mutoe/vue3-realworld-example-app
- directus/app/src at main · directus/directus
- vitesse/src at main · antfu/vitesse
5. It makes heavy use of composables (hence, the Composition API)
Vue 3 introduces a new feature called composables, which are essentially reusable pieces of logic that can be shared across components. These composables are created using a new function-based API and are designed to make it easier to share logic and functionality between components.
One of the key benefits of composables is that they can improve the readability and maintainability of your code. Since they are self-contained and can be easily imported and used in multiple components, you can avoid duplicating code and keep your components lean and focused. Composables also make it easier to test and debug your code, since you can isolate and test individual pieces of logic separately.
Overall, composables are a powerful and useful addition to the Vue ecosystem, and they are sure to become an essential tool for developers working with Vue 3. There is so much content around composables, that it warrants an entire post dedicated to their usage 🎉
Learn More
Examples
- directus/app/src/composables at main · directus/directus
- slidev/packages/client/composables at main · slidevjs/slidev
- vue3-realworld-example-app/src/composable at master · mutoe/vue3-realworld-example-app
- vitesse/src/composables at main · antfu/vitesse
- hoppscotch/packages/hoppscotch-common/src/composables at main · hoppscotch/hoppscotch
There’s plenty more to explore…
These were just five of the organizational aspects I observed in a handful of open-source projects. I’ll continue exploring examples of large scale, production-grade Vue 3 projects. There is a lot to cover - and I’m thoroughly enjoying the Vue ecosystem.