Docs
Next

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.

OptionTypeDefaultDescription
aifalse | SysthemaAiOptionsfalseAI assistant configuration — field-level generation, selection-based lexical editor actions, layout generation with native blocks, and image generation/refinement.
analyticsfalse | SysthemaAnalyticsOptionsfalseAnalytics dashboard widgets — provider-backed traffic stats on the admin dashboard (KPI overview, visitors/views chart, top-N breakdowns, live visitors).
articleColorSettingbooleanfalseEnable/disable article color setting (articleTheme)
button{ defaultIconBefore?: string defaultIconAfter?: string }Button block defaults.
chip{ defaultIconBefore?: string defaultIconAfter?: string }Chip block defaults.
cloudflarefalse | SysthemaCloudflareOptionsfalseCloudflare 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.
componentsboolean | { enabled: boolean }trueComponents collection configuration Can be a boolean (true/false) or an object with enabled property
customAdminPathstring'/admin' (or '/' when customAdminURL is set)Custom admin route path override.
customAdminURLstringundefined (standard /admin path on the same domain)Custom admin panel URL for hosting admin on a separate domain or subdomain.
customBlocksCustomBlock[][]Custom blocks to register in lexical editors Each block specifies type (inline/block), data, and target editor(s)
customCollectionsCollectionConfig[][]Custom collections to register alongside Systhema collections Accepts standard Payload collection configs
customGlobalsGlobalConfig[][]Custom globals to register alongside Systhema globals Accepts standard Payload global configs
customLivePreviewURLstringundefinedPin live preview to a specific server URL.
customPageTemplatesSysthemaPayloadPageTemplate[][]Custom page templates to add to the default templates
customPostTemplatesSysthemaPostTemplate[][]Custom post templates to add to the default Single-Post template.
customTextStatesCustomTextState[][]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 }falseEmails global and Systhema email template wrapping configuration.
embedboolean | { enabled: boolean }trueEmbed 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.
footerboolean | { 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 })falseForms 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.
googleMapsfalse | { enabled?: boolean; apiKey?: string | null }on when `apiKey` is set, otherwise offGoogle Maps block configuration.
headerboolean | { enabled: boolean }{ enabled: true }Header configuration Can be a boolean (true/false) or an object with enabled property
iconsIconPacksConfig{ 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 falseImport/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.
localesSysthemaLocales | falseFrontend/content locales from the Systhema locale contract.
localizationSysthemaLocalizationOptionsWhich fields content localization reaches, and the per-project exceptions.
nestedDocsbooleantrueEnable/disable the nested docs plugin
orderCollectionsstring[]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
orderGlobalsstring[]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
postsboolean | PostsOptionsfalsePosts module configuration — gated blog/posts capability (collections, permalink engine, listing components).
redirectbooleanfalseEnable/disable the redirects plugin (requires
rolesRolesConfig{ 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 }trueEnable/disable the SEO plugin.
sitemapsSitemapsConfigundefinedSitemap configuration.
themes{ hidden?: string[] }Colour-system themes.
translationsSysthemaPayloadTranslationsOptionsPayload 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: