

はじめに
はじめまして!開発部 横断チーム の足立です!
株式会社ビザスクでは Vue.js でフロントエンドの実装が行われております。
たくさんの内製の Atom や Molecules 単位のコンポーネントを活用し画面を実装してますが、
それらを最新の状態でレンダリングして一望できるようにしたかったので今回は Storybook…
ではなくあえて https://histoire.dev を手元で試してみました。
(Vue.js に特化していて比較的導入コストが低いとのこと)
その際に dev (ホットリロードプレビュー) や build preview (ビルドしたものの閲覧) の周りの設定で少しつまずいたり、
Story ファイルの作成を GitHub Copilot Agent に任せてみたので、こちらの記事としてまとめてみました 📝
ご参考になれば幸いです! 🥳
ビザスクは Vue Fes Japan 2025 に今年もスポンサーとして参加します!
ご参加される方はぜひ、ビザスクブースで Vue.js について語り合いましょう! 🙌
別のチームで Storybook を使った事例もあるのでよければこちらもご確認ください!
tech.visasq.com
前提
- Vue.js を使ったプロジェクト ✅️
- vite を使ったプロジェクト ✅️
- Histoire のバージョンは
0.17.17✅️ - https://histoire.dev/guide/config.html (公式) に沿った設定を実施済 ✅️
- Story (
TargetComponent.story.vue) をsrc内に 1 個以上作成済 ✅️
npm run story:build が code: 'PLUGIN_ERROR', plugin: 'commonjs--resolver' で失敗する
対象の vite / vue プロジェクトの構成次第ですが上記のような vite 関連のエラーが生じる場合がございます。
そこで process.env.HISTOIRE の環境変数で vite の設定を動的に切り替えることで回避できました!
Before (例)
import { defineConfig } from 'vite'; export default defineConfig({ build: { rollupOptions: { input: { main: path.resolve(__dirname, 'src/main.ts'), }, }, }, });
After (例)
import { defineConfig } from 'vite'; export default defineConfig({ build: { rollupOptions: { input: process.env.HISTOIRE ? undefined : { main: path.resolve(__dirname, 'src/main.ts'), }, }, }, }, });
npm run story:build の成果物が見当たらない
デフォルトでは .histoire というディレクトリが作られそこにビルドの成果物が出力されるのですが、見当たらない場合は histoire.config.ts 上で outDir の明記をご検討ください!(指定した path への出力がされると思います)
import { defineConfig } from 'histoire'; import { HstVue } from '@histoire/plugin-vue'; import * as path from 'path'; import * as fs from 'fs'; const outDir = path.resolve(__dirname, 'dist', '.histoire'); export default defineConfig({ plugins: [HstVue()], setupFile: 'histoire.setup.ts', outDir, });
npm run story:preview で URL に謎の . が入る
Preview server listening on http://localhost:6006./
とターミナルに表示される場合があるかと思います。. の部分は vite の設定 (例 vite.config.ts 等) の base プロパティを参照しているようなのでこちらも process.env.HISTOIRE で解決できます!
import { defineConfig } from 'vite'; export default defineConfig({ base: process.env.HISTOIRE ? undefined : './', });
npm run story:dev で 画像や fontawesome のアイコンが表示されない
MPA 構成などの場合、対象の Web プロダクト上で想定されている静的リソースへのパスと、Histoire の dev サーバ上でのパスが異なり、うまく読み込めないことがあります。
今回は静的リソースへのリクエストを利用可能なリソースへリダイレクトさせるだけのシンプルな vite プラグインを書き足すことで解決できました!
import { defineConfig } from 'histoire'; import { HstVue } from '@histoire/plugin-vue'; import * as path from 'path'; import * as fs from 'fs'; const outDir = path.resolve(__dirname, 'dist', '.histoire'); export default defineConfig({ plugins: [HstVue()], setupFile: 'histoire.setup.ts', outDir, vite: { plugins: [ { name: 'histoire-asset-redirects', configureServer(server) { server.middlewares.use('/dashboard/v3/assets/', (req, res) => { const currentPath = req.url || ''; const newUrl = `http://${req.headers.host}/${currentPath}`; res.writeHead(301, { Location: newUrl }); res.end(); }); }, }, { }, ], }, });
npm run story:preview で画像が表示されない
こちらも場合によっては Histoire の static サーバで閲覧した際にパスが異なり、上手く画像が表示できません。
今回はやや力技ですが、ビルドされた直後に成果物内のパスを利用可能なパスへ置換するプラグインを書くことで解決できました 💪
import { defineConfig } from 'histoire'; import { HstVue } from '@histoire/plugin-vue'; import * as path from 'path'; import * as fs from 'fs'; const outDir = path.resolve(__dirname, 'dist', '.histoire'); export default defineConfig({ plugins: [HstVue()], setupFile: 'histoire.setup.ts', outDir, vite: { plugins: [ { }, { name: 'css-path-replace', writeBundle(options, _bundle) { const outputDir = options.dir || outDir; const assetsDir = path.join(outputDir, 'assets'); if (!fs.existsSync(assetsDir)) { return; } const files = fs.readdirSync(assetsDir); const cssFiles = files.filter(file => file.startsWith('style-') && file.endsWith('.css')); for (const cssFile of cssFiles) { const cssFilePath = path.join(assetsDir, cssFile); if (!fs.existsSync(cssFilePath)) { continue; } let content = fs.readFileSync(cssFilePath, 'utf-8'); content = content.replace( /\/dashboard\/v3\/assets\/img\/common\/arrow_bottom\.png/g, '/img/common/arrow_bottom.png', ); fs.writeFileSync(cssFilePath, content, 'utf-8'); } }, }, { }, ], }, });
npm run story:preview で fontawesome のアイコンが表示されない
こちらも場合によっては Histoire の static サーバで閲覧した際にパスが異なり、上手く画像が表示できません。
今回は対象の実装における fontawesomeの scss ファイルにて以下のように $fa-font-path という scss 変数が使われているため…
// src/scss/fontawesome/_variables.scss (例) // Variables // -------------------------- $fa-font-path: '/dashboard/v3/assets/fonts' !default; $fa-font-size-base: 14px !default; $fa-line-height-base: 1 !default;
preprocessorOptions を使いビルド時に対象の scss 変数を上書きする設定を書き足すことで解決できました!
import { defineConfig } from 'histoire'; import { HstVue } from '@histoire/plugin-vue'; import * as path from 'path'; import * as fs from 'fs'; const outDir = path.resolve(__dirname, 'dist', '.histoire'); export default defineConfig({ plugins: [HstVue()], setupFile: 'histoire.setup.ts', outDir, vite: { css: { preprocessorOptions: { scss: { additionalData: ` // Histoire 用に変数上書き $fa-font-path: '/fonts'; `, }, }, }, plugins: [ ], }, });
Atom や Molecules コンポーネントが多すぎて全部の Story を書くのが大変
対象となるコンポーネントは約 40 個ありこれらの Story を書き上げるのは大変です!🔥
今回は 1 個だけ参考となる Story を書いた上で GitHub Copilot Agent に以下のようなプロンプトを渡すことで、仕事の合間に 1 日で書き上げることができました 🤖 💪
- AtomXXX.story.vue を参考にする - Layout は grid 200~300px - 最初に Interactive な Variant - init-state には型を意識し無名関数を用いて state を指定 - click が必要なときは Histoire の logEvent を使う - 1 Variant 内に 1個 の 対象コンポーネントまで - Variant は v-for などで効率的に書く - docs の最後は Figma: todo:url と書く - style タグや div タグは書き足さない style 属性も使わない 以上を踏まえ、 atoms と molecules ディレクトリ内のコンポーネントの Story を作成してください
最初に Interactive な Variant
こちらの指示を入れることで、プルダウンで値を変えたりしリアルタイムで見た目を確認できる Variant を書いてくれるようになります。

