Quickstart
Go from an empty Nuxt 4 app to Markdown rendering at its own URL in about five minutes.
Start with an empty Nuxt 4 app. In about five minutes, you will render content/index.md at / and a second Markdown file at /guide.
Install
Add Ginko with the Nuxt CLI:
npx nuxi module add @lupinum/ginko-content@1.0.0-beta.10Or install and register it yourself:
pnpm add @lupinum/ginko-content@1.0.0-beta.10 zodexport default defineNuxtConfig({
modules: ['@lupinum/ginko-content']
})The current package supports Nuxt 4.5.1 through Nuxt 4.x, Vue 3.5.40 through Vue 3.x, and Node.js 22.18–22.x, 24.11–24.x, or 26+.
Use a coding agent
A coding agent is a development tool that can inspect and change your project.
After installation, copy this prompt into your agent:
Add Ginko Content to this Nuxt application and render a Markdown page.
Read the project's existing instructions first. Resolve
@lupinum/ginko-content/agent-docs from this application's directory and read
its starting pages. Use the installed version's examples and public types.
Preserve existing routes, conventions, and AGENTS.md instructions. Add or
update one short Ginko Content pointer in AGENTS.md if the project allows it;
do not duplicate the documentation. If the file is absent, create only that
pointer. Report missing guidance. Verify the page, links, and a missing route.If the installed package has no agent-docs export, read its packaged README
and types. Use documentation from the matching source tag when more detail is
needed. Installing or updating the package does not edit project instructions.
The pointer resolves the installed package, so upgrades and rollbacks select
the matching documentation without copying it into your application.
Declare a collection
Create content.config.ts at the app root. Export the pages handle so components can import the collection instead of referring to it by a string name.
import { defineCollection, defineContentConfig } from '@lupinum/ginko-content/config'
import { z } from 'zod'
export const pages = defineCollection({
type: 'page',
source: '**/*.md',
schema: z.object({
title: z.string(),
description: z.string().optional()
})
})
export default defineContentConfig({
collections: { pages }
})Add two Markdown files
---
title: Welcome
description: The home page for this content site.
---
# Welcome
This page comes from `content/index.md`.
[Read the guide](/guide)---
title: Guide
description: A second route backed by Markdown.
---
# Guide
Both pages belong to the same typed collection.index.md resolves to / and guide.md resolves to /guide.
Render them at their URL
Use one catch-all route for the collection. useContentPage resolves the current URL; the page component owns the 404, metadata, and rendering policy.
<script setup lang="ts">
import { createError, useSeoMeta } from '#imports'
import { pages } from '~~/content.config'
definePageMeta({ key: route => route.path })
const { page, status } = await useContentPage(pages)
if (status.value === 'not-found') {
throw createError({
statusCode: 404,
statusMessage: 'Page not found',
fatal: true
})
}
useSeoMeta({
title: () => page.value?.title,
description: () => page.value?.description
})
</script>
<template>
<main v-if="page">
<ContentRenderer :value="page" />
</main>
</template>Pass the whole document to<ContentRenderer>, notpage.body. The renderer uses the document's collection, locale, and resolved references while rendering Markdown. Passing only the body drops that context.
Run it
pnpm devOpen http://localhost:3000/ and /guide to see rendered Markdown, and /missing to see your 404.
Add another Markdown file matched by the pages collection and Ginko exposes it at the corresponding route.