menu

Nuxt Enterprise Patterns: Component Management

As your Nuxt application grows, there are some conventions used by Nuxt and Vue that may start to annoy you. The following is my prefered way to organize components in larger projects. Yes, this may go against several "best practices".

Quick Overview

/components             // for global components only
/pages
  /home                 // the homepage
    /components         // components for the homepage
      /Banner.vue
    /index.vue           // the home page
  /product
    /components         // components for the product page
      /Reviews.vue      // files are single words if possible
      /Gallery          // larger components can have their own children
        /index.vue      // the main Gallery container
        /ImageList.vue  // more components used in the Gallery component
    /index.vue          // the product page

Local Component Folders

I love having everything I need physically close together when working on a feature. This is one of the reasons I love Single File Components (SFC) so much. I feel the exact same about pages. If there are components that I've needed to break out just for one page, they belong in a sub-folder of that page. There is no need for me to hunt around. This also saves you from going up/down the folder tree all the time just to complete a feature.

Component Folder Is Now Globals

In the example above, it's assumed the Gallery components are special to the product page. The second they are needed on any OTHER page, then they should be moved into the old components folder. That folder should be reserved for any component that's used on multiple pages, or doesn't belong to any one page (like global modals).

Short And Sweet Filenames

In the Vue Style Guide, it's recommended that singletons begin with The and any other component be two words like ToDoList.vue or ToDoItem.vue. I find this pretty annoying. Do you really want to write ProductReview, ProductGallery, ProductDetails over and over for the rest of your life? Not me. I know this was supposed to reduce conflicts and collisions, but honestly in practice I've had zero problems with it.

I Love It, Now What?

Ok you're convinced this is right for you, to pull it off you'll have to make 1 or 2 tweaks to your Nuxt configs.

1) Tell Nuxt Not To Treat Components As Pages

Tell Nuxt to ignore the new components folders in the pages folder. Since the pages folder is special in Nuxt, we have to tell it to exclude these new sub-folders when doing builds so they don't end up as weirdo routes.

nuxt.config.ts
hooks: {
  'pages:extend'(pages) {
    const pagesToRemove: NuxtPage[] = []
    pages.forEach((page) => {
      if (page.path.includes('component')) pagesToRemove.push(page)
    })

    pagesToRemove.forEach((page: NuxtPage) => {
      pages.splice(pages.indexOf(page), 1)
    })
  }
}

2) Add Sub-Folders To Auto Imports

If you use auto imported components you have to tell Nuxt there are more components to be discovered in your pages folder.

nuxt.config.js
components: [
  '~/components', {
    path: '~/pages',
    pattern: '*/components/**',
    pathPrefix: false
  }
]

3) Move Your Homepage Too

To be consistent, you can even move your homepage from the page root into it's own folder! All you have to do is use the definePageMeta composable to define it's route path.

pages/home/index.ts
<template>
  <div>My Homepage</div>
</template>
<script setup>
  definePageMeta({
    path: '/',
  })
</script>

Conclusion

We use this pattern at work, and we love it! We have hundreds of components in some pretty complex pages and this pattern helps us organize the chaos without losing our minds.