VueJS wrapping UI toolkits
There are a number of UI toolkits in the JS (and specifically VueJS) space. BootstrapVue, Vuetify, VueStrap, Ionic, Quasar, ElementUI and more. I wrote a post some time ago (which may get recovered at some point!) about some of these, and indicated I’d be diving in to one or more at some point, and… I have.
Through a long and winding set of events, I’ve found myself working with Quasar/Vue3 over the last 7 months. This is a relatively big change from BootstrapVue which I’d used on multiple projects since pre-covid times.
As a side note, I’m generally a fan of these, because there’s typically a lot of work that’s already gone in to providing defaults – default settings, theme/styling, documentation, accessibility concerns, testing, and many other things I may not have thought about up front. I’ve worked with folks that are very against this approach, and prefer to do everything ‘by hand’, but I’ve also found a lot tends to be missed, especially with respect to accessibility, documentation and testing, and often for little upside (yes, you can control the UI 100% to your liking but… I also happen to like the defaults and options available in the bootstrap world, for example).
Moving in to Quasar has been … interesting. I’m not a fan of material design styling, which is their default design language. I think changing *from* it to something else may be too big a lift for the project, and they actually may not care to, although there do seem to be some rumblings from folks in the forums about supporting alternate design languages. We’ll see where that goes.
What I’ve recently started doing is wrapping the framework components in my own super-components. This allows for some ability to do more uniform customizations of, say, input tags.
<q-input dense stacked-label clearable v-model="user.email" />
This use of Quasar’s q-input component is common, useful but also gets verbose. You may want the same set of attributes on dozens of inputs on a screen. Wrapping in a common custom input component can save a lot of repetition.
// src/components/My/Input.vue <template> <div class="my-input"> <q-input dense stacked-label dark clearable v-bind="$attrs" > <slot></slot> </q-input> </div> </template> <script> export default { name: "MyInput", }; </script>
Using this in a project…
// src/components/UserInfo.vue <template> <q-page> <q-row> <my-input autofocus v-model="user.name" label="Name" /> </q-row> <q-row> <my-input v-model="user.email" label="Email" /> </q-row> <q-row> <my-input v-model="phone" label="Phone" /> </q-row> </div> </template> <script> import MyInput from "src/components/My/Input.vue"; export default { components: [MyInput], .... .... }; </script>
Each input now gets ‘dense’, ‘stacked-label’ and ‘clearable’ attributes set to on without the repetition. (FWIW, this is typed from memory – hopefully no major errors in there!).
There may potentially be some benefit if the desire to switch to a new UI toolkit arises, but I suspect that’s probably way too hard of a lift (but I’ve only just considered this a potential benefit this morning!)
I’ve seen this done in other places in the past – this is not claiming the approach is new by any means. I’ve just recently started to incorporate this approach to recent projects. I’ll keep you posted if this proves troublesome later on down the road.