はじめに
管理画面を作るたびに、ボタン・カード・フォーム・データテーブルといった同じようなUI部品を一から実装していないでしょうか。CSSでボタンの当たり判定やフォーカスリングを調整し、モーダルの開閉アニメーションを書き、フォームのバリデーションエラー表示を毎回作り直す。デザイナー不在のプロジェクトだと、この「見た目を整える作業」だけで開発時間の大半が溶けてしまうこともあります。
Vuetifyは、この悩みをMaterial Designベースの作り込まれたVueコンポーネント群でまるごと解決してくれるUIフレームワークです。<v-btn>や<v-card>、<v-data-table>をテンプレートに書くだけで、デザインスキルがなくても統一感のある画面が組み上がります。
とはいえ、読むより触った方が早いと思います。問い合わせフォームをVuetifyのコンポーネントだけで組んだサンプルを置いてあるので、先に挙動を見たい方はこちらからどうぞ。
Vuetifyとは
Vuetifyは、"no design skills required"(デザインスキル不要)を掲げるVue向けのコンポーネントフレームワークです。npm上ではVue Material Component Frameworkとして公開されており、MITライセンスで商用利用も可能。GitHubリポジトリは4万1000以上のスターを集める、Vueエコシステムを代表するUIライブラリのひとつです。
現在は、Material Design 2ベースで実績のあるv3系と、Material Design 3(MD3)への刷新やグリッドシステムの全面見直しを行った最新のv4系が並行してリリースされています。本記事では執筆時点の最新版であるv4.1.7を前提に解説します。
主な特徴
- 豊富なコンポーネント群 - ボタン、カード、フォーム、ナビゲーション、データテーブル、ダイアログなど、管理画面に必要なUI部品がほぼ揃っています
- Material Design準拠 - v4ではMD3のタイポグラフィやエレベーション(影)に対応し、Googleのデザインガイドラインに沿った見た目を素早く実現できます
- 強力なテーマシステム - 色のパレットを定義するだけでアプリ全体の配色を統一でき、ライト/ダークテーマの切り替えも標準機能として用意されています
- 高いカスタマイズ性 - SASS/SCSS変数やBlueprints、コンポーネントごとのデフォルト設定など、細部まで調整できる仕組みが揃っています
- Viteサポートとツリーシェイキング - 使うコンポーネントだけを読み込む構成にすれば、バンドルサイズを抑えられます
- 国際化対応 - 42以上の言語に標準対応しており、日本語UIもそのまま組み込めます
インストール
新規プロジェクトなら、公式のスキャフォールディングツールを使うのが最も簡単です。
npm create vuetify
既存のVueプロジェクトに手動で組み込む場合は、パッケージをインストールしたうえで、プラグインとして登録します。
npm install vuetify
// src/plugins/vuetify.js
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
import 'vuetify/styles'
export default createVuetify({
components,
directives,
})
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify'
createApp(App).use(vuetify).mount('#app')
app.use(vuetify)でプラグインを登録しておけば、あとはどのコンポーネントからでも<v-btn>や<v-card>をそのままテンプレートに書けるようになります。
Vuetifyのサンプルを動かす
下のサンプルは、v-card・v-text-field・v-btn・v-alertというVuetifyのコンポーネントだけで組んだ問い合わせフォームです。名前を入力して「送信」ボタンを押すと、v-alertで完了メッセージが表示されます。CSSは1行も書いていません。
なお、このプレイグラウンドはビルドツールを使わずCDNから直接Vuetifyを読み込んでいる関係で、実際のアプリでよく使う<template>構文ではなく、Vueのh()関数(レンダー関数)でコンポーネントを組み立てています。書き方は違いますが、v-modelに相当するmodelValueとイベントハンドラのやり取りなど、中身の考え方は<template>で書く場合と同じです。要点を<template>構文で抜き出すと、次のようになります。
<script setup>
import { ref } from 'vue'
const name = ref('')
const submitted = ref(false)
</script>
<template>
<v-card>
<v-card-title>お問い合わせ</v-card-title>
<v-card-text>
<v-text-field v-model="name" label="お名前" variant="outlined" />
<v-btn color="primary" block @click="submitted = true">送信</v-btn>
<v-alert v-if="submitted" type="success" class="mt-4">
{{ name || 'ゲスト' }}さん、送信を受け付けました
</v-alert>
</v-card-text>
</v-card>
</template>
実際に動かせるのが下のサンプルです。名前を入力してから送信ボタンを押してみてください。
送信前に何も入力しなければ「ゲストさん、送信を受け付けました」と表示され、名前を入れてから送信すればその名前がそのまま表示に反映されます。v-text-fieldのvariantを"outlined"から"solo"に変えたり、v-btnのcolorを"primary"から"error"に変えたりすると、Material Designのスタイルがコンポーネントのpropだけで切り替わる様子が確認できます。
基本的な使い方
Vuetifyのプラグインを登録してしまえば、あとはコンポーネントをテンプレートに並べるだけです。ボタンひとつとっても、colorやvariant、sizeといったpropを変えるだけで見た目のバリエーションを作れます。
<template>
<v-btn color="primary">Primary</v-btn>
<v-btn color="secondary" variant="outlined">Outlined</v-btn>
<v-btn color="error" variant="text">Text</v-btn>
<v-btn color="success" size="large" rounded>Large & Rounded</v-btn>
</template>
クラス名を組み合わせてスタイルを作り込む必要がなく、propの値を変えるだけで一貫したデザインのバリエーションを表現できるのがVuetifyの基本的な使い方です。
実践的なユースケース
Vuetifyのv-formとrulesで入力バリデーションを行う
会員登録や問い合わせフォームでは、送信前に入力内容をチェックしたい場面が必ず出てきます。Vuetifyではv-formが持つvalidate()メソッドと、各入力コンポーネントのrulespropを組み合わせるだけで、バリデーションの仕組みを自前で書かずに済みます。要点を抜き出すと次のようになります。
<script setup>
import { ref } from 'vue'
const formRef = ref()
const email = ref('taro@example.com')
const rules = [
(v) => !!v || '必須項目です',
(v) => /.+@.+\..+/.test(v) || 'メール形式が正しくありません',
]
async function check() {
const { valid } = await formRef.value.validate()
console.log(valid ? '送信できます' : 'エラーがあります')
}
</script>
<template>
<v-form ref="formRef">
<v-text-field v-model="email" label="メールアドレス" :rules="rules" />
<v-btn color="primary" @click="check">バリデーション実行</v-btn>
</v-form>
</template>
実際に動かして確認できるのが下のサンプルです。メールアドレスを空にしたり、@を含まない文字列にしてから「バリデーション実行」を押してみてください。
rulesには「値を受け取ってtrueかエラーメッセージ文字列を返す関数」を並べるだけでよく、複数のルールを配列にすればそのまま複合バリデーションになります。ルール関数を追加・削除すれば、チェックする条件をその場で増減できます。
Vuetifyのuse-themeでライト/ダークテーマを切り替える
管理画面や社内ツールでは、ユーザーがライト/ダークテーマを自由に切り替えられると使い勝手が上がります。VuetifyはcreateVuetify()にテーマ定義を渡しておくだけで複数のテーマを管理でき、useTheme()コンポーザブルから現在のテーマ名を読み書きできます。骨格は次のとおりです。
<script setup>
import { useTheme } from 'vuetify'
const theme = useTheme()
function toggle() {
theme.global.name.value = theme.global.current.value.dark ? 'light' : 'dark'
}
</script>
<template>
<v-btn @click="toggle">ライト / ダークを切り替え</v-btn>
</template>
実際にボタンでテーマを切り替えられるのが下のサンプルです。
theme.global.name.valueに'dark'もしくは'light'という文字列を代入するだけで、v-cardやv-btnを含む画面全体の配色が即座に切り替わります。個別のコンポーネントにdarkpropを一つずつ設定して回る必要がない点が、Vuetifyのテーマシステムの強みです。
Vuetifyのv-data-tableで検索付きの一覧表示を作る
一覧画面と検索ボックスの組み合わせは、管理画面では定番の要件です。Vuetifyのv-data-tableはheadersとitemsを渡すだけで表組みを描画でき、searchpropに文字列を渡すと自動で絞り込みが行われます。要点は次のとおりです。
<script setup>
import { ref } from 'vue'
const search = ref('')
const headers = [
{ title: '名前', key: 'name' },
{ title: '部署', key: 'dept' },
{ title: '評価', key: 'score' },
]
const items = ref([
{ name: '田中', dept: '営業', score: 82 },
{ name: '鈴木', dept: '開発', score: 91 },
])
</script>
<template>
<v-text-field v-model="search" label="検索" density="compact" />
<v-data-table :headers="headers" :items="items" :search="search" />
</template>
実際に検索ボックスへ入力して絞り込みを試せるのが下のサンプルです。
検索ボックスに「開発」と入力すると、部署が開発の行だけに絞り込まれます。headersに列を1つ追加してitems側にも同じキーでデータを足せば、ソートやページネーションも含めて自動的に対応する列が増える点がv-data-tableの便利なところです。
まとめ
Vuetifyは、ボタンやカードといった基本部品から、フォームバリデーション、テーマ切り替え、検索付きデータテーブルまで、管理画面でよく使う機能をMaterial Designベースのコンポーネントとしてまとめて提供してくれるフレームワークです。CSSを書き込む代わりにpropを調整するだけで見た目を作り込める分、UIの実装に割く時間を大きく減らせます。
今回紹介したのはv-card・v-form・v-data-tableといったごく一部のコンポーネントです。ダイアログやナビゲーションドロワー、日付ピッカーなど他にも多くのコンポーネントが揃っているので、公式ドキュメントのコンポーネント一覧を眺めながら、実際のプロジェクトに組み込んでみることをおすすめします。