@genko-me/vue

genko.me配信API向け公式Vue 3コンポーネント集
1,093
Install
npm install @genko-me/vue
Latest Version:0.5.4
Vue:3
Last Updated:Oct 6, 2026
Links: npm
Maintainer: ichi_107

@genko-me/vue

Vue 3用の、genko.meのコンテンツを表示する部品です。Vue 3.5以降が対象で、Nuxtでも素のVite + Vue SSRでも、クライアントだけのSPAでも使えます。

部品名は@genko-me/reactと同じで、DOMも同じものを出します。乗り換えても同じ書き方が通るようにしてあります。

取得と表示は分かれています

Vueにはサーバーコンポーネントが無いので、React版の<List>/<View>のように「取得しながら描く」部品はありません。取得は@genko-me/vue/serverの関数、表示は@genko-me/vueの部品、という2段構えです。取得側の返り値がそのまま表示側のpropsになります。

@genko-me/vue/serverはAPIキーを扱うので、サーバー側(Nuxtのserver/、SSRのローダー、Route Handler)からだけ呼んでください。ブラウザ向けのバンドルには入りません。

一覧

// server/api/blog.get.ts (Nuxtの場合)
import { fetchGenkoList } from '@genko-me/vue/server';
import { client } from '~/lib/client';

export default defineEventHandler(event => fetchGenkoList({
  api: client.apis.blog,
  limit: 6,
  orders: '-publishedAt',
  href: '/blog/{id}',
  fields: ['title', 'image', 'publishedAt'],
  pagination: { mode: 'url' },
  searchParams: getQuery(event) as Record<string, string | string[] | undefined>,
}));
<script setup lang="ts">
import { ListClient, ListItem, Pagination, Error, Null, createPageLoader } from '@genko-me/vue';

const { data } = await useFetch('/api/blog');
</script>

<template>
  <ListClient
    v-if="data?.ok"
    :initial="data.initial"
    :initial-page="data.initialPage"
    :fields="data.fields"
    :schemas="data.schemas"
    href="/blog/{id}"
    :pagination-options="{ mode: 'url' }"
    :load-page="createPageLoader({ url: '/api/genko-page', token: data.paginationToken! })"
  >
    <article>
      <h2><ListItem id="title" /></h2>
      <ListItem id="image"><template #fallback><span>画像なし</span></template></ListItem>
    </article>

    <template #pagination><Pagination mode="url" /></template>
    <template #error><Error template /></template>
    <template #empty><Null>記事はまだありません。</Null></template>
  </ListClient>
</template>

ListClientの既定スロットが1件分のレイアウトです。pagination・loading・error・emptyは名前付きスロットで、繰り返しません。スロットを省略すると、その状態では何も表示しません。templateを指定したときだけ標準メッセージを表示します。

fetchGenkoListは失敗しても例外を投げず{ ok: false, message }を返します。詳細はサーバーのログにだけ出て、レスポンス本文・URL・APIキーがログに混ざることはありません。

詳細と下書き

// server/api/blog/[id].get.ts
import { fetchGenkoEntry } from '@genko-me/vue/server';
import { client } from '~/lib/client';

export default defineEventHandler(event => fetchGenkoEntry({
  api: client.apis.blog,
  id: getRouterParam(event, 'id'),
  searchParams: getQuery(event) as Record<string, string | string[] | undefined>,
}));
<script setup lang="ts">
import { EntryProvider, ViewItem, PreviewNotice } from '@genko-me/vue';

const route = useRoute();
const { data } = await useFetch(`/api/blog/${route.params.id}`);
</script>

<template>
  <EntryProvider v-if="data?.ok && data.entry" :entry="data.entry" :fields="data.fields" :schemas="data.schemas">
    <PreviewNotice v-if="data.draft" />
    <h1><ViewItem id="title" /></h1>
    <ViewItem id="body" />
  </EntryProvider>
  <p v-else>記事がありません。</p>
</template>

管理画面のプレビューURLを https://your-site.example/blog/{id}?draftKey={key} に設定します。searchParamsを渡すと保存済み下書きをキャッシュせず取得し、draft: trueが返ります。不正なキーで公開版へ切り替えることはありません。下書き表示のときは<meta name="robots" content="noindex, nofollow">をページ側で出してください(React版はサーバーコンポーネントの中で自動で出しますが、VueではページのuseHeadが担当します)。

