導入
ViteなどのReactアプリにgenko.meのSDKを入れ、記事一覧と記事詳細をブラウザから表示する手順。
Viteで作ったReactアプリを例に、記事一覧と記事詳細を表示するまでの手順です。React RouterやCRA系の構成でも、環境変数の読み方以外は同じです。
ワークスペースの作成とブログAPIの用意はクイックスタートの手順1〜3にまとめています。
用意するもの
- genko.meのアカウントとワークスペース。APIの作成とAPIキーの発行にはオーナー権限が必要です。
- 記事が1件以上、保存済みかつ公開状態で入っているAPI。下書きのままでは配信APIから取得できません。
- React 19のアプリと、バンドラー(Viteなど)。
@genko-me/reactはReact 19をpeerDependencyに指定しています。 - ビルドに使うNode.js 20.12以降。
以下のyour-workspaceは自分のワークスペースIDに、blogは自分のエンドポイントに置き換えます。
インストール
npm install @genko-me/sdk @genko-me/react@genko-me/reactは本文を描画するRichTextとシェアボタンのためのパッケージです。取得だけなら@genko-me/sdkのみで足ります。
APIキーとワークスペースIDを設定する
APIキーは管理画面の「設定・プラン」→「APIキー」(/w/your-workspace/settings/api-keys)で発行します。「選択したAPIのみ」で、ブラウザに出しても構わないAPIだけに絞ってください。この構成ではキーがバンドルに入り、誰でも読めます。
ViteではVITE_で始まる環境変数だけがimport.meta.envからブラウザ側のコードに渡ります。
VITE_GENKO_WORKSPACE=your-workspaceVITE_GENKO_API_KEY=発行したキーAPIの定義と型を書きます。フィールドIDと種類は管理画面のスキーマに合わせてください。
import { listApi } from "@genko-me/sdk";
export type Blog = { title: string; body: string };
export const apis = { blog: listApi<Blog>({ fields: [ { id: "title", type: "text" }, { id: "body", type: "richtext" }, ], }),};接続するクライアントを1か所に作ります。
import { createGenko } from "@genko-me/sdk";import { apis } from "./schema";
export const genko = createGenko({ workspace: import.meta.env.VITE_GENKO_WORKSPACE, apiKey: import.meta.env.VITE_GENKO_API_KEY, apis,});
export const client = genko.apis;@genko-me/cliのinit / pullでschema.tsを管理画面から生成することもできます。ただし生成されるclient.tsはprocess.env.GENKO_API_KEYを読むので、Viteでは上のようにimport.meta.envへ書き換えてください。@genko-me/reactは、依存にnextがあるプロジェクトにしか追加されません。
記事一覧を出す
取得はuseEffectで行い、結果を状態に入れます。読み込み中・失敗・0件の3つを必ず用意してください。
import { useEffect, useState } from "react";import type { GenkoEntryMeta } from "@genko-me/sdk";import { client } from "../lib/genko";import type { Blog } from "../lib/schema";
export function BlogList() { const [posts, setPosts] = useState<(Blog & GenkoEntryMeta)[] | null>(null); const [failed, setFailed] = useState(false);
useEffect(() => { let active = true; client.blog .list({ limit: 10, orders: "-publishedAt" }) .then((result) => { if (active) setPosts(result.contents); }) .catch(() => { if (active) setFailed(true); }); return () => { active = false; }; }, []);
if (failed) return <p>記事を取得できませんでした。</p>; if (!posts) return <p>読み込み中…</p>; if (posts.length === 0) return <p>公開されている記事はありません。</p>;
return ( <ul> {posts.map((post) => ( <li key={post.id}> <a href={`/blog/${post.id}`}>{post.title}</a> </li> ))} </ul> );}list()はcontentsとtotalCountを返します。totalCountは条件に合う全件数、contents.lengthは今回取得した件数です。並び替えと絞り込みの書き方は並び替え・絞り込み・検索を参照してください。
useEffectは開発時のStrict Modeで2回実行されます。上の例のように後片付けでactiveを落とすと、古いレスポンスで状態を上書きしません。
記事詳細を出す
リッチテキストの本文はRichTextに渡します。HTML文字列をそのままJSXの波括弧に入れると、タグが文字として表示されます。
import { useEffect, useState } from "react";import { RichText } from "@genko-me/react";import { ShareButtons } from "@genko-me/react/share-buttons";import type { GenkoEntryMeta } from "@genko-me/sdk";import { client } from "../lib/genko";import type { Blog } from "../lib/schema";
export function BlogPost({ id }: { id: string }) { const [post, setPost] = useState<(Blog & GenkoEntryMeta) | null | undefined>(undefined);
useEffect(() => { let active = true; client.blog .getOrNull(id) .then((result) => { if (active) setPost(result); }) .catch(() => { if (active) setPost(null); }); return () => { active = false; }; }, [id]);
if (post === undefined) return <p>読み込み中…</p>; if (post === null) return <p>記事が見つかりません。</p>;
return ( <article> <h1>{post.title}</h1> <RichText html={post.body} as="div" className="article-body" /> <ShareButtons url={`https://your-site.example/blog/${post.id}`} title={post.title} /> </article> );}getOrNull()は404のときだけnullを返します。認証エラーやサーバーエラーは例外のままなので、catchで分けて扱ってください。
本文の見出し・段落の間隔・画像の最大幅は、サイト側のCSSで指定します。
.article-body { line-height: 1.9; }.article-body h2 { margin-block: 2em 0.75em; font-size: 1.5em; }.article-body p { margin-block: 1em; }.article-body img { max-width: 100%; height: auto; }ShareButtonsのurlには、配信APIのURLではなくサイト側の公開記事URLを渡します。propsとCSS変数は本文・シェアの補助部品にまとめています。
表示されないとき
| 状況 | 確認すること |
|---|---|
| 一覧が空 | 記事が保存済みで「公開」か。公開終了日時を過ぎていないか |
401 | APIキーが有効期間内か。VITE_で始まる名前にしているか。開発サーバーを再起動したか |
403 | APIキーを発行したワークスペースと接続先が同じか |
404 | ワークスペースIDとエンドポイントが正しいか。キーでそのAPIを許可しているか |
| 本文がタグごと表示される | RichTextに渡さず{post.body}と書いていないか |
次は実践で、ページ送りと下書きプレビューを組み立てます。
最終更新: 2026年9月17日