Get started
Install @genko-me/vue in Nuxt, fetch through server routes, and render a list and a single entry with SSR.
This guide connects genko.me to a Nuxt app and renders list and detail pages on the server.
To create a workspace and blog API, follow steps 1–3 of the quickstart.
Prerequisites
- A genko.me account and workspace. You need Owner access to create an API and issue an API key.
- An API with at least one saved, published entry. The delivery API does not return drafts by default.
- A Nuxt 4 project with Vue 3.5 or later.
@genko-me/vuelists Vue 3.5 as a peer dependency. - Node.js 20.12 or later and a package manager.
- A Nitro preset with a Node.js-compatible runtime, such as the default
node-server. Pagination signatures usenode:cryptodirectly and cannot run in runtimes without it, such as Cloudflare Workers.
Replace your-workspace with your workspace ID and blog with your endpoint in the examples below.
Install
npm install @genko-me/vue @genko-me/sdkThis page covers @genko-me/[email protected].
Prepare the schema and types
Generate types and API definitions from your dashboard schema. Run this at the project root.
npx @genko-me/cli initThe CLI detects the output location as if this were Next.js. Nuxt 4 uses app/ by default, which the CLI cannot distinguish from Next.js App Router's app/. It writes to the project root's lib/. Move the files to app/lib/ and update schemaOutput and clientOutput in genko.config.json.
{ "schemaOutput": "app/lib/schema.ts", "clientOutput": "app/lib/client.ts"}After moving them, genko pull uses that configuration. Nuxt's ~ alias points to app/ (srcDir), letting server routes and pages use the same imports.
If you do not use the CLI, write app/lib/schema.ts yourself. Match field IDs and types to the dashboard schema.
import { listApi } from "@genko-me/sdk";
export type Blog = { title: string; body: string; publishedAt: string };
export const apis = { blog: listApi<Blog>({ fields: [ { id: "title", type: "text" }, { id: "body", type: "richtext" }, { id: "publishedAt", type: "datetime" }, ], }),};Set the API key and workspace ID
Issue an API key in the dashboard under Settings and plan → API keys (/w/your-workspace/settings/api-keys). Give it a descriptive name and expiry. If you choose “Selected APIs only,” allow blog.
You can use the generated app/lib/client.ts as-is. It reads process.env.GENKO_API_KEY.
import { createGenko } from "@genko-me/sdk";import { apis } from "./schema";
export const genko = createGenko({ workspace: "your-workspace", apiKey: process.env.GENKO_API_KEY!, apis,});export const client = genko.apis;GENKO_API_KEY=your-issued-keyKeep the environment variable named GENKO_API_KEY. Pagination signing and verification in @genko-me/vue/server read process.env.GENKO_API_KEY directly, without going through client.ts. If apiKey uses a different value, pagination fails its configuration check.
Make sure Git does not track .env. Nuxt reads it automatically in development. Set the same variable in your hosting environment for production.
Show a list
Fetch in a route under server/. The return value of fetchGenkoList can be passed directly as props to ListClient.
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", "publishedAt"], searchParams: getQuery(event) as Record<string, string | string[] | undefined>,}));List the IDs displayed by ListItem in fields. React can inspect children to infer fields; Vue cannot, so you must specify them here. Omitting fields fetches everything. An ID missing from the schema causes a configuration error. {id} in href is included automatically.
<script setup lang="ts">const { data } = await useFetch("/api/blog");</script>
<template> <main> <h1>Blog</h1> <ListClient v-if="data?.ok" :initial="data.initial" :initial-page="data.initialPage" :fields="data.fields" :schemas="data.schemas" href="/blog/{id}"> <article> <h2><ListItem id="title" /></h2> <time><ListItem id="publishedAt" format="yyyy/MM/dd" time-zone="Asia/Tokyo" /></time> </article> <template #empty><Null>No published entries yet.</Null></template> </ListClient> <Error v-else template /> </main></template>You can use registered names such as ListItem, Null, and Error directly in templates after registering GenkoVuePlugin. No import is needed for template use. Without the plugin, import them from @genko-me/vue yourself.
import { GenkoVuePlugin } from "@genko-me/vue";
export default defineNuxtPlugin(nuxtApp => { nuxtApp.vueApp.use(GenkoVuePlugin); });The default ListClient slot defines one item's layout. The named pagination, loading, error, and empty slots are not repeated. If you omit a state slot, nothing appears for that state. template shows a default message.
Start the development server and open /blog. Seeing entries confirms the connection.
Show a single entry
import { fetchGenkoEntry } from "@genko-me/vue/server";import { client } from "~/lib/client";
export default defineEventHandler(event => fetchGenkoEntry({ api: client.blog, id: getRouterParam(event, "id"), fields: ["title", "body"], searchParams: getQuery(event) as Record<string, string | string[] | undefined>,}));<script setup lang="ts">const route = useRoute();const { data } = await useFetch(`/api/blog/${route.params.id}`, { query: route.query });</script>
<template> <main> <EntryProvider v-if="data?.ok && data.entry" :entry="data.entry" :fields="data.fields" :schemas="data.schemas"> <h1><ViewItem id="title" /></h1> <ViewItem id="body" /> </EntryProvider> <p v-else>No entry found.</p> </main></template>Always pass the page's query (route.query) to useFetch. /api/blog/... is a separate URL, so opening /blog/xxx?draftKey=... does not automatically forward draftKey. Without it, draft preview always shows the published result. The same applies to URL pagination in the tutorial.
fetchGenkoEntry returns entry: null for a 404 without throwing (data.ok remains true). data.ok is false only if fetching or configuration fails.
Troubleshooting
| Symptom | Check |
|---|---|
| Empty list | Is an entry saved and Published? Has its publication end time passed? |
data.ok is false | Look for a [genko.me] line in the server log. It includes configuration errors or HTTP status codes. |
401 | Is the key valid, and can the server process read GENKO_API_KEY? |
403 | Does the key belong to the workspace you are connecting to? |
404 | Are the workspace ID and blog endpoint correct, and does the key allow blog? |
Cannot find ~/lib/client | The CLI wrote to lib/, but ~ points to app/lib/. Did you move the files as described above? |
| Drafts or pagination do not work | Did you pass { query: route.query } to the page's useFetch? |
Next, use the tutorial to add pagination and draft previews.
Last updated: October 16, 2026