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:
- GHSA-vcvr-r3jv-pc5j (opens in new tab): remote code execution through
next/og'sImageResponse(affects 16.2.0 to 16.3.5). - GHSA-2xp9-vwfh-vxw4 (opens in new tab): unauthenticated remote code execution in the image optimization API when AVIF files are used (affects 16.0.0 to 16.3.2, and 15.x before 15.5.24).
- GHSA-p293-qw3h-jr36 (opens in new tab): unauthenticated remote code execution on Windows-hosted servers (affects 16.0.0 to 16.3.2, and 15.x before 15.5.24).
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).
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).
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). 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).
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).
⬆️ UpgradeLink to this section
pnpm add -g @systhemaui/cli@latest
cd <your-project>
systhema upgrade --dry-run
systhema upgrade --yes --allow-databaseThe 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 migrateOn 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. 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):
- Components and embed default to on in both call forms. A project that called
withSysthemawith no options gets a newcomponentstable 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
apiKeyis forwarded. A project that passedgoogleMapswith an empty or missingapiKeyloses the block and the Google Maps media option until it sets a key; neither could render a map without one.enabled: falsestill 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, #231).
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).
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):
- An account with both
devandadminis now visible to non-dev users. It stays read-only to them. - Server code that reads users with
overrideAccess: trueis no longer filtered for a non-devreq.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).
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).
🐛 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
rolesfield 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,buttonGroupFieldandcolorSelectFieldkept the language that first built the form after a server start.drawerField()accepts alabelKey, and labels fromsysthemaLabel()ordefinePayloadAdminTranslations()carry their key to the client (#231) - Localized SEO settings land in
general_settings_locales— on a project with content localization,migrate-seo-dataandmigrate-emails-dataresolve 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) - The Mongo emails migration keeps existing tab keys —
migrate-emails-to-general-settingssets each copied key instead of replacing the whole Emails tab, as the SEO migration and the SQL paths already did (#230) - 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) EmailFieldaccepts uppercase letters and+tags — the defaultpatternrejectedJane.Doe+news@Example.com, which affected every Systhema form (#239)- The CLI no longer crashes on
file:,link:,workspace:or git specs —codemod,info,upgradeanddoctorreport the raw spec as the version (#240) - Hero descriptions are tagged with the
slottier — inline blocks in a page or post hero's description were taggedroot, so their tier-gated fields did not match their editor.HeroBackgroundBlockalso loses itsaspectRatiofield, which was never shown and had no effect (#236) - React and Next twins match — react's Header and Footer instances take the localizable
labelsprop and name every navigation landmark,has-subnavmarks the subnavigation trigger on desktop and mobile in both packages, next'sLinkacceptsdisableAnimation, next'sCard.arendersthemeasdata-theme, next'sQuoteAvataracceptspreload, and@systhemaui/nextre-exportsgetReactConfiganduseDeferredVideoControls.<Video>in@systhemaui/nextkeepsautoas its default aspect ratio (#235, #228) - Systhema Design keeps the palette seed and loads more fonts — a seed survives a
systhema.config.tsexport and import, and variable fonts without awghtaxis (such as Agu Display) load their static file instead of a 404 (#233) - Posts never show the author's email as the byline (#228)
HeroSimple,HeroBackgroundandHeroFeaturerender when registered — they had no converter.@systhemaui/payload/translations/languages/{fr,nl,cs,sk,ar}are exported, and@systhemaui/payload/gatewayis no longer deprecated (#228)- The public
useSysthemaLivePreviewhook works on static routes, and a nested page slug is no longer re-slugified on mount (#228) - 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)
/sys/permalink-patternlogs and returns 500 on an error instead of a 200 (#228)- Core config fixes —
isObject(null)isfalseanddeepMergeacceptsnull, spacing functions are evaluated by a small arithmetic parser instead ofeval(), and the config loader no longer serves a stale config after a same-second edit (#228) Separator.hrandSeparator.divwork, and dotted Button, Chip and Accordion variants stop remounting their subtree on every render (#228)- A crashing doctor check reports a warning instead of disappearing from the output (#228)
systhema designexports and--mode mergecarry the colour overlay, and@systhemaui/core/designgains the config helper block, composite text styles and colour overlay anchors (#228)systhema createlists the sync script in its Next steps, before dev, withnpm runfor npm projects (#227)
Internal / MonorepoLink to this section
- Security overrides in one place —
scripts/security-overrides.jsonholds the transitive floors; the rootpackage.jsonmirrors them andcopy-bundle.mjsinjects them into the bundlednextandpayloadtemplates (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/designrunning on the core design engine, andcreate-app-filesreading the bundledtemplates/payloadfiles instead of hand-copied strings (#228) - 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) twinDrift.test.tscompares the react and next twins; after #235 it carries no drift entries (#228, #235)- No more
Unable to deserialize cloned dataflakes — tests that boot Payload route drizzle-kit's push spinner to stderr, pinned bytestUtils/stdout.test.ts(#238) - Generated Payload types refreshed and a stray
release.yml__bakremoved (#234) - The stable release checklist includes the GitHub Release step (f8067c36)
List of all changesLink to this section
🚀 FeaturesLink to this section
coreLink to this section
- feat(core): write the Payload route group when a project has none (#241) (6ed23d8f)
payloadLink to this section
- feat(payload): create post categories and tags as you type (#243) (7056aa86)
- feat(payload): add admin password generator, show/hide and rules (#244) (e8451411)
payload, templates/payload, apps/demoLink to this section
- feat(payload,templates/payload,apps/demo): use client live preview (#227) (9ccd4b7f)
♻️ RefactorsLink to this section
core, react, next, payload, cliLink to this section
- refactor(core,react,next,payload,cli): slop audit fixes and cleanup (#228) (416bb9b0)
🐛 Bug fixesLink to this section
cliLink to this section
- fix(cli): stop crashing on file:, link: and workspace: package specs (#240) (6cc236b9)
coreLink to this section
- fix(core): keep the palette seed on config import and fix variable font URLs (#233) (71c5ed62)
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)
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)
payloadLink to this section
- fix(payload): hide developer accounts from every non-dev users read (#242) (8f2254f2)
- fix(payload): align the components, embed and googleMaps defaults (#237) (eebedb22)
- fix(payload): tag the page hero description with the slot tier (#236) (40899906)
- fix(payload): translate drawer and option labels in the current admin language (#231) (b39a6d50)
- fix(payload): keep existing emails tab keys in the mongo migration (#230) (f70b877f)
- fix(payload): answer 400 when an endpoint's JSON body is not an object (#232) (09380458)
reactLink to this section
- fix(react): accept uppercase letters in the email field pattern (#239) (176faf1a)
react, nextLink to this section
- fix(react,next): align the react and next component twins (#235) (e89a6068)
🧪 TestsLink to this section
payloadLink to this section
- test(payload): keep drizzle-kit's push spinner off the node:test channel (#238) (c250bdbd)
📚 DocsLink to this section
claudeLink to this section
- docs(claude): add the GitHub Release step to the stable checklist (f8067c36)
🧹 ChoresLink to this section
payload, apps/demoLink to this section
- chore(payload,apps/demo): regenerate generated types and drop a stray backup (#234) (2a8028ee)