v1.7.6
A security release for the Next.js 16.4 remote code execution flaws, with Payload 3.91, reliable post publishing, custom Lexical node rendering and private AI writing settings.
On this page
✨ HighlightsLink to this section
1.7.6 is a security release first. Next.js published fixes for two critical unauthenticated remote code execution flaws and a denial of service on 14 October 2026, with no backport to 16.3, and Systhema's exact sharp pin blocked the workaround Vercel recommends. 1.7.6 requires Next.js 16.4.2 and sharp 0.35.5, and moves the Payload family to 3.91.0. Every project on 1.7.4 or 1.7.5 should upgrade now. The rest of the release is the batch of fixes that collected since 1.7.5: posts that publish reliably, custom Lexical nodes that render on the site, private AI writing settings, safer self-update and upgrade runs, Admin labels that stay in one language, and a round of accessibility, right-to-left and styling fixes.
Security: Next.js 16.4.2 and sharp 0.35.5Link to this section
| Advisory | Severity | Affected | Fixed in |
|---|---|---|---|
| GHSA-2464-fhr4-w9xw, unauthenticated RCE in the image optimizer with SVG | critical | next >=10.0.0 <=16.4.0 | next 16.4.1, or sharp 0.35.5 |
GHSA-673q-mjm6-r5xr, unauthenticated RCE in use cache returning typed arrays | critical | next >=16.0.0 <=16.4.0 | next 16.4.1 |
| GHSA-p3m5-59mp-jv5r, denial of service in App Router Server Actions | high | next >=16.0.0 <=16.4.0 | next 16.4.1 |
| GHSA-xqc2-xf4c-x3h3, unauthenticated RCE in React Server Components | critical | react-server-dom-* 19.3.0 | 19.3.1 (Next vendors its own copy) |
| GHSA-wq5f-xc86-pv6w, librsvg vulnerability | high | sharp <0.35.5 | sharp 0.35.5 |
16.4.1 is the first fixed 16.x release, so every project on 16.3.8, the floor 1.7.4 set, is affected. The image-optimizer flaw sits in sharp's bundled librsvg on glibc Linux, and Vercel's workaround is sharp 0.35.5 or later. Systhema pinned sharp to exactly 0.35.4 in the Payload template, the root overrides and the packages, so no consumer could take that workaround. These advisories are not yet in GitHub's global advisory database, so pnpm audit and dependency dashboards built on it do not report them.
The next peer range of @systhemaui/next and @systhemaui/payload is now ^16.4.2, and the sharp peer of @systhemaui/payload is ^0.35.5 (was ^0.34.2 || ^0.35.0). systhema upgrade reads those peers, so it raises next to ^16.4.2, sharp to ^0.35.5 in Payload projects, and the whole Payload family to ^3.91.0. New projects also get pnpm.overrides floors for compression 1.8.2, shell-quote 1.11.0 and source-map-js 1.2.2 (#278).
Payload 3.91.0Link to this section
Payload 3.91.0 carries no security fixes of its own (3.90.2 already had October's), but it is the version the bundled patches now target. It brings nodemailer 10, activity-aware Admin sessions and a faster rich text client schema map. Both bundled patches are regenerated as @payloadcms__ui@3.91.0.patch and @payloadcms__richtext-lexical@3.91.0.patch and keyed ^3.91.0; every hunk carries over, and systhema upgrade and systhema doctor install them. Payload 3.91 needs Node 20.18.1 or later, adds a _parent_id index to localized tables, and moves @payloadcms/email-nodemailer to nodemailer 10; the Upgrade section below says what that means for your project (#278).
Posts publish reliablyLink to this section
Publishing a post could fail with "The following field is invalid: slug", and scheduled publishes failed in the job queue with the same error. Three causes, three fixes (#276):
- A taken URL path gets the next free suffix. On Posts, Categories and Tags the slug hook appends
-2,-3and so on until the path is free, counting trashed documents too, because the unique index covers them. Before, the save failed on the index. - A blank URL path is filled from the title, the same as a missing one, so a published post always has a URL.
- The slug no longer freezes on title blur. Leaving the title field quickly, for example after pasting a title, cancelled the debounced sync and left the slug empty or cut short. Blur now runs the pending sync first.
A post published without a date now gets the publish time as its publishedAt, also when per-locale publishing publishes every locale at once. Listings sort by -publishedAt and PostgreSQL puts empty dates first, so undated posts used to sit above every dated one. A date the editor set is kept. Existing undated posts need a one-off backfill in each project.
Post bodies can also hold a Gallery block now. The default post editor turns on the new opt-in regionLexicalEditor({ withGallery: true }) flag, so projects with contentEditor: 'region' or no option get it without a code change (#263).
Custom Lexical nodes render on the siteLink to this section
A project that added its own Lexical feature with a custom node saw Payload's unknown node placeholder on the published page, because Systhema's renderer had no extension point for nodes. The new customNodeConverters option of withSysthema() takes a converter per node type; under livePreview.mode: 'client', pass the same entries to registerSysthemaClientNodeConverters from @systhemaui/payload/next/client. Built-in node types are kept unless an entry sets override: true, and block types stay on customBlocks. In development, client-mode preview warns when a node converter is registered on only one side (#271).
withSysthema(payloadConfig, {
customNodeConverters: [{ nodeType: 'callout', converter: calloutConverter }],
})AI writing settings are privateLink to this section
Any user with ai.generate could read and overwrite another user's AI writing settings (systhemaAiSettings: tone, dials and custom instructions), admins included. The group is now visible and editable only by its owner, when they hold ai.generate, and by dev users. An admin can still save another user's name, avatar and roles; Payload strips the settings from that request without an error. The new selfOrDevField(cap) access helper, exported from @systhemaui/payload, protects a project's own per-user fields the same way (#275).
Safer self-update, upgrade and doctorLink to this section
Four changes for real projects (#269):
systhema self-updaterefuses a downgrade. On a canary, alpha, beta or internal CLI, or inside a project on such a version,latestis older. The command now names the channel to stay on, asks in a terminal (default no), and exits 1 under--yesor without a TTY.--target <channel>or the new--allow-downgradeinstalls as asked.systhema upgradepins pnpm on a project with nopackageManager, using the pnpm new projects get whenpnpm-lock.yamlallows it. Its installs pass--config.confirm-modules-purge=false, so a pnpm switch no longer stalls on an invisible question or aborts without a TTY.- The upgrade refreshes project-scope skills only.
--user-skillsadds user scope back;systhema skills refreshstill covers both. - New
stale-import-mapdoctor check (fast). It flags a PayloadimportMap.jsthat names a@systhemaui/payloadentry the installed package no longer ships, misses an Admin provider (the Admin renders blank), or has no AI component while an AI provider is configured.--fixrunspayload generate:importmap.
The Admin stays in one languageLink to this section
Switching the Admin language left native block and field labels in the previous language, because Payload caches the client field schema without the language. The bundled UI patch now keys those caches by language and entity. Row actions, nested validation messages, Live Preview device labels and the Add block and Drag to move labels are translated in all seven Admin languages, and broken interpolation tokens in French, Arabic and Slovak are fixed (#262).
⬆️ UpgradeLink to this section
pnpm add -g @systhemaui/cli@latest
cd <your-project>
systhema upgrade --dry-run
systhema upgrade --yesNo codemods, token migrations or database hooks run in this release. systhema upgrade raises next, sharp and the Payload family through the peer check, pins pnpm if the project has no packageManager, and installs both regenerated Payload patches. Then install, rebuild and redeploy.
Check that next resolves to 16.4.2 or later after the install (pnpm why next). If your project pins sharp or next in its own pnpm.overrides, raise those entries yourself: the peer check rewrites only Payload-family overrides.
Node 20.18.1 or later. Payload 3.91 raises its Node floor. New projects declare engines.node: ">=20.18.1"; check your build host and runtime image.
The new _parent_id index on localized tables is additive. Push-mode databases get it on boot. On a push-disabled PostgreSQL database, add it with systhema migrate --schema or a Payload migration. Nothing breaks without it.
Your own nodemailer transport. A project that passes its own nodemailer 9 transport instance to @payloadcms/email-nodemailer must move to nodemailer 10. Systhema's scaffolded SMTP and SendGrid modules pass transportOptions and need nothing.
Custom Payload patches. systhema upgrade and systhema doctor install @payloadcms__ui@3.91.0.patch and @payloadcms__richtext-lexical@3.91.0.patch. A patch path you point at yourself is never overwritten; combine your changes with the new patches by hand.
New security floors reach new projects only. Copy the compression, shell-quote and source-map-js entries from scripts/security-overrides.json into your pnpm.overrides and run pnpm install.
Regenerate the import map if systhema doctor reports stale-import-map: systhema doctor --fix, or payload generate:importmap.
Backfill undated posts if your listings show old posts on top: set publishedAt on published posts that have none.
⚠️ Breaking & Behavioral ChangesLink to this section
1. Next.js 16.4.2, sharp 0.35.5, Payload 3.91 and Node 20.18.1Link to this section
The next peer of @systhemaui/next and @systhemaui/payload is ^16.4.2, the sharp peer of @systhemaui/payload is ^0.35.5, and every payload and @payloadcms/* peer is ^3.91.0. systhema upgrade moves the dependencies; a project that pins the Payload family to one exact version is moved to exactly 3.91.0. Payload 3.91 needs Node 20.18.1 or later. Manual: your own next or sharp overrides, and your own nodemailer 9 transport (#278).
2. Radio and checkbox ids are generatedLink to this section
FormRadio and FormCheckbox used name as the default id, so radios in one group shared an id, and RadioField and CheckboxField used each item's value, so two yes/no groups collided. An input without an explicit id now gets a generated one. Manual: CSS, scripts or htmlFor that target #<name> or #<value> need an explicit id (#267).
3. aria-invalid follows the visible errorLink to this section
FormFieldShell no longer sets aria-invalid="true" whenever invalidMessage is present; Payload's form block always passes one, so every such field was announced as invalid on page load. Controls set aria-invalid from the is-invalid class, and an explicit aria-invalid prop wins. The Payload form block adds is-invalid when its own validation rejects a value (#267).
4. self-update --yes refuses a downgradeLink to this section
A script that runs systhema self-update --yes on a prerelease CLI, or inside a prerelease project, now exits 1 when latest is older. Pass --target <channel> or --allow-downgrade. systhema upgrade no longer refreshes user-scope skills unless you pass --user-skills (#269).
5. AI writing settings are owner-onlyLink to this section
Admins no longer see or change another user's systhemaAiSettings; dev users still can. On create, only a dev user can set the group (#275).
6. A custom block is no longer offered inside itselfLink to this section
A custom block registered for a tier is left out of that tier's editors in its own rich-text fields, so payload generate:types stops overflowing the stack. A block that can still reach itself through built-in blocks gets a warning with the full path; it never throws. A document that already nests a block inside itself fails validation on its next save for the nested copy (#268).
7. Post slugs are suffixed, and published posts get a dateLink to this section
A post, category or tag saved onto a taken URL path gets -2, -3 and so on instead of a validation error. A post published without publishedAt gets the publish time (#276).
8. Layout and styling changes you can seeLink to this section
- Every
Columnsandflex-gridgap is 1px wider. Gaps used to render 1px short of their token; they now use the exact value, and span widths round down instead (#273). - Divided
Columnsclip.columns-divide-xandcolumns-divide-yno longer draw a divider after the last column of a row or under the last row. They useoverflow: clip, so.column-stickykeeps working;overflow-visibleturns the clipping off (#277). - Parallax modifiers do what they say.
.parallax--bigzooms deeper to cover its longer travel, and.parallax--noscaletravels inside a taller box without zooming (#273). blocks.rootVariables,blocks.variablesUtilitiesandblocks.applicationLayoutare honoured. They were emitted even when set tofalse(#273).- An unprefixed
text-h1totext-h6on<Heading>replaces the level's default size, so it can enlarge a heading as well as shrink it (#273). - HTML projects configure parallax through
window.__SYSTHEMA_PARALLAX__. The bundle no longer imports the Node config loader;systhema.config'sparallaxkey was never readable in the browser (#270).
🐛 Fixes & Internal ImprovementsLink to this section
Bug FixesLink to this section
- Draft-mode homepage —
/no longer returns 404 in draft mode when the homepage is published but has newer unpublished or autosaved changes; public visitors are unaffected (#261) - Client-mode live preview keeps up — archive theme, background, padding, gaps, layout and Load-more button update while you edit, Posts blocks and forms stay after the first update, and the archive search filter no longer drops
__livePreviewfrom the URL (#266) - Inline block drawers — Button, Chip, Icon and Avatar drawers no longer show Stack Related Settings outside a Stack, a drawer save rejected by server validation now says so, and visually hidden button and chip titles are hidden in the editor too (#266)
- Nested Stack renders again, and editing a component revalidates the pages that embed it (#268)
- Accordion shorthands work from Server Components —
Accordion.section,AccordionTitle.buttonandAccordionContent.divtake the base props,expandedis followed after mount, andonExpandedChangereports toggles (#267) - The header's mobile menu works inside a layout wrapper; it used to make its own ancestor inert (#267)
- Form and byline accessibility —
is-invalidon a radio or checkbox styles the whole control,CheckboxField requiredMode="any"works with uncontrolled items, the HTML-projects accordion inerts collapsed panels, and the post byline avatar is decorative (#267) - Media play buttons from Server Components — a
MediaWrapperwritten in a Server Component, including every Payload media converter, becomes the play button after hydration; an autoplaying video never does (#272) - Smaller component fixes —
Stack wrap="wrap"addsflex-wrap, custom list marker icons are sized, Carousel forwards extraFigureprops, the nextAvatardefaultssizesto its width instead of100vw, and Footer bottom-navigation links get their own props slot (#272) @systhemaui/nextparity — the provider and listeners acceptresetKey, and the helpers@systhemaui/reactexports are re-exported (#272)- The HTML-projects bundle loads in the browser again — parallax, the cookie banner and
window.SysthemaJSstart; the bundle also drops from 54.7 KB to 34.7 KB (#270) - Right-to-left projects — lists, quotes, select and search icons, checkboxes and radios, file inputs, features, header icons, the share rail and column dividers use logical properties (#270)
animate-disableshows a fade-in's end state, the skip link follows the primary button radius, and the gallery, carousel and posts pagination buttons render their shadow (#270)- Tailwind v4 utilities —
flex-grid-gap-*andflex-grid-divide-*emit CSS again, including arbitrary values;autotext-*keeps arbitrary units and drops the bogusautotext-shadow-*classes (#273) - Full-bleed figures outside an Article lose their radius and shadow, and an ancestor
stagger-delay-*no longer retunes the posts listing (#273) - Translucent colours in TOON references keep their alpha (
#00000040instead of#000000) (#273) - A linked parallax video is clickable — a parallax wrapper that is itself a link or button takes pointer events (#274)
- Gallery in development — Strict Mode no longer crashes a page with "Cannot read properties of undefined (reading 'navigation')" (#265)
- Admin copy — "Justify content" and "Paste YouTube URL" are spelled right, and the
linkFieldsJSDoc points at@systhemaui/payload/utils/internalRefToHref(#268)
Internal / MonorepoLink to this section
- Dependency refresh — every in-range bump, plus happy-dom 21, tsup 9 (building needs Node 22.18+), vite 8 and emojibase-data 18;
@lexical/utilsmoves to^0.50.0(#278) happy-domclient tests for react and next —*.client.test.tsxcovers behaviour that needs effects (#272)- New guard tests —
'use client'modules may not attach component statics, everyvar(--x, fallback)must be defined or listed inCONSUMER_OVERRIDABLE, no custom property references itself, and physical left/right styles need an allowed reason (#267, #270) - The HTML bundle is pinned browser-only by
js/browserBundle.test.ts(#270)
List of all changesLink to this section
🚀 FeaturesLink to this section
payloadLink to this section
- feat(payload): render project lexical nodes through registered converters (#271) (97d390fe)
- feat(payload): offer the gallery block in the default post body (#263) (e52c80d0)
🐛 Bug fixesLink to this section
cliLink to this section
- fix(cli): make self-update, upgrade and doctor safer on real projects (#269) (97d0eadf)
coreLink to this section
- fix(core): clip column dividers at row and grid edges (#277) (057dc5d7)
- fix(core): fix the html bundle, animate-disable, skip link and rtl styles (#270) (06c748ae)
- fix(core): let a link or button parallax wrapper take pointer events (#274) (f0a4d95f)
core, reactLink to this section
- fix(core,react): repair v4 utilities, parallax modifiers and toggles (#273) (212254ec)
core, react, nextLink to this section
- fix(core,react,next): media play targets from server components and fixes (#272) (45fa8225)
core, react, next, payloadLink to this section
- fix(core,react,next,payload): accordion, form, header menu and byline a11y fixes (#267) (6ed59801)
next, payloadLink to this section
- fix(next,payload): require next 16.4.2, sharp 0.35.5 and payload 3.91 (#278) (5b0b297)
payloadLink to this section
- fix(payload): keep post slugs unique and date posts on publish (#276) (d0aab317)
- fix(payload): keep AI writing settings private to their owner and developers (#275) (ae11aff5)
- fix(payload): stop custom block self-nesting and fix stack, component revalidation and copy (#268) (f70613df)
- fix(payload): keep live preview and inline block drawers in sync with edits (#266) (94de6c2f)
- fix(payload): keep native admin translations consistent (#262) (60fcfd29)
- fix(payload): render a homepage with unpublished changes in draft mode (#261) (cc3aa612)
reactLink to this section
- fix(react): skip a destroyed swiper instance in the gallery (#265) (a23ac88c)