@next-library/core
v1.0.38
Published
Core functionality for Next.js documentation framework
Maintainers
Readme
@next-library/core
Core utilities for a Next.js (App Router) documentation site backed by markdown in a GitHub repository: content tree generation, page data extraction, GitHub fetch, i18n helpers, markdown processing, and SEO helpers. No UI components — pair with @next-library/theme or build your own.
Documentation hub (start here)
Extended guides live in the monorepo docs/. Prefer them over long README drift:
| Topic | Guide |
|--------|--------|
| End-to-end setup | Getting started |
| library.config.ts | Configuration |
| CLI vs runtime | Architecture |
| Docs repo layout | Content repository |
| Export index | API reference |
Supported workflow: run next-library generate (often from predev / prebuild) to emit lib/tree.ts. Import contentTree and call extractPageData(contentTree, locale, slugSegments). You do not need withLibrary or a virtual @next-library/tree module.
Features
- Multi-language content (repo layout +
i18n/), RTL-aware helpers - Markdown pipeline (syntax highlighting, math, diagrams — see theme for rendering)
- GitHub API integration for tree generation and raw file fetch
- SEO: metadata helpers and JSON-LD builders
- TypeScript-first
Installation
pnpm add @next-library/coreQuick start
1. Config
// library.config.ts
import type { LibraryConfigInput } from '@next-library/core';
export const libraryConfig: LibraryConfigInput = {
github: {
user: 'org-or-user',
repo: 'your-docs-repo',
branch: 'main',
token: process.env.GITHUB_TOKEN,
},
i18n: {
cookieName: process.env.NEXT_PUBLIC_LOCALE_COOKIE_NAME || 'NEXT_LOCALE',
},
website: {
title: 'My docs',
description: 'Documentation',
siteUrl: process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000',
organizationName: 'My org',
organizationLogo: '/logo.png',
},
};2. Generate lib/tree.ts
{
"scripts": {
"predev": "node ./node_modules/@next-library/core/dist/cli/generate.cjs generate",
"prebuild": "node ./node_modules/@next-library/core/dist/cli/generate.cjs generate"
}
}3. Use the tree at runtime
import contentTree from '@/lib/tree';
import {
extractPageData,
isValidLocale,
getSupportedLocales,
createConfig,
type Locale,
} from '@next-library/core';
import { loadDocumentationMarkdown } from '@next-library/core/server';
import { libraryConfig } from '@/library.config';
const fullConfig = createConfig(libraryConfig);
const locale = 'en' as Locale;
const slug = ['guides', 'intro'];
const pageData = extractPageData(contentTree, locale, slug);
const locales = getSupportedLocales(contentTree);
if (pageData.node?.githubPath && fullConfig.github?.user && fullConfig.github?.repo) {
const markdown = await loadDocumentationMarkdown(pageData.node.githubPath, {
contentDir: fullConfig.tree?.contentBodiesDir,
github: {
user: fullConfig.github.user,
repo: fullConfig.github.repo,
branch: fullConfig.github.branch || 'main',
token: fullConfig.github.token,
app: fullConfig.github.app,
},
});
}Merge runtime config with createConfig(libraryConfig) so GitHub and website settings match what the CLI used to build the tree.
How it works
Build time (or pre-dev)
next-library generateloadslibrary.config.ts, callsgenerateContentTree, writeslib/tree.ts, and (by default) writes markdown bodies +manifest.jsonundertree.contentBodiesDir(default.next-library/content, gitignored). Use--no-content-bodiesto skip body output (Next will fetch from GitHub during build/runtime instead).- Run
next buildafter generate so static pages read bodies from disk (fast SSG, no per-page GitHub raw fetch). In CI/Vercel, setNEXT_LIBRARY_CONTENT_DIRif bodies live outside the default path. - Next.js bundles
lib/tree.tslike any other TypeScript module.
Operational: run generate with a GitHub token or app so contributor commits populate lastCommitAt on nodes (better metadata without extra API calls at page render).
Runtime
- Routes import
contentTreeand resolve locale (e.g.isValidLocale). extractPageData(contentTree, locale, slug)returnsPageData: node, sidebars, breadcrumbs, prev/next, computed flags.loadDocumentationMarkdown(from@next-library/core/server) reads from the generated content directory when present, otherwise falls back tofetchRawFileContent. The/serverentry uses Nodefsand must not be imported from client components.
sequenceDiagram
participant CLI as next_library_generate
participant GH as GitHub_API
participant Tree as lib_tree.ts
participant Bodies as content_bodies_dir
participant Page as App_Route
participant Core as extractPageData
CLI->>GH: List and read markdown
CLI->>Tree: Write ContentTree module
CLI->>Bodies: Write body files plus manifest
Page->>Tree: import contentTree
Page->>Core: extractPageData(tree, locale, slug)
Core-->>Page: PageData
Page->>Bodies: loadDocumentationMarkdown
Bodies-->>Page: markdownPageData (summary)
extractPageData returns navigation, sidebars, computed (pathExists, isFolderView, lang, fallback flags, etc.), and node (or null). For field-level detail, use your editor on PageData in src/lib/content/page-data/types.ts or see the example app.
Configuration
Types live in src/lib/config/types.ts. At runtime, createConfig(libraryConfig) produces a merged LibraryConfig.
See Configuration for github, website, i18n, env vars, and CLI config discovery.
Content repository layout
English (or primary locale) at repo root; other locales under i18n/<locale>/ mirroring paths. See Content repository.
Utilities (tree-first)
Pass the same contentTree you use in pages.
import contentTree from '@/lib/tree';
import {
extractPageData,
getSupportedLocales,
getContentMetadataBySlug,
generateStaticContentPaths,
getBreadcrumbs,
getSiblingPages,
type Locale,
} from '@next-library/core';
const locale = 'en' as Locale;
const slug = ['api', 'reference'];
const pageData = extractPageData(contentTree, locale, slug);
const meta = getContentMetadataBySlug(contentTree, slug, locale);
const paths = generateStaticContentPaths(contentTree);
const crumbs = getBreadcrumbs(contentTree, 'api/reference', locale);
const siblings = getSiblingPages(contentTree, 'api/reference', locale);i18n
import {
isValidLocale,
isLocaleEnabled,
getTextDirection,
getLocaleMetadata,
isRightToLeft,
getSupportedLocales,
} from '@next-library/core';
import contentTree from '@/lib/tree';
isValidLocale('en');
getSupportedLocales(contentTree);
getTextDirection('ar');SEO
import {
generatePageMetadata,
generateArticleSchema,
generateBreadcrumbSchema,
} from '@next-library/core';
const metadata = generatePageMetadata(
{ title: 'Page', description: '…', canonicalUrl: 'https://…', lang: 'en' },
websiteConfig
);Use generateAlternateLanguageUrls(contentTree, path, baseUrl) when wiring hreflang (see example/app/[locale]/docs/[[...slug]]/page.tsx in the monorepo).
Static params
import contentTree from '@/lib/tree';
import { generateStaticContentPaths } from '@next-library/core';
export async function generateStaticParams() {
const paths = generateStaticContentPaths(contentTree);
return paths.map((p) =>
p.slug.length > 0 ? { locale: p.lang, slug: p.slug } : { locale: p.lang }
);
}Markdown processing
import { processMarkdown } from '@next-library/core';
const processed = await processMarkdown(markdownContent, {
allowHtml: true,
allowMath: true,
syntaxHighlighting: true,
sanitize: true,
cacheEnabled: true,
});GitHub / doc markdown
Prefer loadDocumentationMarkdown so SSG uses files from next-library generate when tree.contentBodiesDir is set. fetchRawFileContent remains available for direct raw fetches.
import { createConfig } from '@next-library/core';
import { loadDocumentationMarkdown } from '@next-library/core/server';
import { libraryConfig } from '@/library.config';
const config = createConfig(libraryConfig);
const gh = config.github;
if (gh?.user && gh?.repo) {
await loadDocumentationMarkdown('path/in/repo.md', {
contentDir: config.tree?.contentBodiesDir,
github: {
user: gh.user,
repo: gh.repo,
branch: gh.branch || 'main',
token: gh.token,
app: gh.app,
},
});
}Speed Insights / TTFB: after generate + build, doc HTML should not wait on GitHub; confirm NEXT_LIBRARY_CONTENT_DIR matches your CI output if you override the default directory.
Hooks (client only)
'use client';
import { useLocale } from '@next-library/core/hooks';Proxy / locale routing
For Next.js proxy handling (locale detection / redirects), use handleLocaleRequest from the main package:
// proxy.ts
import { handleLocaleRequest } from '@next-library/core';
import type { NextRequest } from 'next/server';
export async function proxy(request: NextRequest) {
return handleLocaleRequest(request);
}Markdown pipeline (overview)
Raw Markdown → frontmatter → remark (gfm, math, …) → rehype (slug, highlight, mermaid, sanitize) → HTML + TOCTypeScript
import type {
LibraryConfig,
LibraryConfigInput,
ContentTree,
ContentNode,
PageData,
Locale,
} from '@next-library/core';API reference
See ./docs/api.md for a structured export index.
Contributing
See CONTRIBUTING.md.
License
MIT
