Configuration
How cms.config.ts works, and the options most projects change first.
All project-wide settings live in cms.config.ts at the project root. Every option has a default, so an empty config works:
import { defineConfig } from 'nextjs-cms/core/config'
export default defineConfig({})defineConfig is an identity helper that gives you autocompletion and type checking. For example, localization.defaultLocale only accepts codes you listed in localization.locales.
The CMS looks for cms.config.{ts,mts,cts,js,mjs,cjs,json} in the project root. It is validated with Zod when loaded, so an invalid value stops the app with an error. Unknown top-level keys are silently ignored, which is one more reason to use defineConfig: TypeScript catches the typo. In development, config changes are picked up without a restart.
Branding
export default defineConfig({
ui: {
title: 'Acme CMS', // browser tab title
logo: '/logo.svg', // file in /public
logoText: { text: 'Acme', position: 'right' }, // 'left' | 'right' | 'top' | 'bottom'
defaultTheme: 'light', // 'light' | 'dark' (default 'dark')
},
})What the sidebar header shows:
| You set | Sidebar shows |
|---|---|
logo and logoText | Logo, with the text on the chosen side |
logo only | Logo, scaled to the header height |
logoText only | The text, centered (position is ignored) |
| neither | title, centered |
Sections folder
import { resolve } from 'node:path'
export default defineConfig({
sections: {
path: resolve(process.cwd(), 'sections'), // default: 'sections'
strict: false,
category: { allowRecursiveDelete: false },
},
})strict: whentrue, one broken section file makes the CMS load no sections. Useful in CI to fail loudly. Whenfalse(default), broken sections are skipped and reported, and the rest keep working.category.allowRecursiveDelete: global switch for deleting a category together with its children. A category section must also opt in. See Category Section.
Uploads and images
export default defineConfig({
media: {
upload: {
path: '.uploads', // where files are written, relative to the project root
preserveExtension: true,
},
images: {
background: { r: 255, g: 255, b: 255, alpha: 1 }, // padding for fit: 'contain'
image: {
size: { width: 1200, height: 630, fit: 'cover' },
quality: 85, // 50-100
},
thumbnail: {
size: { width: 400, height: 300, fit: 'cover' },
quality: 80,
},
watermark: false, // or { imagePath: './watermark.png', position: 'bottom-right', ... }
},
},
})These are defaults. Every photoField can override its size, thumbnail, and watermark. Read Media & Uploads for how files are stored and served.
Admin UI languages
The admin interface ships in English. To offer more languages, import dictionaries and list them:
import en from 'nextjs-cms/translations/base/en'
import ar from '@nextjscms/translations/ar'
export default defineConfig({
i18n: {
supportedLanguages: { en, ar },
fallbackLanguage: 'en',
},
})Each admin picks their language in the admin's settings.
Content localization
Different from UI languages: this lets editors translate content (a post's title in English and Arabic).
export default defineConfig({
localization: {
locales: [
{ code: 'en', label: 'English', flag: '/img/flags/en.svg' },
{ code: 'ar', label: 'العربية', rtl: true, flag: '/img/flags/ar.svg' },
],
defaultLocale: 'en',
},
})Then mark fields with localized: true. The full story is in the Localization guide.
Plugins
import { googleAnalyticsPlugin } from '@nextjscms/plugin-google-analytics'
export default defineConfig({
plugins: [googleAnalyticsPlugin],
})See Using Plugins.
Everything else
The full list of options, types and defaults is in the cms.config.ts reference.