ページ送り

Vue/Nuxtにはサーバーアクションが無いので、React版がServer Actionで担っていた部分は「自分のアプリの1本のAPIルート」に置き換わります。

// server/api/genko-page.post.ts
import { loadGenkoPage } from '@genko-me/vue/server';

export default defineEventHandler(async event => {
  const { token, page } = await readBody<{ token: string; page: number }>(event);
  return loadGenkoPage(token, page);
});

fetchGenkoListにpaginationを指定するとpaginationTokenが返ります。これはHMAC署名付きで、取得先・条件・有効期限(1時間)を固定したものです。ブラウザへ渡してもendpointやfilterを書き換えることはできません。APIキーはブラウザへ送られません。期限切れ・キー交換後はページを再読み込みしてください。ページ送りを使うクライアントのキーはGENKO_API_KEYと一致させてください。カスタムfetch/wrapRequestはページ送りには対応しません。

既定の<Pagination />はURLを変えず一覧だけを切り替えます。<Pagination mode="url" param="page" />とListClientの:pagination-options="{ mode: 'url' }"を揃えるとURLと連動し、戻る操作にも追従します。複数のURL連動一覧には別々のparamを指定してください。

多言語

fetchGenkoList と fetchGenkoEntry は lang、langFallback、include を受け取り、@genko-me/sdk へそのまま渡します。指定しなければリクエストは従来と同じです。include: ['_langs'] を指定すると、各項目の _langs({ current, available })で読める言語を取れます。

const result = await fetchGenkoList({ api: genko.apis.blog, lang: 'en', langFallback: true, include: ['_langs'], pagination: true });

pagination を使う場合、言語指定はpaginationTokenの署名に含まれます。ブラウザからlangを書き換えても検証で拒否されます。

fetchGenkoList が返す lang と hideLangs を ListClient に渡す(:lang="data.lang" :hide-langs="data.hideLangs")と、各項目のルート要素にHTMLの lang 属性が付きます。値は実際に返ってきた言語(_langs.current、無ければ指定した lang)で、langFallback で日本語の項目が混ざる一覧ならその項目だけ lang="ja" になります。lang を指定しなければ属性は付きません。

詳細は EntryProvider がDOMを出さないため、囲む要素は自分で書きます。fetchGenkoEntry が返す lang(実際に返った言語)をそこに付けてください。

<template>
  <article v-if="data?.ok && data.entry" :lang="data.lang">
    <EntryProvider :entry="data.entry" :fields="data.fields" :schemas="data.schemas">…</EntryProvider>
  </article>
</template>

langFallback では、返った言語を知るために include=_langs を内部で足します(lang と langFallback の両方を指定し、include に _langs が無いときだけ)。EntryProvider の配下や fetchGenkoEntry の entry には、自分で include: ['_langs'] を指定しない限り _langs は入りません。fetchGenkoList の initial は ListClient が項目ごとの言語を決めるために _langs を含みます。

デザインはサイト側で指定する

一覧・詳細・フィールド・ページ送り・状態表示は標準の装飾を付けません。class・styleと、見出しやdivなどのHTMLでサイトのデザインを適用します。本文の見出し・段落間隔・画像サイズもサイト側のCSSで設定してください。複数行テキストの改行を表示する場合はwhite-space: pre-wrapなどを指定します。

下書きプレビュー通知と終了ボタンだけは標準デザインを表示します。<PreviewNotice unstyled />で装飾を外せます。

記事全体のリンクには、クリック領域とキーボードフォーカスを成立させる最小限のCSSを使います。これは見た目のテーマとは別の、操作に必要な処理です。

表示の指定

  • 表示項目はListItem / ViewItemで明示します。テキスト・画像・HTMLなどの種類は生成スキーマから判断します。
  • class・styleを各部品に渡せます。styleはオブジェクトでも文字列でも受け付け、数値にはReact版と同じ規則でpxを補います。
  • 日時は<ListItem id="publishedAt" format="yyyy/MM/dd HH:mm" time-zone="Asia/Tokyo" />。既定のタイムゾーンはUTCです。
  • 参照はid="author.name"。複数参照は値を参照順で表示します。標準の参照展開は2階層までです。
  • 未入力の値は表示しません。<template #fallback>に代わりの表示を書けます。falseや0は未入力と扱いません。
  • リンクはListClientのhref="/blog/{id}"で記事全体に設定するか、内部に<ListLink href="/blog/{id}">読む</ListLink>を置きます。ネストしたリンクを壊さず操作できます。
  • fetchGenkoList / fetchGenkoEntryのfieldsは、実際に描くフィールドIDを並べると転送量を減らせます。省略すると絞り込みません。スキーマに無いIDを書くと取得前に弾かれます。