click が必要なときは Histoire の logEvent を使う
console.log や window.alert でなく logEvent を使わせるようにすることで実際に操作した際に Events タブ上でどのような値が取り扱われているのか確認することができます。

docs の最後は Figma: todo:url と書く
Figma 上にデザインデータがあるコンポーネントの場合は対象の Figma の URL を掲載したいのでこのように指示してみました ✒️


今後やってみたいこと
- GitHub Pages や Cloudflare Pages で前述のコマンド不要ですぐ Histoire を閲覧できる状態にしてみたいです
- Devin などで atoms ディレクトリなどに更新があった際に自動で Story の追加・更新・削除を行う PR を立ててくれる仕組みとかも作っていきたいです
ビザスクでは様々なAI開発ツールをご利用いただけます
最後に
以上、Histoire 設定のつまずきポイントと対処方法(+ GitHub Copilot Agent の活用)でした!
これまではコンポーネントの見た目を確認するために以下のように一手間二手間必要でしたが…
これらの手間がなくなり、見た目を確認したり、検索したり、 props をプルダウンやインプットボックスでリアルタイムで調整し見た目を確認したり、レンダリングしながらドキュメントも読めたりできる環境を手に入れることができました!
今のところ独り占めした状態なので社内でニーズがあれば前述のように社内向けで何処かにホスティングできたらと思います 💪

今回はフロントエンドエンジニア向けなブログですが、ビザスクではフロントエンドに限らずリーダー候補やプロダクトマネージャーなど幅広くエンジニアやデザイナーの仲間を募集しています!
少しでもビザスク開発組織にご興味を持たれた方は、ぜひ一度カジュアルにお話ししましょう!

コメント