Docs
Systhema Design (opens in new tab)
Unreleased

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: