Docs
Systhema Design (opens in new tab)
Changelog

v1.7.4

A security release that requires Next.js 16.3.8 for three critical remote code execution advisories, plus fourteen audit fixes, inline post categories and tags, and password tools in the Admin.

On this page

✨ HighlightsLink to this section

1.7.4 is a security release first. Next.js has shipped fixes for three critical remote code execution advisories, and every Systhema project should upgrade: @systhemaui/next and @systhemaui/payload now require Next.js 16.3.8, and systhema upgrade raises next to ^16.3.8 in your package.json. The Payload family moves to 3.90.2 at the same time. The rest of the release is the work merged since 1.7.3: a repo-wide audit with fourteen bug fixes, consistent withSysthema() defaults, inline creation of post categories and tags, password tools in the Admin, and a run of smaller Admin and CLI fixes.

Next.js 16.3.8 is the minimumLink to this section

Three critical Next.js advisories are fixed in the 16.3 line:

Next.js 16.3.8 also fixes a high-severity SSRF in image optimization (GHSA-cjq9-62q9-8jv4 (opens in new tab)) and several medium cache-poisoning and disclosure issues. Both @systhemaui/next and @systhemaui/payload declare next: ^16.3.8 as their peer range, so the upgrade's peer check writes that range into the project. The Payload family peer floor is ^3.90.2.

Newly scaffolded next and payload projects also get pnpm.overrides floors for transitive packages with high or critical advisories (brace-expansion, browserslist, fast-uri, immutable, lodash, minimatch, picomatch, tmp, undici, ws), each kept within its major. Existing projects do not get them automatically; see the upgrade section.

Create post categories and tags as you typeLink to this section

The Posts Category and Tags fields offer Create "…" when nothing matches the typed name (ignoring case). Picking it creates the document and selects it, with no drawer and no separate save, and focus stays in the input. On Tags, a comma commits the typed name and a pasted comma- or line-separated list selects the existing tags and creates the missing ones once each. The field is Payload's own relationship input underneath, so search, sorting, drag reorder and the "+" drawer work as before. It falls back to the plain field for editors without create permission on the collection, for admin.allowCreate: false and for polymorphic relations (#243 (opens in new tab)).

Password tools in the AdminLink to this section

Every Admin password input gets a show/hide button. New-password forms (create first user, account, user edit, Users → Create new, reset password) also get a generate button that fills both fields with a 20-character random password and copies it to the clipboard, a live rule checklist and a note when zxcvbn rates the password easy to guess. The rules (at least 8 characters, an uppercase letter and a number) are enforced on the server for create, update and reset password over REST and GraphQL; the Local API is not checked, and existing passwords keep working until they are changed. users.passwords.enforce: false keeps the checklist without rejecting. An opt-in users.passwords.jev setting asks TypeSafe's Jev for a second opinion; it sends the candidate password to TypeSafe and is off by default. See Passwords (#244 (opens in new tab)).

withSysthema(config) means the same as withSysthema(config, {})Link to this section

Called without a second argument, withSysthema used to turn the Components collection and the embed block off; called with any options object, both were on. Both forms now resolve the same way, with Components and embed on by default. The Google Maps block is on exactly when a non-blank apiKey is forwarded, and apiKey accepts undefined, so googleMaps: { apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY } works directly. The hero, feature and post hero media types offer Google Maps only when a key is configured (#237 (opens in new tab)). The schema consequences are under Breaking & Behavioral Changes.

Client-side live preview in new projectsLink to this section

New Payload projects start on livePreview: { mode: 'client' }, so the Admin preview re-renders unsaved changes on every keystroke. The Components preview now follows livePreview.mode too. The plugin default stays 'server', so existing projects keep their behaviour; add livePreview: { mode: 'client' } to opt in (#227 (opens in new tab)).

Purge Cloudflare from your own collectionsLink to this section

autoPurgePaths(payload, paths) is now exported from @systhemaui/payload, so a project with the Cloudflare module can purge the edge cache from its own collection hooks the same way Systhema's Pages, Posts and Redirects hooks do. See Cloudflare.

A Next project upgraded to Payload gets its Payload routesLink to this section

systhema-core payload create-app-files now writes Payload's src/app/(payload)/ route group (the Admin page and 404, layout, custom.scss, a starter importMap.js, and the REST, GraphQL and GraphQL Playground routes) when the project has no such directory. The group is written whole or not at all, never overwritten and never recorded in the managed-files ledger, so every existing Payload project is untouched. After writing it, the command tells you to run payload generate:importmap (#241 (opens in new tab)).


⬆️ UpgradeLink to this section

pnpm add -g @systhemaui/cli@latest
cd <your-project>
systhema upgrade --dry-run
systhema upgrade --yes --allow-database

The upgrade bumps @systhemaui/*, raises next to ^16.3.8, and moves payload and every @payloadcms/* package to 3.90.2. A project that pins the Payload family exactly is moved to exactly 3.90.2, with whole-family overrides, as in 1.7.3. The bundled Lexical patch is keyed to ^3.90.0 and applies unchanged.

One codemod runs: rename-deduplicated-translation-keys rewrites the 130 Admin translation keys this release removes to the keys that replace them, wherever your source passes one to systhemaLabel(), a drawer labelKey or t('systhema:…').

The upgrade regenerates the import map, which picks up the new Admin components (the Google Maps-aware media-type field, the creatable relationship field and the password assist provider). If you commit importMap.js and skip the upgrade's regeneration, run payload generate:importmap yourself; a provider missing from the map blanks the Admin.

If you call withSysthema(config) with no second argument, the Components collection is now on, which adds a components table. Run the schema push in development, or create a Payload migration before deploying to a database with push disabled:

pnpm payload migrate:create add-components
pnpm payload migrate

On a push-mode PostgreSQL deployment, systhema migrate --schema adds it. To keep the old behaviour instead, pass { components: false, embed: false }.

On Cloudflare, let PURGE through any method-restricted cache rule. Single-URL purges are evaluated with the request method PURGE, so a cache rule that matches only GET and HEAD never sees them. Add PURGE to the rule's method list, or the posts purges this release adds have no effect.

Optional: add the security overrides to an existing project. The pnpm.overrides floors new projects get are listed in scripts/security-overrides.json (opens in new tab). Copy the entries into your project's pnpm.overrides, run pnpm install, and check with pnpm audit.


⚠️ Breaking & Behavioral ChangesLink to this section

1. Next.js 16.3.8 or later is requiredLink to this section

The next peer range of @systhemaui/next was >=14.2.35 … || ^16.1.0 and that of @systhemaui/payload was >=15.2.9 … || >=16.2.6 <17.0.0. Both are now ^16.3.8. A project still on Next 14 or 15 is moved to Next 16 by the upgrade's peer check, which is a major Next.js upgrade; read Next's 16 upgrade guide before running it on such a project. Projects already on Next 16 only move within the minor.

2. Components and embed are on without options; Google Maps needs a keyLink to this section

withSysthema(config) now resolves exactly like withSysthema(config, {}) (#237 (opens in new tab)):

  • Components and embed default to on in both call forms. A project that called withSysthema with no options gets a new components table and the embed block. Manual: push or migrate the schema, or pass { components: false, embed: false }.
  • Google Maps is on exactly when a non-blank apiKey is forwarded. A project that passed googleMaps with an empty or missing apiKey loses the block and the Google Maps media option until it sets a key; neither could render a map without one. enabled: false still turns the block off and keeps the key, so hero and feature map media still render.
  • A document that already stores mediaType: 'googleMaps' on a keyless project still validates, and the stored choice stays visible so an editor can switch away. Its map renders nothing instead of an empty iframe.

3. 130 Admin translation keys are removedLink to this section

Keys whose text was identical to another key in every shipped language are removed from all seven catalogs, and common:advanced_settings is merged into common:advancedSettings (English "Advanced settings"). A project that passes a removed key fails its typecheck. systhema upgrade rewrites them through the rename-deduplicated-translation-keys codemod; nothing is manual. Rendered labels are unchanged, except that the English seo.websiteName label is now "Website name" (#228 (opens in new tab), #231 (opens in new tab)).

4. Password rules are enforced over REST and GraphQLLink to this section

Creating a user, changing a password or resetting one through REST or GraphQL now fails when the password has fewer than 8 characters, no uppercase letter or no number. Scripts that create users over HTTP with weak passwords need stronger ones, or set users: { passwords: { enforce: false } }. The Local API, and therefore seeds, is exempt (#244 (opens in new tab)).

5. Developer accounts are hidden through read accessLink to this section

Accounts with the dev role and without admin are now excluded from every users read a non-dev user makes, including the relationship picker behind a post's Author field, and the Users list counts are correct. Two consequences differ from 1.7.3 (#242 (opens in new tab)):

  • An account with both dev and admin is now visible to non-dev users. It stays read-only to them.
  • Server code that reads users with overrideAccess: true is no longer filtered for a non-dev req.user. Public post bylines still name a dev author, and in the Admin an Author set to a hidden dev account shows as an ID.

6. blocks.<name> = { enabled: false } works for every blockLink to this section

Only grid honoured enabled: false in object form; other blocks kept emitting their CSS. Every object-form block now does. A grid object without enabled now counts as on (#228 (opens in new tab)).

7. create-app-files respects a deleted managed fileLink to this section

A managed file you deleted was recreated on every create-app-files --override. When the ledger records the path and the file is gone, the command now logs Skipped <path> (deleted locally). and keeps the ledger entry, so the decision persists. A path with no ledger entry is still created. Note that public/systhema-icon.svg backs the Admin favicon (#226 (opens in new tab)).


🐛 Fixes & Internal ImprovementsLink to this section

Bug FixesLink to this section

  • Post, category, tag and author changes purge Cloudflare — publishing, editing, unpublishing or deleting a post, category or tag, or editing an author, now purges the post URL, the homepage and the category, tag, archive and author listings. Before, these called only revalidatePath, and edge-cached HTML stayed stale
  • Creating a user without touching Roles works again — the Users roles field defaulted to the string 'editor' on a multi-select, so the role escalation guard threw. It now defaults to ['editor']
  • Drawer and option labels follow the current Admin language — Advanced settings drawer buttons and the option labels of iconGroupField, buttonGroupField and colorSelectField kept the language that first built the form after a server start. drawerField() accepts a labelKey, and labels from systhemaLabel() or definePayloadAdminTranslations() carry their key to the client (#231 (opens in new tab))
  • Localized SEO settings land in general_settings_locales — on a project with content localization, migrate-seo-data and migrate-emails-data resolve each field's destination from the config and write localized fields once per locale to the locales table instead of adding dead columns to the base table. Both SQL adapters; Mongo writes { [locale]: value } (#226 (opens in new tab))
  • The Mongo emails migration keeps existing tab keys — migrate-emails-to-general-settings sets each copied key instead of replacing the whole Emails tab, as the SEO migration and the SQL paths already did (#230 (opens in new tab))
  • JSON endpoints answer 400 on a non-object body — posts-list, revalidate, AI SEO, AI alt-text and send-test-email answered a null, array or scalar body with a 500 (#232 (opens in new tab))
  • EmailField accepts uppercase letters and + tags — the default pattern rejected Jane.Doe+news@Example.com, which affected every Systhema form (#239 (opens in new tab))
  • The CLI no longer crashes on file:, link:, workspace: or git specs — codemod, info, upgrade and doctor report the raw spec as the version (#240 (opens in new tab))
  • Hero descriptions are tagged with the slot tier — inline blocks in a page or post hero's description were tagged root, so their tier-gated fields did not match their editor. HeroBackgroundBlock also loses its aspectRatio field, which was never shown and had no effect (#236 (opens in new tab))
  • React and Next twins match — react's Header and Footer instances take the localizable labels prop and name every navigation landmark, has-subnav marks the subnavigation trigger on desktop and mobile in both packages, next's Link accepts disableAnimation, next's Card.a renders theme as data-theme, next's QuoteAvatar accepts preload, and @systhemaui/next re-exports getReactConfig and useDeferredVideoControls. <Video> in @systhemaui/next keeps auto as its default aspect ratio (#235 (opens in new tab), #228 (opens in new tab))
  • Systhema Design keeps the palette seed and loads more fonts — a seed survives a systhema.config.ts export and import, and variable fonts without a wght axis (such as Agu Display) load their static file instead of a 404 (#233 (opens in new tab))
  • Posts never show the author's email as the byline (#228 (opens in new tab))
  • HeroSimple, HeroBackground and HeroFeature render when registered — they had no converter. @systhemaui/payload/translations/languages/{fr,nl,cs,sk,ar} are exported, and @systhemaui/payload/gateway is no longer deprecated (#228 (opens in new tab))
  • The public useSysthemaLivePreview hook works on static routes, and a nested page slug is no longer re-slugified on mount (#228 (opens in new tab))
  • Redirect-plugin Admin translations are installed for every shipped language, not only Hungarian, and the General Settings email placeholders no longer always read "not set" (#228 (opens in new tab))
  • /sys/permalink-pattern logs and returns 500 on an error instead of a 200 (#228 (opens in new tab))
  • Core config fixes — isObject(null) is false and deepMerge accepts null, spacing functions are evaluated by a small arithmetic parser instead of eval(), and the config loader no longer serves a stale config after a same-second edit (#228 (opens in new tab))
  • Separator.hr and Separator.div work, and dotted Button, Chip and Accordion variants stop remounting their subtree on every render (#228 (opens in new tab))
  • A crashing doctor check reports a warning instead of disappearing from the output (#228 (opens in new tab))
  • systhema design exports and --mode merge carry the colour overlay, and @systhemaui/core/design gains the config helper block, composite text styles and colour overlay anchors (#228 (opens in new tab))
  • systhema create lists the sync script in its Next steps, before dev, with npm run for npm projects (#227 (opens in new tab))

Internal / MonorepoLink to this section

  • Security overrides in one place — scripts/security-overrides.json holds the transitive floors; the root package.json mirrors them and copy-bundle.mjs injects them into the bundled next and payload templates (tests/copy-bundle.test.ts). The monorepo's pnpm moves to 10.34.6
  • Dependency refresh — every in-range bump taken (swiper 14.3, next-intl 4.14.9, pg 8.23.1, typescript-eslint 8.71, prettier 3.9.9, vitest 5.0.3 and others). Majors stay held at Payload's website template baseline (TypeScript 6, ESLint 9, jsdom 28), and sharp stays at 0.35.4 to match it
  • Slop audit — duplicated helpers, dead exports and narrating comments removed, shared field and hook factories, six oversized payload modules split, apps/design running on the core design engine, and create-app-files reading the bundled templates/payload files instead of hand-copied strings (#228 (opens in new tab))
  • CI runs every @systhemaui/* test suite against PostgreSQL, plus Systhema Design's and the demo's checks against the packages built in the same run (#228 (opens in new tab))
  • twinDrift.test.ts compares the react and next twins; after #235 it carries no drift entries (#228 (opens in new tab), #235 (opens in new tab))
  • No more Unable to deserialize cloned data flakes — tests that boot Payload route drizzle-kit's push spinner to stderr, pinned by testUtils/stdout.test.ts (#238 (opens in new tab))
  • Generated Payload types refreshed and a stray release.yml__bak removed (#234 (opens in new tab))
  • The stable release checklist includes the GitHub Release step (f8067c36 (opens in new tab))

List of all changesLink to this section

🚀 FeaturesLink to this section

coreLink to this section

payloadLink to this section

payload, templates/payload, apps/demoLink to this section

♻️ RefactorsLink to this section

core, react, next, payload, cliLink to this section

🐛 Bug fixesLink to this section

cliLink to this section

coreLink to this section

core, payloadLink to this section

  • fix(core,payload): write localized SEO settings to the locales table and let the ledger respect a deletion (#226) (5162310f (opens in new tab))

next, payloadLink to this section

  • fix(next,payload): require Next.js 16.3.8, purge Cloudflare on post changes and default user roles to an array (#245 (opens in new tab))

payloadLink to this section

reactLink to this section

react, nextLink to this section

🧪 TestsLink to this section

payloadLink to this section

📚 DocsLink to this section

claudeLink to this section

🧹 ChoresLink to this section

payload, apps/demoLink to this section