Skip to main content

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:

terminal
npx nuxi module add @lupinum/ginko-content@1.0.0-beta.10

Or install and register it yourself:

terminal
pnpm add @lupinum/ginko-content@1.0.0-beta.10 zod
nuxt.config.ts
export 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.

content.config.ts
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

content/index.md
---
title: Welcome
description: The home page for this content site.
---

# Welcome

This page comes from `content/index.md`.

[Read the guide](/guide)
content/guide.md
---
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.

app/pages/[...slug].vue
<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>, not page.body. The renderer uses the document's collection, locale, and resolved references while rendering Markdown. Passing only the body drops that context.

Run it

terminal
pnpm dev

Open 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.