Plugin options
Every option of withSysthema()'s second argument, with defaults.
On this page
All options live in the second argument to withSysthema(). Every option is, well, optional — Systhema ships sensible defaults.
withSysthema(config) without a second argument resolves exactly like withSysthema(config, {}). The Components collection (components) and the embed block (embed) are on by default; turn them off with components: false and embed: false.
The Google Maps block (googleMaps) uses the Maps Embed API, which needs a key, so it is on exactly when you forward one: googleMaps: { apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY }. A missing or blank apiKey leaves it off, and enabled: false turns it off even with a key. The package never reads the environment itself.
The hero, feature and post hero media types offer Google Maps only when a key is configured. Without one the option is not shown, a document that already stores mediaType: 'googleMaps' still saves (and the editor still sees that choice and can switch away), and its map renders nothing instead of an empty iframe. The same goes for a stored Google Maps block. The media type is rendered by a new Admin field component, so regenerate the import map after upgrading (payload generate:importmap; systhema upgrade does it for you).
ExampleLink to this section
const userSysthemaConfig: SysthemaPayloadPluginOptions = {
// Globals (header / footer).
header: true, // shorthand for { enabled: true }
footer: { enabled: true, type: 'simple' }, // 'simple' | 'advanced'
// Collections.
pages: { enabled: true },
components: true, // default true
users: true,
uploads: { slug: 'media', staticDir: 'media' },
// Built-in feature plugins.
nestedDocs: true,
redirect: true,
seo: true,
embed: true, // embed block, default true
googleMaps: { apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY }, // on when the key is set
articleColorSetting: true,
themes: { hidden: ['mall'] }, // a page-level accent your own field applies — no swatch, stored values stay valid
// Optional Posts module (blog/posts). Off by default. See "Posts module".
posts: { enabled: true, permalink: '/posts/{slug}' },
// Custom registrations.
customBlocks: [], // see "Custom blocks"
customTextStates: [], // see "Custom text states"
customPageTemplates: [],
customPostTemplates: [], // see "Posts module" (only used when posts is on)
customCollections: [],
customGlobals: [],
// Roles & capabilities.
roles: {
custom: [], // see "Access control"
extend: {},
},
// Admin panel ordering.
orderCollections: ['pages', 'media', 'users'],
orderGlobals: ['header', 'footer'],
// Form builder + captcha provider settings (optional) — see "Captcha".
forms: {
fields: {
turnstile: { siteKey: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY!, secretKey: process.env.TURNSTILE_SECRET_KEY! },
},
},
// AI assistant (optional, disabled by default) — see docs/payload/ai/index.md.
ai: {
text: { provider: 'anthropic', apiKey: process.env.ANTHROPIC_API_KEY }, // or 'openai' | 'anthropic' | 'google' | 'groq' | 'openai-compatible'
image: { provider: 'openai', apiKey: process.env.OPENAI_API_KEY }, // or false to disable image generation
},
// Analytics dashboard widgets (optional, disabled by default) — see docs/payload/analytics/index.md.
analytics: {
provider: { source: 'ga4', propertyId: '123456789', clientEmail: process.env.SYSTHEMA_ANALYTICS_GA_CLIENT_EMAIL, privateKey: process.env.SYSTHEMA_ANALYTICS_GA_PRIVATE_KEY }, // or 'plausible' | 'umami' | 'matomo' | 'fathom'
},
// Cloudflare integration (optional, disabled by default) — see docs/payload/cloudflare/index.md.
cloudflare: {
apiToken: process.env.CLOUDFLARE_API_TOKEN, // or apiKey + email for the legacy Global API Key
},
// Content localization (optional, off by default) — the SAME value from systhema.config.ts.
// See docs/nextjs/locales/index.md and docs/payload/localization/migrating-existing-site.md.
// WHICH fields are per-locale is the separate `localization` option below.
locales: systhemaConfig.locales,
// Which fields content localization reaches. Defaults to 'legacy' (a fixed
// name allowlist); 'all' localizes every field of every Systhema-owned schema
// except a documented exception list. Switching is a schema change — see
// docs/payload/localization/field-policy.md.
// `localizeStatus` additionally makes `_status` per-locale, so a page can be
// published in `hu` while `en` stays a draft. On by default with `locales`;
// set it to false to opt out — see docs/payload/localization/per-locale-publishing.md.
localization: { policy: 'all' },
// Admin interface languages (optional, independent of content locales) — see docs/payload/localization/admin-translations.md.
translations: { supported: ['en', 'hu'], fallback: 'en' },
// Admin overrides.
admin: { /* path, URLs, custom paths, ... */ },
// Live preview rendering mode — see "Live preview".
livePreview: { mode: 'server' }, // 'server' (default) | 'client'
}Most boolean options accept a shorthand true or an object form for fine-grained settings.
The Payload template extracts the forms and redirect values into src/payload/forms.ts and src/payload/redirects.ts (wired back by shorthand) so systhema create / systhema setup can toggle each module without touching payload.config.ts.
Admin sidebarLink to this section
The Posts module's collections (Posts / Categories / Tags) and the form-builder collections (Forms / Form Submissions) are grouped under Posts and Forms admin sidebar groups respectively, so each reads as its own module instead of mixing in with the general collections. Override a group label via that collection's admin.group (Posts/Categories/Tags) or via forms.formOverrides.admin.group / forms.formSubmissionOverrides.admin.group.
Systhema also ships a custom admin Nav (admin.components.Nav) that reorders the sidebar so the default Globals group sits below those custom module groups (Payload otherwise pins the default Collections/Globals groups above any custom group, which puts site-config globals above your content modules). It's a thin wrapper around Payload's DefaultNav plus a small CSS rule, so all default nav behaviour is preserved; a consumer that sets its own admin.components.Nav overrides it.
ReferenceLink to this section
The table below is generated from the SysthemaPayloadPluginOptions type.
| Option | Type | Default | Description |
|---|---|---|---|
ai | false | SysthemaAiOptions | false | AI assistant configuration — field-level generation, selection-based lexical editor actions, layout generation with native blocks, and image generation/refinement. |
analytics | false | SysthemaAnalyticsOptions | false | Analytics dashboard widgets — provider-backed traffic stats on the admin dashboard (KPI overview, visitors/views chart, top-N breakdowns, live visitors). |
articleColorSetting | boolean | false | Enable/disable article color setting (articleTheme) |
button | { defaultIconBefore?: string defaultIconAfter?: string } | Button block defaults. | |
chip | { defaultIconBefore?: string defaultIconAfter?: string } | Chip block defaults. | |
cloudflare | false | SysthemaCloudflareOptions | false | Cloudflare integration — edge cache purging (mirrored from Next.js revalidations), a zone-settings admin page, and optional dashboard widgets backed by Cloudflare's GraphQL Analytics API. |
components | boolean | { enabled: boolean } | true | Components collection configuration Can be a boolean (true/false) or an object with enabled property |
customAdminPath | string | '/admin' (or '/' when customAdminURL is set) | Custom admin route path override. |
customAdminURL | string | undefined (standard /admin path on the same domain) | Custom admin panel URL for hosting admin on a separate domain or subdomain. |
customBlocks | CustomBlock[] | [] | Custom blocks to register in lexical editors Each block specifies type (inline/block), data, and target editor(s) |
customCollections | CollectionConfig[] | [] | Custom collections to register alongside Systhema collections Accepts standard Payload collection configs |
customGlobals | GlobalConfig[] | [] | Custom globals to register alongside Systhema globals Accepts standard Payload global configs |
customLivePreviewURL | string | undefined | Pin live preview to a specific server URL. |
customPageTemplates | SysthemaPayloadPageTemplate[] | [] | Custom page templates to add to the default templates |
customPostTemplates | SysthemaPostTemplate[] | [] | Custom post templates to add to the default Single-Post template. |
customTextStates | CustomTextState[] | [] | Custom text states to register in lexical editors Each state config specifies the state object and target editor(s) Multiple configs targeting the same editor will be merged |
emailTemplate | ( html: string, options: { logoUrl?: string; serverUrl?: string }, ) => string | Promise<string> | Custom email template wrapper function. | |
emails | | false | { /** Default "from" address for outgoing emails (e.g. "Acme <noreply@acme.com>") */ defaultFromAddress: string /** Default "reply-to" address. Falls back to defaultFromAddress if not set. */ defaultReplyTo?: string /** Default recipient for form submission notifications (admin inbox) */ adminAddress?: string } | false | Emails global and Systhema email template wrapping configuration. |
embed | boolean | { enabled: boolean } | true | Embed block configuration Can be a boolean (true/false) or an object with enabled property |
environment | 'production' | 'staging' | 'development' | 'production' | Application environment — controls robots meta tags, caching behavior, and other environment-specific features. |
footer | boolean | { enabled: boolean; type?: 'simple' | 'advanced' } | { enabled: true, type: 'simple' } | Footer configuration Can be a boolean (true/false) or an object with enabled and optional type properties If boolean is true or not provided, type defaults to 'simple' |
forms | | boolean | ({ enabled?: boolean } & Omit<FormBuilderPluginConfig, 'fields'> & { fields?: SysthemaFormsFields submissionColumns?: SysthemaFormSubmissionColumns autoSubmissionColumns?: boolean }) | false | Forms block configuration (requires |
generalSettings | | boolean | { enabled?: boolean cookieConsent?: { /** Editor-level kill switch defaults to `false`. ANDed with developer's `cookieConsent.enabled` from systhema.config.ts. */ enabled?: boolean } } | GeneralSettings global config. | |
googleMaps | false | { enabled?: boolean; apiKey?: string | null } | on when `apiKey` is set, otherwise off | Google Maps block configuration. |
header | boolean | { enabled: boolean } | { enabled: true } | Header configuration Can be a boolean (true/false) or an object with enabled property |
icons | IconPacksConfig | { fontAwesome: { styles: ['solid', 'brands'] }, materialSymbols: { style: 'outlined', weight: 400, fill: false }, } | Icon picker pack configuration. |
importExport | | boolean | ({ enabled?: boolean } & Partial<ImportExportPluginConfig> & { formSubmissionExport?: false | SysthemaFormSubmissionExportOptions }) | true when `forms` is enabled, otherwise false | Import/Export (@payloadcms/plugin-import-export) — adds Export controls to a collection's list view. |
livePreview | { mode?: 'server' | 'client' /** * Relationship-population depth for client-side live preview re-fetches. * Should match the depth of the initial server render (2). Only used when * `mode` is `'client'`. * @default 2 */ depth?: number /** * A `'use client'` component that WRAPS every client-side preview render. * * `customBlocks` and `customTextStates` are registered during server-side * config resolution, into stores the browser never receives — so under * `mode: 'client'` a project's custom blocks render as nothing and its text * states render unstyled, while the published page looks correct. Importing * a registration module into your own views does not fix it: Systhema's * built-in views (`DefaultPageClientView`, `DefaultPostClientView`, * `DefaultArchiveClientView`) cannot import consumer code. * * Point this at a `'use client'` module whose MODULE SCOPE performs the * registration, and Systhema threads it into every preview — built-in, * custom template, and consumer collection alike: * * ```tsx * // src/payload/previewClientSetup.tsx * 'use client' * import { registerSysthemaClientBlocks, registerSysthemaClientTextStates } * from '@systhemaui/payload/next/client' * import { myBlock, myConverter } from './blocks/MyBlock' * import { customTextStates } from './textStates' * * registerSysthemaClientBlocks([{ type: 'block', data: myBlock, converter: myConverter }]) * registerSysthemaClientTextStates(customTextStates) * * export default function PreviewClientSetup({ children }: { children: React.ReactNode }) { * return <>{children}</> * } * ``` * * It WRAPS rather than sitting beside the view on purpose: the registries * are plain non-reactive objects, so a registration that lands after the * first render would never trigger a re-render. Wrapping makes "module * evaluated before the view renders" a structural guarantee instead of an * ordering race. */ clientSetup?: React.ComponentType<{ children: React.ReactNode }> } | { mode: 'server', depth: 2 } | Live preview rendering mode. |
locales | SysthemaLocales | false | Frontend/content locales from the Systhema locale contract. | |
localization | SysthemaLocalizationOptions | Which fields content localization reaches, and the per-project exceptions. | |
nestedDocs | boolean | true | Enable/disable the nested docs plugin |
orderCollections | string[] | Optional ordering for collections by slug Collections listed here will be moved to the front in the specified order Remaining collections keep their relative order afterwards | |
orderGlobals | string[] | Optional ordering for globals by slug Globals listed here will be moved to the front in the specified order Remaining globals keep their relative order afterwards | |
pages | | boolean | { enabled: boolean default?: SysthemaPayloadPageTemplate } | { enabled: true, default: { name: 'default', component: DefaultPage, fields: defaultTemplateFields } } | Pages collection configuration Can be a boolean (true/false) or an object with enabled and default properties |
posts | boolean | PostsOptions | false | Posts module configuration — gated blog/posts capability (collections, permalink engine, listing components). |
redirect | boolean | false | Enable/disable the redirects plugin (requires |
roles | RolesConfig | { customRoles: [], customCapabilities: [], editorCapabilities: undefined } | Roles and capabilities configuration for fine-grained access control. |
seo | | boolean | { /** @default true */ enabled?: boolean /** * Site-level defaults defined in code (usually mirroring the app's * layout metadata). Used when the corresponding General Settings → * SEO field is empty. */ defaults?: SeoDefaults } | true | Enable/disable the SEO plugin. |
sitemaps | SitemapsConfig | undefined | Sitemap configuration. |
themes | { hidden?: string[] } | Colour-system themes. | |
translations | SysthemaPayloadTranslationsOptions | Payload Admin translations. | |
uploads | { /** Collection slug - affects URL paths and database table name */ slug?: string /** Admin UI labels for the collection */ labels?: { singular: LabelFunction | StaticLabel; plural: LabelFunction | StaticLabel } /** Static directory for file storage */ staticDir?: string /** Enable folder organization for uploads */ folders?: boolean /** Enable trash/soft-delete for uploads */ trash?: boolean /** Maximum allowed file size in bytes. Set to 0 to disable the limit. @default 104857600 (100MB) */ maxFileSize?: number /** Allowed MIME type prefixes (e.g., ['image', 'video', 'application/pdf']). Files not matching any prefix are rejected client-side. Set to empty array to allow all. @default images, video, audio, PDF, Office, iWork, archives, text/csv */ allowedMimeTypes?: string[] /** Image optimization settings for JPEG/PNG uploads. Set to false to disable optimization entirely. Number fields accept true (default) or false (no limit/compression). @default { maxWidth: 2560, jpegQuality: 60, convertPngToJpeg: true, pngQuality: 60 } */ imageOptimization?: UploadsImageOptimizationInput | false } | { slug: 'uploads', labels: { singular: 'Upload', plural: 'Uploads' }, staticDir: 'storage/uploads', folders: true, trash: true } | Uploads collection configuration |
users | | boolean | { /** Enable or disable the Users collection */ enabled?: boolean /** Collection slug - affects URL paths and database table name */ slug?: string /** Admin UI labels for the collection */ labels?: { singular: LabelFunction | StaticLabel; plural: LabelFunction | StaticLabel } /** Enable avatar upload field on users. @default true */ avatar?: boolean /** Authentication configuration */ auth?: { /** Token expiration time in seconds @default 86400 (24 hours) */ tokenExpiration?: number /** Maximum failed login attempts before lockout @default 5 */ maxLoginAttempts?: number /** Account lockout duration in seconds @default 300 (5 minutes) */ lockTime?: number /** Username login settings */ loginWithUsername?: { /** Allow users to login with email @default true */ allowEmailLogin?: boolean /** Require email field @default true */ requireEmail?: boolean } } /** New-password rules and the Admin password assist */ passwords?: { /** * Reject a new password set over REST or GraphQL unless it has at * least 8 characters, an uppercase letter and a number. The Local API * (seeds, scripts) is never checked. * @default true */ enforce?: boolean /** * Ask TypeSafe's Jev whether a password that passes the local checks * is still easy to guess. Off unless set. The Admin then sends the * candidate password in plain text to TypeSafe's API, which retains * requests unless your plan has zero data retention. */ jev?: { apiKey: string /** @default 'jev-latest' */ model?: string } } } | { enabled: true, slug: 'users', labels: { singular: 'User', plural: 'Users' } } | Users collection configuration Can be a boolean (true/false) or an object with enabled and additional properties Set to false or { enabled: false } to disable and provide your own users collection |
whitelabel | | boolean | { enabled: boolean graphics?: { Logo?: string Icon?: string } } | { enabled: false } | Whitelabel configuration to remove or replace Systhema branding Can be a boolean (true/false) or an object with enabled and optional graphics properties When enabled: - Removes SysthemaThemeProvider (Systhema CSS theme) - Removes default Logo/Icon graphics (or replaces with custom paths) - Keeps other providers (Richtext, EditorStyles, LivePreview) |
Option guidesLink to this section
Options with more than a line of behaviour have their own page:
uploads.imageOptimization: Media and image optimizationusers.passwords: Users and passwordsseo: SEO settingsredirect: Redirectsroles: Access controlcustomBlocks: Custom blockscustomTextStates: Custom text statesicons,button,chip: Icon pickerforms.fields.recaptcha,forms.fields.turnstile: Captchaforms.submissionColumns,forms.autoSubmissionColumns: Submission columnsimportExport: Import and exportemails: Emailsposts: Posts moduleai: AI assistantanalytics: Analytics dashboardcloudflare: Cloudflare integrationlocales,localization: Content localizationtranslations: Admin translationslivePreview: Live preview