@djangocfg/nextjs
v2.1.571
Published
Next.js server utilities: sitemap, health, OG images, contact forms, navigation, config
Maintainers
Readme

@djangocfg/nextjs
Server-side Next.js utilities: OG images, sitemap, health checks, i18n, PWA, navigation, and config
Part of DjangoCFG — a modern Django framework for building production-ready SaaS applications. All @djangocfg/* packages are designed to work together, providing type-safe configuration, real-time features, and beautiful admin interfaces out of the box.
Overview
@djangocfg/nextjs provides server-side utilities for Next.js applications built on top of Django-CFG.
Note: Client components (legal pages, error pages, redirect components) are in
@djangocfg/layouts.
Features
- Page Metadata + OG Images — one factory for complete page metadata: titles, canonical + hreflang, OpenGraph, Twitter, icons (
@djangocfg/nextjs/og-image) - i18n — full next-intl integration with routing, middleware, and components
- PWA — zero-config service worker and manifest
- Base Next.js Config — reusable
createBaseNextConfig()factory for monorepos - Security headers + CSP — baseline Content-Security-Policy and hardening headers on every route, dev-aware (see below)
- DPoP auth — one-flag opt-in to RFC 9449 sender-constrained tokens (
dpop: true) — see@docs/DPOP.md - Sitemap — paginated sitemap-index backed by
django_cfg.modules.django_sitemap(handles 2M+ URLs) - Health Checks — production-ready health monitoring endpoints
- Navigation — route definitions, menu generation, and active-state helpers
- AI Documentation — search DjangoCFG docs via MCP server and CLI
Installation
npm install @djangocfg/nextjs
# or
pnpm add @djangocfg/nextjs
# or
yarn add @djangocfg/nextjsQuick Start
AI Documentation Search
Search DjangoCFG documentation from the terminal:
# CLI (works anywhere via npx)
npx @djangocfg/nextjs ai-docs search "database configuration"
npx @djangocfg/nextjs ai-docs mcpOr use the TypeScript API:
import { search, getDocs, getMcpConfig } from '@djangocfg/nextjs/ai';
// Search documentation
const results = await search('database configuration');
results.forEach(r => console.log(r.title, r.url));
// Get formatted docs
const docs = await getDocs('How to configure PostgreSQL?');
// Get MCP server config for AI assistants
const config = getMcpConfig();MCP Server for AI Assistants
Add to your AI assistant configuration:
{
"mcpServers": {
"djangocfg-docs": {
"url": "https://mcp.djangocfg.com/mcp"
}
}
}Base Next.js Configuration
Create a reusable base configuration for all your Next.js projects:
// next.config.ts
import { createBaseNextConfig } from '@djangocfg/nextjs/config';
import bundleAnalyzer from '@next/bundle-analyzer';
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
});
export default withBundleAnalyzer(createBaseNextConfig({
// Base path for static builds
basePath: process.env.NEXT_PUBLIC_STATIC_BUILD === 'true' ? '/cfg/admin' : undefined,
// Add custom transpile packages
transpilePackages: ['my-custom-package'],
// Custom webpack configuration (called after base webpack logic)
webpack: (config, options) => {
// Your custom webpack rules
return config;
},
}));Security headers & CSP
createBaseNextConfig() adds baseline security headers to every route out of
the box — no setup required:
Content-Security-Policy— a pragmatic baseline that blocks the high-value injection vectors (object-src 'none',base-uri 'self',form-action 'self',frame-ancestors) while keeping the inline/eval scripts a Next.js app needs. Dev-aware:connect-src/img-srcallowhttp:/ws:in development (a local Django, plus any WebSocket transport the app brings itself) and tighten tohttps:/wss:only in production.X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-originX-Frame-Options: DENY(orSAMEORIGINwhenallowIframeFromis set)
A fully strict nonce-based script-src is a planned follow-up (needs middleware +
per-request nonce). To allow iframe embedding, pass allowIframeFrom: ['https://example.com'].
DPoP — sender-constrained tokens (RFC 9449)
Make a stolen access token useless, without a BFF/proxy — opt in with one flag:
export default createBaseNextConfig({
dpop: true, // inlines NEXT_PUBLIC_DPOP_ENABLED='true'
});The browser holds a non-extractable key and signs a per-request DPoP proof; the
backend binds the token to that key (cnf.jkt) and rejects replays. Requires
JWTConfig(dpop_enabled=True) on the Django side. Full guide:
@docs/DPOP.md.
PWA (Progressive Web App)
Zero-config PWA - Works out of the box! Service worker and offline support included automatically.
1. Create manifest
// app/manifest.ts
import { createManifest, createScreenshots } from '@djangocfg/nextjs/config';
import { settings } from '@core/settings';
export default createManifest({
name: settings.app.name,
description: settings.app.description,
themeColor: '#ffffff',
backgroundColor: '#000000',
icons: {
logo192: settings.app.icons.logo192,
logo384: settings.app.icons.logo384,
logo512: settings.app.icons.logo512,
},
// Optional: Add screenshots for Richer PWA Install UI
// IMPORTANT: Image dimensions must match sizes parameter!
// screenshots: createScreenshots([
// '/screenshots/desktop-1920x1080.png', // Wide: 1920x1080
// '/screenshots/mobile-390x844.png', // Narrow: 390x844
// ]),
});2. Add manifest metadata
// app/layout.tsx
import { createManifestMetadata } from '@djangocfg/nextjs/config';
export const metadata = {
...createManifestMetadata({
name: 'My App',
themeColor: '#000000',
}),
};That's it! PWA is now enabled in production. Generated files:
/manifest.webmanifest- Fromapp/manifest.ts/sw.js- Service worker (auto-generated in production)/workbox-*.js- Workbox runtime
Customization (optional):
// next.config.ts
export default createBaseNextConfig({
pwa: {
dest: 'public',
disable: process.env.NODE_ENV === 'development', // Disable in dev
fallbacks: {
document: '/_offline', // Offline page
},
runtimeCaching: [
// Custom caching rules
...defaultRuntimeCaching,
createApiCacheRule('https://api.example.com'),
],
},
});See PWA.md for complete guide.
i18n (Internationalization)
Full next-intl integration for multilingual Next.js apps.
Important: Use subpath imports to avoid React Context issues in server components:
@djangocfg/nextjs/i18n- Types only@djangocfg/nextjs/i18n/routing-routing,createRouting,generateLocaleParams@djangocfg/nextjs/i18n/client-I18nProvider, hooks@djangocfg/nextjs/i18n/components-LocaleSwitcher
1. Create middleware (proxy.ts)
// proxy.ts (or middleware.ts)
import createMiddleware from 'next-intl/middleware';
import { routing } from '@djangocfg/nextjs/i18n/routing';
export default createMiddleware(routing);
export const config = {
matcher: ['/((?!api|_next|_vercel|.*\\..*).*)',],
};2. Create i18n request config
// i18n/request.ts
import { getRequestConfig } from 'next-intl/server';
import { en, ru, ko } from '@djangocfg/i18n/locales';
import { mergeTranslations } from '@djangocfg/i18n/utils';
const locales = {
en: mergeTranslations(en, { app: appEn }),
ru: mergeTranslations(ru, { app: appRu }),
ko: mergeTranslations(ko, { app: appKo }),
};
export default getRequestConfig(async ({ requestLocale }) => {
let locale = await requestLocale;
if (!locale || !(locale in locales)) locale = 'en';
return {
locale,
messages: locales[locale],
timeZone: 'UTC',
};
});3. Use in layout
// app/[locale]/layout.tsx
import { getMessages } from 'next-intl/server';
import { I18nProvider } from '@djangocfg/nextjs/i18n/client';
import { routing, generateLocaleParams } from '@djangocfg/nextjs/i18n/routing';
export function generateStaticParams() {
return generateLocaleParams();
}
export default async function LocaleLayout({ children, params }) {
const { locale } = await params;
const messages = await getMessages();
return (
<I18nProvider locale={locale} messages={messages}>
{children}
</I18nProvider>
);
}4. Use translations in components
'use client';
import { useTranslations } from 'next-intl';
function MyComponent() {
const t = useTranslations('app');
return <h1>{t('title')}</h1>;
}Locale Switcher
Smart wrapper around @djangocfg/layouts LocaleSwitcher with automatic next-intl hooks:
import { LocaleSwitcher } from '@djangocfg/nextjs/i18n/components';
// Basic usage (automatically gets locale from next-intl)
<LocaleSwitcher />
// With custom locales (override routing config)
<LocaleSwitcher locales={['en', 'ru']} />
// With custom labels and styling
<LocaleSwitcher
labels={{ en: 'English', ru: 'Русский' }}
variant="outline"
size="sm"
/>Note: This component wraps
@djangocfg/layoutsLocaleSwitcher withuseLocaleSwitcher()hook. For a presentational version that accepts locale data via props, useLocaleSwitcherfrom@djangocfg/layoutsdirectly.
Sitemap Generation
Paginated sitemap-index integrated with django_cfg.modules.django_sitemap.
Backed by a typed JSON contract: Django streams keyset-paginated chunks,
Next.js's native generateSitemaps() turns each chunk into one XML file
plus an auto-generated <sitemapindex> at /sitemap.xml. Handles
catalogs up to Google's 50 000-URLs-per-file cap without materialising
data on the frontend.
// app/sitemap.ts
import { createDjangoSitemap } from '@djangocfg/nextjs/sitemap';
const { generateSitemaps, sitemap } = createDjangoSitemap({
host: process.env.NEXT_PUBLIC_SITE_URL!,
apiUrl: process.env.NEXT_PUBLIC_API_URL, // optional — omit for static-only
staticRoutes: [
{ path: '/', changeFrequency: 'daily', priority: 1.0 },
{ path: '/catalog', changeFrequency: 'daily', priority: 0.9 },
],
});
export { generateSitemaps, sitemap as default };
export const revalidate = 3600;// app/robots.ts
import { createRobots } from '@djangocfg/nextjs/sitemap';
export default createRobots({
host: process.env.NEXT_PUBLIC_SITE_URL!,
disallow: ['/account/', '/auth', '/api/'],
});See src/sitemap/README.md for the full
backend ↔ frontend integration guide, including the Django source
registration, cache layers, and deployment notes.
Health Check Endpoint
Create a health monitoring endpoint:
// app/healthz/route.ts
import { createHealthHandler } from '@djangocfg/nextjs/health';
export const GET = createHealthHandler({
version: process.env.NEXT_PUBLIC_APP_VERSION || '1.0.0',
checks: [
{
name: 'database',
check: async () => {
// Check database connection
return await checkDatabase();
},
},
],
});Page Metadata + OG Images
One factory per app builds complete page Metadata (title, openGraph, twitter,
canonical + hreflang, icons). The OG image is chosen by a single og field —
the static brand image by default, or an exact URL:
// app/_core/metadata.ts — bind once
import { makeMetadataFactory } from '@djangocfg/nextjs/og-image'
export const createMetadata = makeMetadataFactory({
name: settings.app.name,
description: settings.app.description,
siteUrl: settings.app.siteUrl,
ogImage: settings.app.media.ogimage,
locales: LOCALES,
})
// landing page → static brand image (default)
export const metadata = createMetadata({ title: 'Pricing', path: '/pricing' })
// content page with its own image
export const metadata = createMetadata({ title: p.name, path, og: { image: p.image } })For a card rendered per page, add an opengraph-image route — Next.js finds it
by filename and its output overrides whatever the factory set, so the factory
has no "dynamic" case of its own.
Navigation Utilities
Define routes and generate navigation menus:
import { defineRoute, routesToMenuItems } from '@djangocfg/nextjs/navigation';
const routes = [
defineRoute('/dashboard', { title: 'Dashboard' }),
defineRoute('/settings', { title: 'Settings' }),
];
const menuItems = routesToMenuItems(routes);Client Components
Client-side components (legal pages, error pages, redirect components) are available in @djangocfg/layouts:
// Legal pages
import { PrivacyPage, TermsPage } from '@djangocfg/layouts/pages/legal';
// Error pages
import { ErrorLayout } from '@djangocfg/layouts/components/errors';
// Redirect component
import { RedirectPage } from '@djangocfg/layouts/components/RedirectPage';Exports
| Path | Description |
|------|-------------|
| @djangocfg/nextjs | Main exports (all modules) |
| @djangocfg/nextjs/i18n | Types only (safe for server components) |
| @djangocfg/nextjs/i18n/routing | routing, createRouting, generateLocaleParams, DEFAULT_LOCALES, DEFAULT_LOCALE |
| @djangocfg/nextjs/i18n/client | I18nProvider, hooks ('use client') |
| @djangocfg/nextjs/i18n/server | Server-side i18n utilities |
| @djangocfg/nextjs/i18n/components | LocaleSwitcher ('use client') |
| @djangocfg/nextjs/i18n/proxy | createProxy for middleware |
| @djangocfg/nextjs/i18n/navigation | Link, redirect, usePathname, useRouter |
| @djangocfg/nextjs/ai | AI documentation search and MCP config |
| @djangocfg/nextjs/config | Base Next.js configuration factory |
| @djangocfg/nextjs/pwa | PWA client utilities (service worker registration) |
| @djangocfg/nextjs/pwa/worker | Service worker creation helpers |
| @djangocfg/nextjs/sitemap | Sitemap generation with i18n hreflang support |
| @djangocfg/nextjs/health | Health check handlers |
| @djangocfg/nextjs/og-image | Page metadata factory — titles, canonical + hreflang, OpenGraph, Twitter, icons |
| @djangocfg/nextjs/navigation | Route definitions, menu generation, navigation helpers |
API Reference
Base Configuration
createBaseNextConfig(options)
Creates a reusable base Next.js configuration with standard DjangoCFG settings.
createBaseNextConfig({
basePath?: string; // Base path for static builds
transpilePackages?: string[]; // Additional packages to transpile
optimizePackageImports?: string[]; // Additional packages to optimize
webpack?: (config, options) => config; // Custom webpack configuration
experimental?: NextConfig['experimental']; // Custom experimental options
env?: Record<string, string>; // Custom environment variables
// ... any other NextConfig options
})Features:
- Automatic dev/production optimizations
- Filesystem caching for faster rebuilds
- Compression plugins for static builds (Gzip + Brotli)
- Standard transpile packages for monorepo
- Type-safe configuration merging
i18n
Subpath imports required - see table below for correct import paths.
DEFAULT_LOCALES / DEFAULT_LOCALE
All 17 supported locales and the default locale constant.
import { DEFAULT_LOCALES, DEFAULT_LOCALE } from '@djangocfg/nextjs/i18n/routing';
DEFAULT_LOCALES // ['en', 'ru', 'ko', 'ja', 'de', 'fr', 'zh', 'it', 'es', 'nl', 'ar', 'tr', 'pt-BR', 'pl', 'sv', 'no', 'da']
DEFAULT_LOCALE // 'en'Use in locales.config.ts to stay in sync with the package:
// i18n/locales.config.ts
export { DEFAULT_LOCALES as LOCALES, DEFAULT_LOCALE } from '@djangocfg/nextjs/i18n/routing';
export type { LocaleCode as AppLocale } from '@djangocfg/nextjs/i18n';routing
Default routing configuration with all 17 locales and default locale 'en'.
import { routing } from '@djangocfg/nextjs/i18n/routing';
routing.locales // ['en', 'ru', 'ko', 'ja', ...]
routing.defaultLocale // 'en'createRouting(config?)
Create custom routing configuration.
import { createRouting } from '@djangocfg/nextjs/i18n/routing';
const routing = createRouting({
locales: ['en', 'de', 'fr'],
defaultLocale: 'en',
localePrefix: 'always', // 'always' | 'as-needed' | 'never'
});I18nProvider
Provider component for next-intl integration (client component).
import { I18nProvider } from '@djangocfg/nextjs/i18n/client';
<I18nProvider locale="en" messages={messages}>
{children}
</I18nProvider>LocaleSwitcher
Smart locale switcher that wraps @djangocfg/layouts LocaleSwitcher with next-intl hooks.
import { LocaleSwitcher } from '@djangocfg/nextjs/i18n/components';
// Automatically gets locale from next-intl routing
<LocaleSwitcher />
// With custom options
<LocaleSwitcher
locales={['en', 'ru']} // Override available locales
labels={{ en: 'EN', ru: 'RU' }} // Custom locale labels
variant="outline"
size="sm"
showIcon={true}
className="w-full"
/>Props (extends @djangocfg/layouts LocaleSwitcherProps):
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| locales | string[] | From routing | Override available locales |
| labels | Record<string, string> | Built-in | Custom locale labels |
| showCode | boolean | false | Show locale code with label |
| variant | 'ghost' \| 'outline' \| 'default' | 'ghost' | Button variant |
| size | 'sm' \| 'default' \| 'lg' \| 'icon' | 'sm' | Button size |
| showIcon | boolean | true | Show globe icon |
Sitemap
createSitemapHandler(options)
Creates a Next.js route handler for sitemap generation with optional i18n support.
createSitemapHandler({
siteUrl: string;
staticPages?: SitemapUrl[];
dynamicPages?: SitemapUrl[] | (() => Promise<SitemapUrl[]>);
cacheControl?: string;
// i18n support
i18n?: {
locales: string[]; // e.g., ['en', 'ru', 'ko']
defaultLocale: string; // e.g., 'en'
};
})When i18n is provided:
- URLs are expanded for each locale (e.g.,
/about→/en/about,/ru/about,/ko/about) xhtml:linkelements withhreflangare added for SEOx-defaultpoints to the default locale
Health
createHealthHandler(config)
Creates a health check route handler.
createHealthHandler({
version?: string;
checks?: Array<{ name: string; check: () => Promise<boolean> }>;
customData?: Record<string, any>;
})Page Metadata + OG Image
@djangocfg/nextjs/og-image builds complete page metadata: title templating,
canonical + hreflang alternates, OpenGraph, Twitter cards and icons.
See src/og-image/README.md for full docs (the og
axis, SiteMetaConfig/PageMeta fields).
makeMetadataFactory(config) — recommended
Binds a SiteMetaConfig once; returns a per-page factory. The OG image is set by
the single og field: 'static' (default) · { image, alt? }.
import { makeMetadataFactory } from '@djangocfg/nextjs/og-image'
export const createMetadata = makeMetadataFactory({
name: 'CMDOP', description: '…', siteUrl: 'https://cmdop.com',
ogImage: '/static/ogimage.png', locales: LOCALES,
})
export const metadata = createMetadata({ title: 'About', path: '/about' }) // brand image
export const metadata = createMetadata({ title: p.name, og: { image: p.image } })Per-page rendered cards
Use Next.js's opengraph-image file convention — but note it applies only when
metadata leaves openGraph.images unset. createMetadata always sets it, so
the route is suppressed unless the page strips that key from the metadata the
factory returned. See src/og-image/README.md.
Low-level (escape hatches)
resolveOgImage(spec, staticUrl, fallbackTitle) is the OgSpec decision point;
extractTitle(metadata) reads a plain title out of Next.js's title shapes. Most
pages use createMetadata and never touch these.
Navigation
defineRoute(path, metadata, config?)
Defines a route with metadata.
defineRoute('/dashboard', {
title: 'Dashboard',
description: 'Dashboard overview',
}, {
basePath: '/admin',
isStaticBuild: false,
})isActive(current, target, allRoutes?)
Checks if a route is currently active.
isActive('/admin/dashboard', '/admin/dashboard') // true
isActive('/admin/dashboard/settings', '/admin/dashboard') // true
isActive('/admin/dashboard', '/admin/settings') // falseroutesToMenuItems(routes)
Converts route definitions to menu items.
const menuItems = routesToMenuItems(allRoutes);Requirements
- Next.js >= 13.0.0 (App Router)
- React >= 18.0.0
- TypeScript >= 5.0.0
Peer Dependencies
next- Next.js framework (^15.5.7)
Documentation
Full documentation available at djangocfg.com
Contributing
Issues and pull requests are welcome at GitHub
License
MIT - see LICENSE for details
