Compatibility: any Web-standard runtime — Next.js App Router (Edge or Node),
Remix, Hono, Deno. Node >=18. Zero runtime dependencies.
All exports are available from the package root:
import {
generateLlmsText,
generateLlmsFullText,
createMarkdownRoute,
} from "@vllnt/next-llms";Render an llms.txt index manifest from a config object, following the
llms.txt specification.
function generateLlmsText(config: LlmsConfig): string;Output layout (blocks joined by a blank line, no trailing newline):
# {title}— required H1.> {summary}— blockquote, whensummaryis set.{details}— free-form markdown, whendetailsis set.- One
## {section.title}block per entry insections, each followed by its- [title](url): noteslink list (: notesomitted when absent). - A
## Optionalblock, whenoptionalis a non-empty array.
| Field | Type | Notes |
|---|---|---|
title |
string |
Required. Rendered as the # H1. |
summary |
string? |
Rendered as a > blockquote. |
details |
string? |
Free-form markdown after the blockquote. |
sections |
LlmsSection[]? |
Named ## Section link lists. |
optional |
LlmsLink[]? |
Links under the droppable ## Optional section. |
LlmsSection = { title: string; links: LlmsLink[] }. LlmsLink =
{ title: string; url: string; notes?: string }.
generateLlmsText({
title: "Acme Docs",
summary: "Everything an agent needs to use Acme.",
sections: [
{
title: "Docs",
links: [{ title: "Quickstart", url: "/docs/quickstart.md" }],
},
],
optional: [{ title: "Changelog", url: "/changelog.md" }],
});Render an llms-full.txt manifest — the same header as llms.txt, followed by
every page's full markdown inlined.
function generateLlmsFullText(config: LlmsFullConfig): string;Hierarchy: # {title} (site) → ## {section} → ### {page} + the page body. A
page with a url gets a Source: {url} line under its heading.
| Field | Type | Notes |
|---|---|---|
title |
string |
Required. Rendered as the # H1. |
summary |
string? |
Rendered as a > blockquote. |
details |
string? |
Free-form markdown after the blockquote. |
sections |
LlmsFullSection[] |
Required. Sections whose pages inline. |
LlmsFullSection = { title: string; pages: LlmsFullPage[] }. LlmsFullPage =
{ title: string; url?: string; content: string }.
Create a Next.js App Router route handler that serves a slug's markdown content.
Mount it at a catch-all route (app/[...slug]/route.ts). The handler is a pure
Web-standard (request) => Promise<Response> and imports nothing from next.
function createMarkdownRoute(
resolver: MarkdownResolver,
options?: MarkdownRouteOptions,
): (request: Request) => Promise<Response>;MarkdownResolver =
(slug: string[]) => string | null | Promise<string | null>. The slug is the
path segments, URL-decoded, with any trailing .md removed.
MarkdownRouteOptions = { cacheControl?: string }.
| Request | Resolver returns | Response |
|---|---|---|
/docs/intro.md |
"# Intro…" |
200, Content-Type: text/markdown; charset=utf-8, Cache-Control |
/docs/intro.md |
"" |
200 with an empty body (empty string is content, not absence) |
/missing.md |
null |
404, Content-Type: text/plain; charset=utf-8, body Not Found |
/docs/getting%20started.md |
— | resolver receives ["docs", "getting started"] |
/ |
— | resolver receives [] |
Default Cache-Control:
public, max-age=3600, s-maxage=86400, stale-while-revalidate=604800 — override
with options.cacheControl.
// app/[...slug]/route.ts
import { createMarkdownRoute } from "@vllnt/next-llms";
export const GET = createMarkdownRoute(
async (slug) => (await getDoc(slug.join("/")))?.markdown ?? null,
{ cacheControl: "public, max-age=60" },
);