vue gotchas #2: exporting a UI component under vite

devops terminal
9 min readJan 9, 2024
Photo by Ryan Quintal on Unsplash

source code of the project: https://gitlab.com/quoeamaster/vitey-components

The story…

In gotcha #1, we have learnt how to create a Vue.js application based on the vite framework. Everything is good and we probably started to build some fantastic webapps since then. Now what if you have another project or idea that would need to spin up a webapp separately?

No sweat, we already learnt how to create and bootstrap a Vue.js (a big applause to vite framework). We can quickly dive into the code within minutes. Hm… we probably need a label and button UI component for the pages and wait… we already developed such in earlier webapp projects. So how do we re-use them in the new project?

Ways are:

  • copy and paste the .vue files manually to the new project and update the configurations if necessary
  • re-code the component by referencing what existing components we have on hand; this is a great chance to enhance our existing components with new features and rectify certain business logic associated
  • why the fuss?? Simply import the existing UI components and use them in the new project!

Approach 1 is never a good idea as it involved manual and error-prone work. Approach 2 has its advantages if the existing components are far from expectation, however the ultimate issue of re-usability is still left there not touched (we might have a better version of the component, but it is still not re-usable and only available for the current project). Finally approach 3 is what we want to discuss in this gotcha.

Make your UI components exportable / re-usable

Saying is easy but HOW??? Let’s have a brief idea about the concept of re-usability. An ordinary node.js project serves at least 1 purpose, to provide services in various forms including:

  • hosting and running a webapp
  • running a server side component to handle http requests
  • acting as a console / CLI program
  • being a re-usable component (not executable by itself)

The common use case is obviously host/run a webapp, and if we try to run a build for such type of project, it would end up building a webapp (index.html, uglyfied javascripts, uglyfied css, required resources such as images and static files). The build is perfect for deployment purposes.

Hence if we are expecting the project contents to be re-usable, the default build process just does not work. The hint is we would need to customize the build process to make sure the components are packed correctly and ready for other projects to “import”.

We would go through the build customization in later sections of this gotcha, and first of all we need to have a component for export.

Creating a UI component

Let’s start to create a UI component named Label.vue

src/components/label/Label.vue

PS. Typescript is employed here, hence the script’s syntax will vary from pure Javascript vue components.

PS. TailwindCSS is also applied here, we would have a separate gotcha on using TailwindCSS in the coming future. In simple, TailwindCSS provides a list of pre-defined css classes for web developers to re-use.

The Label component is pretty simple and we could pass in a “title” for the label’s caption and an optional fontawesome “icon” to be displayed along side the caption.

Next is to make sure the Label component is exportable, create an index.js file:

src/components/label/index.js

In the example above, we import the Label.vue component and named it as “viteyLabel” (please feel free to rename it to any favourite name). Then we export the component in 2 flavors:

  • default => any js files importing the component would directly get an instance of “viteyLabel”; this approach is great if you have a bunch of components (eg. Label, NoIconLabel, ImageLabel) to export.
  • named => { viteyLabel } is a named export which means any js files could just import this particular component instead of everything exposed through the default channel. Check the concept of “tree shaking” optimization :)

Now, we would add another index.js at the component’s root folder level (the previous index.js is at the component level):

src/components/index.js

Similarly we import the UI components 1 by 1, in this example assume there are 2 components named viteyCard and viteyLabel. After that we export them out in the default channel.

Typically the components are now exportable and we can test drive them in the same webapp project.

Testing the component locally

Every vue.js project has a App.vue file scaffolding the webapp’s main layout. We can now import the Label component as usual:

src/App.vue

To import, simple “import { viteyLabel } from ‘./components/label’ ”.

To use it, add “ <viteyLabel title=‘some caption value’></viteyLabel> ”.

The output would be something like this:

output of Card and Label

Cool~ Next is to build the project into a re-usable module.

Customizing the build

Every framework has its own way to build a project, for vite, the config file is named vite.config.ts.

vite.config.ts

The come along vite.config.ts only contains the “plugins” line and the output of the build would be a webapp with uglified javascript/css and static resources. In order to convert the build into a re-usable module, we would need to customize the build and add in various plugins.

The “lib” section would state where is the entry point for the build information (e.g. index.js in the project’s root folder, we would cover this file later). We state the formats for the uglified javascripts (e.g. es — ECMAScript, cjs — Common JS) and finally states the filename of the product (in this case vite-components.${format}.js -> vitey-components.es.js or vitey-components.cjs.js)

