Get started
Add @genko-me/wc to plain HTML with an import map, then show a list and a single entry.
This guide shows a list and a single entry using only an .html file and <script type="module">. It does not use a bundler.
Steps 1–3 of the quickstart cover creating a workspace and blog API.
Prerequisites
- A genko.me account and workspace. You need the Owner role to create an API and issue an API key.
- An API with at least one saved and published entry.
- Node.js. You need it for
npm installand to servenode_moduleslocally, not to run the browser. - A browser that supports import maps (current Chrome, Edge, Firefox, and Safari all do).
Replace your-workspace with your workspace ID and blog with your endpoint in the examples below.
Install
npm install @genko-me/wc @genko-me/sdk@genko-me/wc also depends on @genko-me/core, which npm install installs automatically.
A browser cannot resolve import "@genko-me/wc" without a bundler. It does not know how to resolve package names in node_modules. Add an import map to the HTML <head> to map package names to files.
<script type="importmap">{ "imports": { "@genko-me/wc/define": "/node_modules/@genko-me/wc/dist/define.js", "@genko-me/wc": "/node_modules/@genko-me/wc/dist/index.js", "@genko-me/sdk": "/node_modules/@genko-me/sdk/dist/index.js", "@genko-me/core": "/node_modules/@genko-me/core/dist/index.js", "@genko-me/core/browser": "/node_modules/@genko-me/core/dist/browser.js" }}</script>@genko-me/wc internally imports @genko-me/core and @genko-me/core/browser, so map those too even if your code does not import them directly. During development, serve the directory containing index.html and node_modules with a static server such as npx serve. For production, copy only the required dist/*.js files into a directory such as vendor/ and update the import map paths, instead of deploying all of node_modules.
Configure an API key and workspace ID
Issue an API key in Settings and plan → API keys (/w/your-workspace/settings/api-keys). Scope it to blog under Selected APIs only.
This setup puts the API key directly in HTML and JavaScript. Anyone who opens the page can read it. There is no way to hide it (see the overview).
Show a list
<body> <main> <genko-list id="posts" href="/blog/{id}"> <article> <h2><genko-list-item field="title"></genko-list-item></h2> </article> <genko-loading template>Loading…</genko-loading> <genko-error template></genko-error> <genko-null>No entries yet.</genko-null> </genko-list> </main>
<script type="module"> import '@genko-me/wc/define'; import { createGenko, listApi, getApiBinding } from '@genko-me/sdk';
const genko = createGenko({ workspace: 'your-workspace', apiKey: 'your-issued-key', apis: { blog: listApi({ fields: [ { id: 'title', type: 'text' }, { id: 'body', type: 'richtext' }, ], }), }, }); const binding = getApiBinding(genko.apis.blog);
const list = document.getElementById('posts'); list.fields = binding.fields; list.schemas = binding.schemas; genko.apis.blog .list({ limit: 6, orders: '-publishedAt' }) .then((initial) => { list.initial = initial; }); </script></body>Importing @genko-me/wc/define registers the genko-* elements. Importing only @genko-me/wc does not; that entry point exports their classes.
genko-list shows genko-loading until it receives initial (the first page). Pass fields and schemas from getApiBinding() unchanged. Set field markup with the field attribute. The id attribute also works, but field avoids repeating the same ID in each list row. Add pagination in the tutorial.
Serve this directory with npx serve or a similar tool and open it in a browser. Check that the list appears and the developer-tools console has no errors.
Show a single entry
<body> <main> <genko-view id="detail"> <h1><genko-view-item field="title"></genko-view-item></h1> <genko-view-item field="body"></genko-view-item> <genko-loading template></genko-loading> <genko-error template></genko-error> <genko-null>Entry not found.</genko-null> </genko-view> <genko-share-buttons id="share" hidden></genko-share-buttons> </main>
<script type="module"> import '@genko-me/wc/define'; import { createGenko, listApi, getApiBinding } from '@genko-me/sdk';
const genko = createGenko({ workspace: 'your-workspace', apiKey: 'your-issued-key', apis: { blog: listApi({ fields: [{ id: 'title', type: 'text' }, { id: 'body', type: 'richtext' }] }) }, }); const binding = getApiBinding(genko.apis.blog);
const id = new URLSearchParams(location.search).get('id'); const detail = document.getElementById('detail'); const share = document.getElementById('share'); detail.fields = binding.fields; detail.schemas = binding.schemas;
genko.apis.blog .getOrNull(id) .then((entry) => { detail.removeAttribute('state'); if (!entry) detail.setAttribute('state', 'empty'); detail.entry = entry ?? undefined; if (entry) { share.url = location.href; share.shareTitle = entry.title; share.hidden = false; } }) .catch(() => detail.setAttribute('state', 'error')); </script></body>Do not set the state attribute permanently to loading or another value. With no state, the element automatically shows a loading state before entry arrives and the body afterward. Once you set state, it does not remove itself. As above, call removeAttribute('state') when results arrive, then set only the state you need.
The heading attribute for genko-share-buttons is share-title. HTML reserves title for other uses, including tooltips.
Open post.html?id= followed by an entry ID from the list. Check that the body and share buttons appear.
Troubleshooting
| Symptom | Check |
|---|---|
| Nothing appears | Did you import @genko-me/wc/define? Does the import map match the actual node_modules paths? Check the console for module-resolution errors |
| Empty list | Is the entry saved and published? |
401 | Is the API key still valid? |
403 | Does the key belong to the workspace you are connecting to? |
| Missing fields | Does the field attribute match the dashboard field ID? Did you pass fields and schemas to the element? |
Next, the tutorial covers pagination, draft previews, and reflecting content updates.
Last updated: October 6, 2026