npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@genko-me/vue

v0.5.5

Published

genko.me配信API向け公式Vue 3コンポーネント集

Readme

@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.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 route = useRoute();
const { data } = await useFetch('/api/blog', { query: route.query });
</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>
  <Error v-else template />
</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.blog,
  id: getRouterParam(event, 'id'),
  searchParams: getQuery(event) as Record<string, string | string[] | undefined>,
}));
<script setup lang="ts">
import { EntryProvider, ViewItem, PreviewNotice, Error } from '@genko-me/vue';

const route = useRoute();
const { data } = await useFetch(`/api/blog/${route.params.id}`, { query: route.query });
useHead(() => data.value?.ok && data.value.draft ? {
  meta: [{ name: 'robots', content: 'noindex, nofollow' }, { name: 'referrer', content: 'no-referrer' }],
} : {});
</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-if="data?.ok">記事がありません。</p>
  <Error v-else template />
</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: client.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と同じです。