in Code, Tutorials

Building Packageable Components to Extend Vuetify with TypeScript–Part 1

Recently I have been working on a project built with Vue.js and the amazing Vuetify Material UI library. I needed the ability to accept payments from users and decided to use Stripe Elements to accomplish this. While Stripe demonstrates that their Elements are highly customizable, actually integrating them with Vuetify was much easier said than done.

However, since it’s common for me to integrate Stripe into projects, this situation seemed tailor-made for creating a custom extension of Vuetify.

Complicating matters somewhat, Vuetify recently updated all of their components to be functional components built with TypeScript (the Vuetify 2.0 Arcadia release announcement is worth the read). I should say that I am not primarily a TypeScript developer, but I couldn’t resist the challenge of incorporating the full power of their upgrades. However, since the Vuetify core team was focused on releasing the update, they haven’t yet released fully functional type definitions for all of their components. It proved to be somewhat challenging to get all of benefits of TypeScript while creating this extension.

So, I’m writing this series of blog posts to document the process, for myself and others, of building a packageable Vuetify extension with TypeScript. I hope others will be able to use this guide to create amazing extensions that benefit all of us! This first post will demo the finished package and give a brief overview of the project structure.

Last Things First: A Demo

Here’s a screenshot of what the end result looked like, side-by-side with a regular Vuetify VTextField for comparison of how well I was able to achieve the same look and feel.

A VStripeCard component side-by-side with a Vuetify VTextField component
A VStripeCard component side-by-side with a Vuetify VTextField component

You can also see an interactive “playground” demo on CodePen that will let you experiment with adjusting all of the many available settings. As you will see, the VStripeCard extension supports nearly all of the styles and interaction details built into Vuetify’s other input elements. Vuetify’s components, in turn, are very carefully implemented to reflect all of the design wisdom packed into Material Design text fields.

Build Targets

What did I actually build? Vue and Vuetify components are designed to be used in a couple of different common scenarios:

  1. Direct inclusion in web pages via <script> tags
  2. Being imported and used in SPAs (single-page apps) and PWAs (progressive web apps) built (usually) with Webpack
  3. Incorporated into projects that use SSR (server-side rendering), most notably Nuxt.js

Vue and Vuetify are flexible enough to be used with well-established, and nearly universally-supported variants of JavaScript like ES5, as well as incorporated into projects built with ESNext (bleeding-edge JavaScript) and TypeScript. My goal was for my extension to be usable in all of the same environments where Vue and Vuetify can be used. I think I was mostly successful except for SSR. Because of the way that Stripe Elements are instantiated on a page, I’m not sure my solution is compatible with SSR environments. (I’m not sure though because I haven’t tried it.)

Project Structure

This is a screenshot of the project structure that contains:

  • .vscode: VSCode editor settings
  • assets: the logo I used on my README
  • build: webpack config and other scripts supporting various build targets
  • dev: a Vue app environment for visual inspection and manual testing while coding
  • dist: build targets for the browser; what gets served by services like unpkg and jsDelivr
  • es5: ES5-compatible modules importable into most any modular JavaScript project
  • lib: ESNext modules importable into any recent NodeJS or Babel-transpiled project
  • src: the actual extension code
  • test: unit tests (with Jest)
  • types: type definitions for use in TypeScript projects
  • …and all of the other standard project configuration files

This is a modified version of the exact same structure used by Vuetify to build all of the components of their library. One thing that surprised me was that even though the resulting extension is reasonably small (browser target is ~14kb, gzipped), and has few dependencies (basically just Vue and Vuetify, and optionally vue-plugin-load-script), there are a HUGE number of dev dependencies necessary to support the dev environment and all the various build targets. Take a look at package.json to see what I mean.

Some Thoughts

Building a dev environment that can package an extension for almost any build target is way more complicated than I anticipated, and I easily spent more time understanding, configuring, tweaking, and debugging the dev environment than I did actually writing the code for this extension. That’s cool. A big part of the reason I do projects like this is to learn more stuff and deepen my skills.

I had to spend an enormous amount of time reading the docs for Webpack, and TypeScript, and Jest, and more than a dozen other packages that all contributed setting up an efficient and responsive coding environment. The cool thing is that now that I’ve done this, the next time I build an extension, it will all go much faster. During the process, I ended up reading about almost every single one of the dev dependencies, evaluating potential alternatives, and considering whether or not it was even necessary to keep.

Admittedly, I made the whole process much harder for myself by choosing to stick with TypeScript as the coding language. Juggling complex type dependencies across several packages proved to be quite time and energy consuming. I now have a much deeper understanding of, and appreciation for, the work done by the fantastic Vuetify team. Using TypeScript definitely improved my code, though, and I’m glad I stuck it out.

Up Next…

In Part 2 of this series, I’m going to take a deeper dive into the dev environment and describe what each of the pieces does, and why it is there.

Write a Comment

Comment

  1. Fantastic tutorial and resources! I learned a lot about Vuetify and other great tools by following this tutorial.

    I’d be really interested in a series about how to create a component library that depends on Vuetify components. For example: I’d like to create and share a component that uses multiple Vuetify components and does a few API calls. I don’t think there’s enough documentation or other resources on the internet to learn about this pattern and think many people would be interested in it. Would you consider doing a series on this topic?

    Best Regards

Webmentions

  • Building Packageable Components to Extend Vuetify with TypeScript–Part 6 – Morphatic March 3, 2020

    […] than a “tutorial.” If you’d like to follow along from the beginning, please head on over to Part 1. When we left off in Part 5, we had just finished adding functionality and tests. In this […]

  • Building Packageable Components to Extend Vuetify with TypeScript–Part 5 – Morphatic March 3, 2020

    […] just joining us, it would behoove you to go back and read the earlier installments of this series: Part 1, Part 2, Part 3, and Part 4. In this installment, we’re going to pick up where we left off. […]

  • Building Packageable Components to Extend Vuetify with TypeScript–Part 4 – Morphatic March 3, 2020

    […] Part 1 introduced VStripeElements, a component that applies Vuetify styles and interactions to Stripe Elements. […]

  • Building Packageable Components to Extend Vuetify with TypeScript–Part 3 – Morphatic March 3, 2020

    […] through the steps required to build your own custom component that extends Vuetify with TypeScript. Part 1 of this series gave an overview of VStripeElements, a component I built to apply Vuetify styling to the credit […]

  • Building Packageable Components to Extend Vuetify with TypeScript–Part 2 – Morphatic March 3, 2020

    […] Part 1 of this series, I introduced VStripeElements, an NPM package that I wrote to be able to use functionality of […]