Next is the “rollupOptions”, we set “external” for “vue” which means any Vue.js sources should not be included in the build and expected the project using this component would have Vue.js dependency set correctly. We would also use the “postcss” plugin.

PS. A note on postcsss plugin: If our UI component has no CSS required (very rare case) then the postcss plugin section could be totally eliminated. In the example project, TailwindCSS is used and hence the UI component would ONLY work correctly with the required CSS classes also exported. More importantly… TailwindCSS works as a postcss processor and that is why the “postcss” plugin MUST be configured.

The build entry point: index.js

The final step is to configure the build entry point (index.js):

src/index.js

Important concept here! The index.js file needs to import any resources (vue, js, css, image files) that MUST be available to make sure the UI components would work correctly.

Based on the concept, we would be importing:

  • every public components under the src/components folder (this is why we have defined the src/components/index.js)
  • common css files (src/style.css)
  • the public components exposed through the default channel would be wrapped into a vue plugin (check line 6 ~ 14); this plugin method would be used by the target project (would cover this in the coming session)
  • the fontawesome css classes (we are using fontawesome icons in this example)

Finally don’t forget to export all the above resources.

Another thing we need to configure is the package.json file.

package.json

There are a couple of things we need to update:

  • name => provide the name of the module or project, this would be shown on the npmjs.com package list later if we are going to publish the project
  • private => set to “false” as ultimately the project should be public and re-usable by others
  • main => point to the built commonJS file (in this case /dist/vitey-components.cjs.js) The filename is configured in the vite.config.ts file.
  • module => point to the built es file
  • files => indicating where the built files are located (e.g. /dist)
  • devDependencies update => make sure vitejs/plugin-vue.js, postcss, rollup-plugin-postcss, vite is available (others like fontawesome, tailwindcss depends on the project’s actual needs)
  • peerDependencies update => make sure vue is in this section, we are expecting the target project to have vue.js configured correctly

Voila~ We did it~ The project is now exportable. Run the following command (yarn build) to build the project:

Clearly we can see the output is a bunch of javascripts instead of the html and uglified js etc. Next is to test it out… in another project.

Testing the components in another project

As usual, spin up a another vue.js project through vite CLI. Here we have 2 options to include the component module.

  • if we already published the component module to npmjs.com repository; simply run “ yarn add -D vitey-components ”; and import the contents as usual
  • for development purposes, in which we don’t want to publish intermediate codebase to the repository, we can create symbolic link of the component project

In this gotcha, we would choose the symbolic link approach. First of all run

yarn link

under the component project’s root. We might need to use root user for this step. Simply prepend “sudo” to the above command.

Then we can navigate to the target project’s root folder and run:

yarn link vitey-component

By now if we check the node_modules/ folder, a symbolic link named “vitey-component” would be available. Which means we kind of “downloaded” the library from a repository and ready to use.

Once the module is ready, update the target project’s src/main.js and src/App.vue files.

src/main.js

Explainations

line 5: imports the required uglified css file used by the component

line 6: imports all the public components available from our component module (vitey-components in this case); remember the components are wrapped up as a Vue.js plugin method.

line 22: create the Vue app as usual but add back our component plugin through the “use()”; by now every public component (in this case viteyCard and viteyLabel) would be directly accessible within the project.

line 8 ~ 20: registering the fontawesome icons (used and exported by our components) and make sure they are usable in the project; feel free to add other necessary icons for the target project as well.

Explanations

line 6: use the component as is <viteyLabel>

Closings:

Hooray, we just created our very first re-usable UI component~ The following are what we just accomplished:

  • understanding the approaches to re-use UI components
  • create a component and understanding the export hierarchies
  • configuring vite’s build to transform the project from a webapp to a re-usable module
  • proto-tested locally and externally
  • introducing “ yarn link ” for testing purposes
  • understanding how the vue plugin approach works on component imports.

whew~ That’s quite a lot of things isn’t it. There is somehow ONE important caveat on the build — dependencies version chaos~

The above build process and configuration works as is for the shown dependency versions. What does that mean to you? Simply if you are using other versions of vitejs/plugin-vue for example, the build might crash due to the change of code in the library. This is something that every node.js developer needs to understand and try to specify target versions for certain dependencies… or else you would up spending hours to figure out which part goes wrong.

Happy UI building~

--

--

devops terminal

a java / golang / flutter developer, a big data scientist, a father :)