シェアボタンとリッチテキスト

<script setup lang="ts">
import { RichText, ShareButtons } from '@genko-me/vue';
</script>

<template>
  <RichText :html="article.body" as="div" class="prose" />
  <ShareButtons :url="url" :title="article.title" :services="['x', 'line', 'hatena', 'copy', 'share']" />
</template>

ShareButtonsの見た目はCSS変数(GENKO_DESIGN_TOKENS参照)で調整できます。unstyledにするとインラインスタイルを一切当てず、class・button-class・tooltip-classとdata-hovered / data-pressed / data-focus-visibleだけで独自のデザインを当てられます。React版はreact-aria-componentsを使っていますが、Vue版は素の<button>で組んであり、追加の依存はありません。

一括登録

// plugins/genko.ts (Nuxtの場合)
import { GenkoVuePlugin } from '@genko-me/vue';

export default defineNuxtPlugin(nuxtApp => { nuxtApp.vueApp.use(GenkoVuePlugin); });

<ListItem>のような素の名前と、衝突を避けたい場合の<GenkoListItem>の両方が登録されます。専用のNuxtモジュール(@genko-me/vue/nuxt)は用意していません。@nuxt/kitへの依存が増えるわりに、やることがこのプラグインと変わらないためです。

自分でマークアップを組む場合

useListStoreで一覧の状態機械(保留・失敗・世代による追い越しの破棄)だけを借りられます。破棄はコンポーネントのunmountに従うので、dispose()を自分で呼ぶ必要はありません。

import { useListStore } from '@genko-me/vue';

const { state, totalPages, go } = useListStore({ initial, initialPage, loadPage });

GENKO_ENTRY_KEY / GENKO_APPEARANCE_KEY / GENKO_PAGE_KEYもexportしてあるので、自前の一覧の中でも標準のListItemやPaginationをそのまま使えます。

スキーマ同期

npx @genko-me/cli pull
# 開発時の任意の自動同期(別ターミナル)
npx @genko-me/cli pull --watch

変更履歴

0.5.3

  • 追加: 共有ボタンに share(OSの共有シート。navigator.share)を追加しました。services を省略した場合の既定が x, line, hatena, copy, share になり、share は対応している環境でだけ、画面に表示されたあとに最後に現れます。対応していない環境(多くのPCブラウザ)では今までと同じ4つです。サーバー側の出力は変わりません。services を明示した場合は書いたものだけが並びます(share も、対応していなければ隠れます)。
  • 追加: 共有シートを閉じたときは何も起こしません。開けなかったときはリンクのコピーに切り替わり、コンソールに警告を1回出します。ラベルは日本語「その他の方法で共有」・英語 "Share via…" で、labels の share で差し替えられます。
  • 背景: iPhoneのXアプリでは、Xのシェア画面のURLがアプリ内ブラウザのログイン画面で開き、投稿画面に進めないことがあります。OSの共有シートならこの影響を受けず、ほかのアプリにも共有できます。

0.5.2

  • 変更: 共有ボタンの大きさを36×36pxから44×44pxにしました。指で押しやすい大きさ(タップ領域の目安)に合わせるためです。アイコンの既定サイズ(18px)は変わりません。unstyled の場合は従来どおり大きさを指定しません。

0.5.1

  • 追加: lang を指定すると、ListClient が各項目のルート要素にHTMLの lang 属性を付けます。値は実際に返ってきた言語(_langs.current、無ければ指定した lang)です。fetchGenkoList / fetchGenkoEntry は lang を返すので、詳細では囲む要素に自分で付けられます。
  • 変更: lang と langFallback を指定し、include に _langs が無い場合に限り、リクエストの include に _langs を内部で足します(include=_langs が付くのはこの場合だけです)。ページ送りのトークンも送る値と同じもので署名します。
  • lang を指定しない場合の出力とリクエストは0.5.0と同じです。

Version History

Version Released License
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0