Creating a Vue.js component library: Part II - Structure
Tags: vue, library, plugin, javascript
Published: 2020-07-07 14:19:08

---
title: Creating a Vue.js component library: Part II - Structure
published: true
date: 2019-02-28 17:38:50 UTC
tags:  ["vue", "library", "plugin", "javascript"]
canonical_url:
series: "Creating a Vue.js Component Library" 
---

## Overview

In this post, we'll step through the initial project structure for your Vue components. In [the last post](/siegerts/creating-a-vue-js-component-library-part-i-introduction-2o9f), I covered the importance of packaging together a component library. Give that post a quick read, if you haven't yet, for a more context on goals of the project.

## Vue project structure

Let's jump right in. We're going to use the [Vue CLI 3](https://cli.vuejs.org/guide/). Bam!

Luckily, a lot of the _once was_ configuration steps are now handled by the CLI, mostly regarding webpack. That's not to say that you won't eventually need to [modify the webpack config](https://cli.vuejs.org/guide/webpack.html#simple-configuration) with `vue.config.js` but you'll be surprised how far you can get without doing that. I try to avoid modifying the generic webpack settings, if possible :pray:. Remember, you'll want to to try to lower the barriers to entry of understanding for other developers in order for them to grasp your design choices. Not only for use but also for debugging (which is probably more likely, TBH).

With that in mind, create your Vue project scaffold using the CLI.

```bash
vue create vue-component-library
```

After the project is created, and dependencies downloaded, you should see this in your terminal:

```bash

🎉  Successfully created project vue-component-library.
👉  Get started with the following commands:

 $ cd vue-component-library
 $ yarn serve

```

When prompted during the project initialization, I choose the below options:

```
Vue CLI v3.0.0
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Linter
? Pick a linter / formatter config: Prettier
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N) n
```


Make sure to adjust these options in the future if your preferences change. The Vue CLI comes bundled with a nifty [GUI](https://cli.vuejs.org/guide/creating-a-project.html#using-the-gui) that makes it easy to add and remove plugins.


`vue-component-library` is the name of the component library project (folder, etc.). This _does not_ need to be the same as the programmatic representation of the library. We'll get into that in the upcoming _plugin_ post of the series.

By default, the CLI will create the `src/components` directory. I consolidate this project directory and project by removing unused items such as `App.vue`, `assets/favicon.ico`, etc. The initial structure is typically used as an application baseline. For a typical web application, I leave the setup as-is. Instead, we'll use VuePress for the documentation site functionality.

Next, we'll:

1. Remove the `public` directory
2. Remove `src/assets`
3. Remove `components/HelloWorld.vue`
4. Remove `src/App.vue`

The directory changes are diffed in the layout below.

```diff
  .
- ├── public/
  ├── src/
- │   ├─ assets/
  │   └─ components/
- │      └─ HelloWorld.vue
- └─ App.vue
```

You make be thinking..._did we just delete the whole project?_ Nope! The CLI adds a tremendous amount of functionality to your project besides the file layout. Note, `vue-cli-service` and the corresponding `devDependencies` in the generated `package.json`.

Consider using the above-generated view structure as a custom Vue app _or_ [ejecting your VuePress theme](https://vuepress.vuejs.org/theme/default-theme-config.html#ejecting) if you'd like fewer guardrails.


```json
{
  "name": "vue-component-library",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "vue": "^2.6.6"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.0.0",
    "@vue/cli-plugin-eslint": "^3.0.0",
    "@vue/cli-service": "^3.0.0",
    "@vue/eslint-config-prettier": "^4.0.1",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.8.0",
    "eslint-plugin-vue": "^5.0.0",
    "vue-template-compiler": "^2.5.21"
  }
}
```

<small class="black-40">Package versions may not be exactly the same depending on creation date.</small>

### Component directory structure

For each component, I create three files in a named component directory within `src/components`.

A generic view of the directory structure is:

```
.
└─ src/
  └─ components/
    └─ <component-identifier>/
      ├─ <component-identifier>.vue
      ├─ <component-identifier>.spec.js
      └─ index.js
```

Now, instead, for a hypothetical `Button` component:

```
.
└─ src/
  └─ components/
    └─ Button/
      ├─ Button.vue
      ├─ Button.spec.js
      └─ index.js
```

- `<component>.vue`

> Contains the single file component (SFC).

- `index.js`

> Imports and exports the component from the self-contained component directory.

- `<component>.spec.js`

> Where the component tests will live. If the tests create a snapshot, then a `__snapshots__` directory will be created within this directory.

So, for each of the files, let's create a placeholder.

#### `<component>.vue`

```html
<template>
  <div>
    <slot></slot>
  </div>
</template>

<script>
  export default {
    name: 'name-of-your-component'
  }
</script>

<style></style>
```

Components do not need to end up completely self-contained (template + script + style) but I like to start with this approach. I refactor, if needed, as the library grows in size or complexity. There are a lot of opinions about _styles with JS_ or _CSS in JS_. I like to start with a regular SFC + scoped styles and iterate from there.


Notice that the component has a `name`. This is very important and will impact our registering the library as a plugin in a few steps. Components are registered and referred to by the `name` attribute. Try to use an identifier that won't collide with other project dependencies or tags.

#### `index.js`

```js
import Button from './Button.vue'

export default Button
```

#### `<component>.spec.js`

We'll leave this file empty for now. Ultimately, this will contain the component tests.

#### Component export

Within in the `src` directory, create another `index.js` file to export the component(s). This file will sit alongside the top level `/components` directory as below.

```diff
 .
 └─ src/
   ├─ components/
   │  └─ ...
+  └─ index.js
```

In this file, we'll import, and export, the components from this file.

```js
// index.js
export { default as Button } from './Button'
```

This pattern may seem a bit repetitive but it provides flexibility in the library. The intermediate `index.js` file consolidates the components to be imported as a one-liner in the entry point file, _main.js_.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5vxjsyb1m7gdtvtleicp.png)

More than one component can live in the same `<component>` directory. For example, it may make sense to group components in a logical fashion based on usage pattern (i.e. `<List>` and `<ListItem>`). If so, adjust the above files to reflect:

```js
// src/components
import List from './List.vue'
import ListItem from './ListItem.vue'

export default { List, ListItem }
```

And one level higher:

```js
// src/index.js
export { List, ListItem } from './ListComponents'
```

The foundation is now set to add on the documentation part of the library :book:. But first, we'll [consolidate the components into a plugin](/siegerts/consolidating-components-into-a-vue-js-plugin-ndc)! We'll get to that next.