diff --git a/AGENTS.md b/AGENTS.md index f7f46895c..2c2f24620 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,6 +24,8 @@ The `generate-changelog.ts` script reads `## Skipped PRs` to avoid re-adding man PRs for a release are discovered from the release commits (the `(#NNNN)` references between the previous release tag and this version's tag/branch), **not** from a GitHub milestone — so a forgotten milestone no longer drops entries. Run `yarn generate:changelog --version --dry-run` to preview the PRs that would be included. +**Upgrade guide custom steps:** `upgrade-guide.mdx` is regenerated from a template by the release workflows. Put release-specific steps between the `{/* custom-steps:start ... */}` and `{/* custom-steps:end */}` markers, before ``. `scripts/generate-upgrade-guide.ts` keeps that block and the page `id` on every run. Anything outside the markers is overwritten. + **Review markers:** every generated entry gets an MDX comment beneath its `###` heading naming the PR author(s), e.g. `{/* REVIEW-PENDING @author — confirm this entry, then delete this line */}`. Each author must inspect their entry and delete that line. CI (`.github/workflows/check-release-notes.yml`, via `yarn check:changelog-review`) fails on any PR to master that still contains a `REVIEW-PENDING` marker, so release notes cannot be published until every entry is confirmed. ### Validation and Quality diff --git a/docs/developer-docs/6.x/website-builder/custom-component.ai.txt b/docs/developer-docs/6.x/website-builder/custom-component.ai.txt index 039aabb2d..a40f71eab 100644 --- a/docs/developer-docs/6.x/website-builder/custom-component.ai.txt +++ b/docs/developer-docs/6.x/website-builder/custom-component.ai.txt @@ -2,7 +2,7 @@ AI Context: Create Custom Component (website-builder/custom-component.mdx) Source of Information: 1. /Users/adrian/dev/lw/content/lessons/website-builder/editor-components.mdx - Learn Webiny lesson -2. @webiny/website-builder-nextjs SDK documentation +2. @webiny/sdk-nextjs SDK (6.5; was @webiny/website-builder-nextjs in 6.4) 3. Website Builder Next.js starter kit editor component examples Key Documentation Decisions: @@ -23,11 +23,11 @@ Understanding Editor Components: - Two parts: React component + manifest (name, label, group, inputs) - React component receives inputs prop typed with ComponentProps - Manifest tells editor what inputs to expose in sidebar -- createComponent() combines React component with manifest +- createWbComponent() combines React component with manifest (createComponent() is the same function, used by 6.4 projects) - editorComponents array registered with DocumentRenderer - Must be "use client" - SDK runs in browser, uses postMessage - Component names stored in page documents - stable identifiers -- groups.ts registers component groups for palette organization +- sdk/groups.ts exports a componentGroups array, passed to sdk.init() as wb.componentGroups (6.4 used registerComponentGroup() calls) - filter option can create catch-all groups Component Registration Pattern: @@ -86,10 +86,10 @@ Related Documents: - website-builder/theme.mdx - Theme configuration Key Code Locations: -- src/editorComponents/index.tsx - Component registration -- src/editorComponents/[ComponentName].tsx - Individual components -- src/contentSdk/groups.ts - Component group registration -- @webiny/website-builder-nextjs - SDK exports +- editorComponents/index.tsx - Component registration +- editorComponents/[ComponentName].tsx - Individual components +- sdk/groups.ts - Component group definitions +- @webiny/sdk-nextjs - SDK exports Tone Guidelines: - Technical and practical - how-to guide format diff --git a/docs/developer-docs/6.x/website-builder/custom-component.mdx b/docs/developer-docs/6.x/website-builder/custom-component.mdx index fa079b1fd..8f5184382 100644 --- a/docs/developer-docs/6.x/website-builder/custom-component.mdx +++ b/docs/developer-docs/6.x/website-builder/custom-component.mdx @@ -13,7 +13,7 @@ import bannerComponentRendered from "./assets/banner-component-rendered.png"; - What editor components are and how they work - How to create a React component that receives editor inputs -- How to register components with `createComponent()` and input types +- How to register components with `createWbComponent()` and input types - How to organize components into groups in the editor palette @@ -37,19 +37,19 @@ An editor component has two parts: **The manifest** — Metadata that tells the editor about the component: its name, label, group, and what inputs (configurable props) it exposes to the editor sidebar. -You combine both using `createComponent()` from `@webiny/website-builder-nextjs`, then add the result to the `editorComponents` array that you pass to `DocumentRenderer`. +You combine both using `createWbComponent()` from `@webiny/sdk-nextjs`, then add the result to the `editorComponents` array that you pass to `DocumentRenderer`. ## The editorComponents Array -The starter kit includes an `editorComponents` array in `src/editorComponents/index.tsx`: +The starter kit includes an `editorComponents` array in `editorComponents/index.tsx`: -```tsx src/editorComponents/index.tsx +```tsx editorComponents/index.tsx "use client"; -import { createComponent } from "@webiny/website-builder-nextjs"; +import { createWbComponent } from "@webiny/sdk-nextjs"; import { Hero1 } from "./Hero1"; export const editorComponents = [ - createComponent(Hero1, { + createWbComponent(Hero1, { name: "Webiny/Hero", label: "Hero #1", inputs: [] @@ -60,33 +60,35 @@ export const editorComponents = [ Key points: - The file is marked `"use client"` — component registrations must run on the client because the SDK communicates with the editor via the browser. -- `createComponent()` takes the React component as its first argument and the manifest as the second. +- `createWbComponent()` takes the React component as its first argument and the manifest as the second. Projects upgraded from 6.4 may still call it `createComponent()`, which is the same function. - `name` is a namespaced string — use a consistent convention like `"YourNamespace/ComponentName"`. Component names are stored in page documents, so treat them as stable identifiers. Renaming a component breaks existing pages. - `inputs` defines the configurable props that appear in the editor sidebar. An empty array means no inputs. - `group` (optional) links the component to a named component group in the editor palette. ## Component Groups -Component groups organize the editor's component palette into sections. They're registered in `src/contentSdk/groups.ts`: +Component groups organize the editor's component palette into sections. The starter kit defines them in `sdk/groups.ts`: -```typescript src/contentSdk/groups.ts -import { registerComponentGroup, type ComponentManifest } from "@webiny/website-builder-nextjs"; +```typescript sdk/groups.ts +import type { ComponentManifest, ComponentGroup } from "@webiny/sdk-nextjs"; -export const registerComponentGroups = () => { - registerComponentGroup({ +export const componentGroups: ComponentGroup[] = [ + { name: "basic", label: "Basic", description: "Components for simple content creation" - }); - registerComponentGroup({ + }, + { name: "custom", label: "Custom", description: "Assorted custom components", filter: (component: ComponentManifest) => !component.group - }); -}; + } +]; ``` +`sdk/initializeSdk.ts` passes the array to the SDK as `wb.componentGroups` in the `sdk.init()` call. + The `filter` option on the "custom" group is a catch-all: it collects any component that doesn't have an explicit `group` set in its manifest. ## Building a Custom Component @@ -95,11 +97,11 @@ Let's build a **Banner** component—a full-width colored strip with a headline ### Create the React Component -Create `src/editorComponents/Banner.tsx`: +Create `editorComponents/Banner.tsx`: -```tsx src/editorComponents/Banner.tsx +```tsx editorComponents/Banner.tsx import React from "react"; -import { ComponentProps } from "@webiny/website-builder-nextjs"; +import { ComponentProps } from "@webiny/sdk-nextjs"; interface BannerInputs { headline: string; @@ -124,25 +126,25 @@ export function Banner({ inputs: { headline, ctaLabel, ctaUrl } }: ComponentProp } ``` -Always type your component with `ComponentProps` from `@webiny/website-builder-nextjs`. Without it, TypeScript won't know the shape of the `inputs` prop. +Always type your component with `ComponentProps` from `@webiny/sdk-nextjs`. Without it, TypeScript won't know the shape of the `inputs` prop. ### Register the Component -Add the Banner to `src/editorComponents/index.tsx`: +Add the Banner to `editorComponents/index.tsx`: -```tsx src/editorComponents/index.tsx +```tsx editorComponents/index.tsx "use client"; -import { createComponent, createTextInput } from "@webiny/website-builder-nextjs"; +import { createWbComponent, createTextInput } from "@webiny/sdk-nextjs"; import { Hero1 } from "./Hero1"; import { Banner } from "./Banner"; export const editorComponents = [ - createComponent(Hero1, { + createWbComponent(Hero1, { name: "Webiny/Hero", label: "Hero #1", inputs: [] }), - createComponent(Banner, { + createWbComponent(Banner, { name: "Custom/Banner", label: "Banner", inputs: [ @@ -209,19 +211,20 @@ The SDK exports a factory function for each input type: | `createLexicalInput` | Rich text (Lexical editor) | | `createFileInput` | File / media picker | | `createSlotInput` | Slot for nesting other components | +| `createContentEntryInput` | One or more Headless CMS entries, picked by hand or queried | ## Example: Select Input To add a color theme selector to the Banner component: -```tsx src/editorComponents/index.tsx +```tsx editorComponents/index.tsx import { - createComponent, + createWbComponent, createTextInput, createSelectInput -} from "@webiny/website-builder-nextjs"; +} from "@webiny/sdk-nextjs"; -createComponent(Banner, { +createWbComponent(Banner, { name: "Custom/Banner", label: "Banner", inputs: [ @@ -260,7 +263,7 @@ createComponent(Banner, { Update the Banner component to use the `colorTheme` input: -```tsx src/editorComponents/Banner.tsx +```tsx editorComponents/Banner.tsx interface BannerInputs { headline: string; colorTheme: "primary" | "secondary" | "success"; diff --git a/docs/developer-docs/6.x/website-builder/how-it-works.ai.txt b/docs/developer-docs/6.x/website-builder/how-it-works.ai.txt index f74599360..8689ff7c6 100644 --- a/docs/developer-docs/6.x/website-builder/how-it-works.ai.txt +++ b/docs/developer-docs/6.x/website-builder/how-it-works.ai.txt @@ -36,7 +36,7 @@ Unique Website Builder Architecture: - Webiny stores ONLY page structure (which components + inputs) - Communication via postMessage API (handled by SDK) - Two modes: editing (live connection) and rendering (API fetch) -- Current OOTB support: Next.js via @webiny/website-builder-nextjs +- Current OOTB support: Next.js via @webiny/sdk-nextjs (6.5+; 6.4 used @webiny/website-builder-nextjs, which sdk-nextjs now wraps) - Future: Additional framework SDKs planned What Webiny Stores: @@ -53,7 +53,7 @@ What User's App Owns: - Complete control over frontend stack SDK Responsibilities: -- @webiny/website-builder-nextjs package +- @webiny/sdk-nextjs package - Editor Integration: postMessage communication during editing - Page Fetching: API calls to fetch published pages - Component Registration: utilities to register components for editor @@ -88,7 +88,7 @@ Related Documents: - core-concepts/webiny-sdk.mdx - General SDK concepts Key Code Locations (Webiny source): -- packages/website-builder-nextjs/ - Next.js SDK implementation +- packages/sdk-nextjs/ - Next.js SDK entry point (wraps packages/website-builder-nextjs/ and packages/cms-nextjs/) - packages/api-website-builder/ - Backend API for page storage - packages/admin/src/website-builder/ - Editor UI in Admin diff --git a/docs/developer-docs/6.x/website-builder/how-it-works.mdx b/docs/developer-docs/6.x/website-builder/how-it-works.mdx index 2cab23049..e90861c2b 100644 --- a/docs/developer-docs/6.x/website-builder/how-it-works.mdx +++ b/docs/developer-docs/6.x/website-builder/how-it-works.mdx @@ -20,7 +20,7 @@ import webinyWebsiteBuilder from "./assets/webiny-website-builder.png"; The Website Builder uses a unique architecture that separates content management from presentation. The editor runs in Webiny Admin and connects to your frontend app via an iframe. Your app owns all components and styles—Webiny only stores the page structure. -Webiny currently provides out-of-the-box support for Next.js through the `@webiny/website-builder-nextjs` SDK. Support for additional frameworks is planned for future releases. +Webiny currently provides out-of-the-box support for Next.js through the `@webiny/sdk-nextjs` SDK. Support for additional frameworks is planned for future releases. This approach ensures genuine WYSIWYG editing, no style conflicts, and full control over your frontend code. @@ -35,7 +35,7 @@ The Website Builder consists of two separate parts: alt="Website Builder editor interface showing component palette, canvas, and inputs sidebar" /> -**Your Frontend App** - Your frontend application with the Website Builder SDK installed. Currently, Webiny provides the `@webiny/website-builder-nextjs` SDK for Next.js out-of-the-box. Your app contains all component code, styles, and rendering logic. +**Your Frontend App** - Your frontend application with the Website Builder SDK installed. Currently, Webiny provides the `@webiny/sdk-nextjs` SDK for Next.js out-of-the-box. Your app contains all component code, styles, and rendering logic. ### How They Connect @@ -118,7 +118,7 @@ The Website Builder SDK provides: **Rendering Utilities** - Helps render page components with the correct inputs and layout. -Webiny currently provides the `@webiny/website-builder-nextjs` SDK for Next.js. The SDK is a thin layer that connects your app to Webiny without imposing constraints on your architecture. +Webiny currently provides the `@webiny/sdk-nextjs` SDK for Next.js. The SDK is a thin layer that connects your app to Webiny without imposing constraints on your architecture. diff --git a/docs/developer-docs/6.x/website-builder/setup-nextjs.ai.txt b/docs/developer-docs/6.x/website-builder/setup-nextjs.ai.txt index 81ac4e156..94ec29d4c 100644 --- a/docs/developer-docs/6.x/website-builder/setup-nextjs.ai.txt +++ b/docs/developer-docs/6.x/website-builder/setup-nextjs.ai.txt @@ -1,51 +1,48 @@ AI Context: Setup Next.js Project (website-builder/setup-nextjs.mdx) Source of Information: -1. /Users/adrian/dev/lw/content/lessons/website-builder/setting-up-website-builder.mdx - Learn Webiny lesson -2. Website Builder Next.js starter kit documentation -3. @webiny/website-builder-nextjs SDK documentation +1. /Users/adrian/dev/lw/content/lessons/website-builder/setting-up-website-builder.mdx - Learn Webiny lesson (original basis) +2. github.com/webiny/website-builder-nextjs, `starter-kit-6.5.x` branch - 6.5 starter kit +3. webiny-js `release/6.5.0`: packages/sdk-nextjs, packages/frontend-settings (Configure Frontend dialog), packages/api-website-builder (NextjsConfig, ApiKeyInstaller) Key Documentation Decisions: -1. Removed ChapterOverview component (Learn Webiny-specific) -2. Removed Quiz component (Learn Webiny-specific) -3. Removed course-style language ("In this lesson we'll...") -4. Simplified Overview - now focuses on setup, not architecture explanation -5. Added link to how-it-works.mdx for architecture explanation -6. Removed redundant Architecture section (covered in how-it-works) -7. Removed ASCII diagram - kept prose explanation where needed -8. Removed step numbering in headings - used descriptive section names instead -9. Removed images/screenshots - will add if needed later -10. Changed "Step X" headings to descriptive names (Installation, Configuration, etc.) -11. Removed detailed project structure FileTree - kept brief description -12. Removed "See It Rendered" as separate section - integrated into "Create Your First Page" -13. Kept all technical accuracy about SDK, API keys, environment variables +1. Removed ChapterOverview and Quiz components (Learn Webiny-specific) and course-style language +2. Overview focuses on setup. Architecture lives in how-it-works.mdx +3. 6.5 rewrite: single `@webiny/sdk-nextjs` package, `NEXT_PUBLIC_WEBINY_*` env vars, root-level project layout +4. 6.5 rewrite: clone uses `--branch starter-kit-6.5.x`, because each minor version has its own kit branch. Update the branch name for future minors. +5. 6.5 rewrite: the kit's package.json doesn't list the SDK, so the page has an explicit `yarn add @webiny/sdk-nextjs@~6.5.0` step. Yarn because the kit ships a yarn.lock. The peer pins that made npm fail with ERESOLVE on 6.5.0-beta.0 were fixed in webiny-js #5776 (verified: plain `npm install` works on 6.5.0-beta.1), so the old peer-warning alert was removed. +6. 6.5 rewrite: removed the configure-nextjs-menu, configure-nextjs-dialog and api-key-auto-created screenshots. They showed the old Support > Configure Next.js menu, the old env var names, and the old "Website Builder" key. Add new screenshots of Dev Tools > Configure Frontend and the "Frontend Integration" key when available. +7. Upgrade and existing-app scenarios live in release-notes/6.5.0/upgrade-nextjs-frontend.mdx, next to the 6.5.0 upgrade guide. This page links there instead of covering them. Understanding Website Builder Setup: -- Editor loads Next.js app in iframe during editing -- Components and styles live entirely in Next.js project +- Editor loads the Next.js app in an iframe during editing +- Components and styles live entirely in the Next.js project - Webiny stores only page structure and component inputs -- @webiny/website-builder-nextjs SDK handles editor ↔ app communication -- Starter kit at github.com/webiny/website-builder-nextjs -- SDK versions must match Webiny version -- Three env vars needed: API_KEY, API_HOST, API_TENANT -- API key auto-created by Website Builder (read-only) -- Configure Next.js dialog in Admin provides all credentials -- Catch-all route [[...slug]]/page.tsx renders all pages +- @webiny/sdk-nextjs handles editor <-> app communication, page fetching, CMS content, and CMS live preview +- SDK minor version must match the Webiny version +- Env vars: NEXT_PUBLIC_WEBINY_API_KEY, _API_HOST, _API_TENANT, optional _ADMIN_HOST (used in the frame-ancestors CSP header) +- Configure Frontend dialog (Dev Tools menu): Frontend Domain field (used by the WB editor and CMS live preview) plus a Next.js tab with env vars +- "Frontend Integration" API key is auto-created per tenant, read-only (WB, CMS, languages) +- Upgraded projects have the legacy "Website Builder" key. The dialog then shows NEXT_PUBLIC_WEBSITE_BUILDER_* names. +- Catch-all route app/(site)/[[...slug]]/page.tsx renders pages +- app/(site)/articles/ is a CMS rendering and live preview example - Preview API route enables draft mode for unpublished pages Related Documents: - website-builder/how-it-works.mdx - Architecture explanation (prerequisite reading) +- release-notes/6.5.0/upgrade-nextjs-frontend.mdx - Upgrading 6.4 kits, adding the SDK to existing apps - website-builder/theme.mdx - Theme configuration (next step) - website-builder/custom-component.mdx - Creating components (next step) +- headless-cms/live-preview.mdx - CMS live preview Key Code Locations: -- github.com/webiny/website-builder-nextjs - Official starter kit -- src/app/[[...slug]]/page.tsx - Page rendering -- src/editorComponents/ - Component registration -- src/contentSdk/ - SDK initialization +- github.com/webiny/website-builder-nextjs/tree/starter-kit-6.5.x - Official starter kit +- app/(site)/[[...slug]]/page.tsx - Page rendering +- editorComponents/ - Component registration +- sdk/ - SDK initialization Tone Guidelines: -- Technical and practical - setup guide format +- Technical and practical, setup guide format - Direct instructions without unnecessary narrative - Keep "Prerequisites" and "Next Steps" sections - Use code blocks for all commands and config diff --git a/docs/developer-docs/6.x/website-builder/setup-nextjs.mdx b/docs/developer-docs/6.x/website-builder/setup-nextjs.mdx index e62ea70b2..c66f03e1a 100644 --- a/docs/developer-docs/6.x/website-builder/setup-nextjs.mdx +++ b/docs/developer-docs/6.x/website-builder/setup-nextjs.mdx @@ -1,14 +1,11 @@ --- id: wb9setup title: Setup Next.js Project -description: Set up the Website Builder Next.js starter kit and connect it to your Webiny project. +description: Set up the Next.js starter kit and connect it to your Webiny project with the Webiny SDK. --- import { Alert } from "@/components/Alert"; import { Image } from "@/components/Image"; -import configureNextjsMenu from "./assets/configure-nextjs-menu.png"; -import configureNextjsDialog from "./assets/configure-nextjs-dialog.png"; -import apiKeyAutoCreated from "./assets/api-key-auto-created.png"; import notFound from "./assets/not-found.png"; import createPageDialog from "./assets/create-page-dialog.png"; import heroEditor from "./assets/hero-editor.png"; @@ -24,96 +21,89 @@ import heroRendered from "./assets/hero-rendered.png"; ## Overview -This guide walks through setting up the Website Builder Next.js starter kit and connecting it to your Webiny project. The starter kit provides pre-configured routing, SDK setup, and rendering so you can start building pages immediately. +This guide walks through setting up the Next.js starter kit and connecting it to your Webiny project. The starter kit comes with routing, SDK setup, and rendering already wired up, so you can start building pages right away. It renders Website Builder pages, and it includes an example of rendering Headless CMS entries with live preview. + +If you already have a Next.js app, or you built one on an earlier version of the starter kit, see [Upgrade Next.js Frontend to 6.5.0](/release-notes/6.5.0/upgrade-nextjs-frontend) instead. For an explanation of how the Website Builder architecture works, see [How It Works](/{version}/website-builder/how-it-works). ## Prerequisites - Running Webiny project (Core and API applications deployed) -- Node.js 20.9+ installed (Node.js 24+ still required if working with Webiny CLI) +- Node.js 22+ installed (Node.js 24+ still required if working with Webiny CLI) - Familiarity with Next.js App Router ## Installation ### Clone the Starter Kit -The official Next.js starter kit provides pre-configured routing, SDK setup, and rendering: +Each Webiny minor version has a matching starter kit branch. For Webiny 6.5.x, clone the `starter-kit-6.5.x` branch: ```bash -git clone https://github.com/webiny/website-builder-nextjs.git my-website +git clone --branch starter-kit-6.5.x https://github.com/webiny/website-builder-nextjs.git my-website cd my-website -npm install ``` -### Match Webiny Version +### Install the SDK + +All Webiny integration code comes from a single package, `@webiny/sdk-nextjs`. It covers the Website Builder, the Headless CMS, and the rest of the Webiny SDK. Install the version that matches your Webiny project. You can find your version by running `yarn webiny --version` in your Webiny project directory. -Before installing dependencies, ensure the SDK versions in `package.json` match your Webiny version. You can find your version by running `webiny --version` in your Webiny project directory. +The starter kit uses Yarn, so install the dependencies and the SDK with it: -```json package.json -{ - "dependencies": { - "@webiny/website-builder-nextjs": "~6.2.1", - "@webiny/sdk": "~6.2.1" - } -} +```bash +yarn install +yarn add @webiny/sdk-nextjs@~6.5.0 ``` -The `~` prefix allows safe patch updates. +The `~` prefix allows patch updates. Keep the SDK on the same minor version as your Webiny project. ## Configuration ### Get Credentials -To connect the starter kit to your Webiny project, you'll need an API key, API host URL, and tenant ID. The easiest way to get these is through the **Configure Next.js** shortcut in Webiny Admin — click **Support** in the bottom-left corner and select **Configure Next.js**. - -Webiny Admin sidebar with the Support menu open, showing the Configure Next.js option - -A dialog appears with the three environment variables already filled in and ready to copy: +To connect the starter kit to your Webiny project, you need an API key, the API host URL, and the tenant ID. The **Configure Frontend** dialog in Webiny Admin generates all three. Open it from the **Dev Tools** menu in the sidebar. -Configure Next.js dialog showing NEXT_PUBLIC_WEBSITE_BUILDER_API_KEY, API_HOST, and API_TENANT pre-filled +The dialog has a **Frontend Domain** field and a tab for each supported starter kit. Set **Frontend Domain** to the URL your Next.js app runs on (`http://localhost:3000` during development) and click **Save**. The Website Builder editor and the Headless CMS live preview both load your app from this domain. -Click the copy icon and paste the block directly into your `.env` file in the next step. +Open the **Next.js** tab and copy the environment variables. You'll paste them into your `.env` file in the next step. -If your Admin is running on a non-localhost domain (i.e. a deployed CloudFront URL), the dialog will also include a `NEXT_PUBLIC_WEBSITE_BUILDER_ADMIN_HOST` variable — make sure to copy that too. +If your Admin runs on a non-localhost domain (for example, a deployed CloudFront URL), the dialog also includes a `NEXT_PUBLIC_WEBINY_ADMIN_HOST` variable. Copy that too. The starter kit uses it to allow Admin to embed your app in an iframe. #### API Key Is Auto-Created -Unlike the Headless CMS where you need to manually create an API key, the Website Builder API key is created automatically for the current tenant. You'll find it under **Settings → Access Management → API Keys** as "Website Builder". It's a read-only key, intentionally scoped that way since it's meant to be used in external frontend apps like your Next.js project. +You don't need to create an API key by hand. Webiny creates a read-only key for each tenant, called "Frontend Integration". You'll find it under **Settings → Access Management → API Keys**. It can read Website Builder pages and redirects, Headless CMS content, and languages. It can't write anything, which is why it's safe to use in a frontend app. + + -API Keys page showing the auto-created Website Builder key +Projects that were first deployed before 6.5 have a key called "Website Builder" instead, and the dialog shows the older `NEXT_PUBLIC_WEBSITE_BUILDER_*` variable names. See [Upgrade Next.js Frontend to 6.5.0](/release-notes/6.5.0/upgrade-nextjs-frontend#api-key-and-environment-variables) for how to handle that. + + ### Set Environment Variables Create a `.env` file in your Next.js project root and paste the copied variables: ```dotenv .env -NEXT_PUBLIC_WEBSITE_BUILDER_API_KEY=your_api_key_here -NEXT_PUBLIC_WEBSITE_BUILDER_API_HOST=https://your-cloudfront-url.cloudfront.net -NEXT_PUBLIC_WEBSITE_BUILDER_API_TENANT=root +NEXT_PUBLIC_WEBINY_API_KEY=your_api_key_here +NEXT_PUBLIC_WEBINY_API_HOST=https://your-cloudfront-url.cloudfront.net +NEXT_PUBLIC_WEBINY_API_TENANT=root ``` -All three variables use the `NEXT_PUBLIC_` prefix because they're accessed client-side during live editing. +All variables use the `NEXT_PUBLIC_` prefix because the browser reads them during live editing. ## Start Development Run the dev server: ```bash -npm run dev +yarn dev ``` -Open [http://localhost:3000](http://localhost:3000). You'll see a "Page not found" message—this is expected since no pages exist yet. +Open [http://localhost:3000](http://localhost:3000). You'll see a "Page not found" message. This is expected because no pages exist yet. Create a Page dialog with Title set to Hello World and Path set to / -In the page editor: +In the page editor, find **Hero #1** in the component palette (Custom group) and drag it onto the canvas. -1. Find **Hero #1** in the component palette (Custom group) -2. Drag it onto the canvas +Website Builder editor with the Hero #1 component on the canvas -Website Builder editor with the Hero #1 component on the canvas +Click **Publish**, then refresh [http://localhost:3000](http://localhost:3000). The hero component now renders on your homepage. -3. Click **Publish** +Browser showing the Hero #1 component rendered on the homepage -Refresh [http://localhost:3000](http://localhost:3000). The hero component now renders on your homepage. +## Project Structure -Browser showing the Hero #1 component rendered on the homepage +The starter kit keeps its files at the project root, with no `src/` folder. The `@/*` path alias points to the project root. -## Project Structure +**`app/(site)/[[...slug]]/page.tsx`** -The starter kit includes: +Catch-all route that renders Website Builder pages. -**`src/app/[[...slug]]/page.tsx`** +**`app/(site)/articles/`** -Catch-all route that renders all Website Builder pages. +Example of rendering Headless CMS entries. `[...slug]/page.tsx` renders published entries, and `preview/page.tsx` is the route the Headless CMS live preview pane loads. See [Live Preview](/{version}/headless-cms/live-preview). -**`src/app/api/preview/`** +**`app/api/preview/`** Enables Next.js draft mode for unpublished page previews. -**`src/contentSdk/`** +**`app/api/redirects/`** + +Looks up Website Builder redirects. `middleware.ts` calls it on every request. + +**`sdk/`** -SDK initialization and configuration. +SDK initialization. `initializeSdk.ts` calls `sdk.init()` with your credentials, theme, and component groups. `SdkInitializer.ts` runs the same call on the client. -**`src/editorComponents/`** +**`editorComponents/`** -Component registration—add your custom components here. +Component registration. Add your custom components here. -**`src/theme/`** +**`theme/`** Theme configuration (CSS variables, typography, colors). ## Next Steps -With the starter kit running and your first page rendered, you're ready to customize the theme and create custom components. +With the starter kit running and your first page rendered, you're ready to [customize the theme](/{version}/website-builder/theme) and [create custom components](/{version}/website-builder/custom-component). diff --git a/docs/developer-docs/6.x/website-builder/theme.ai.txt b/docs/developer-docs/6.x/website-builder/theme.ai.txt index f8233a52b..16d34cf39 100644 --- a/docs/developer-docs/6.x/website-builder/theme.ai.txt +++ b/docs/developer-docs/6.x/website-builder/theme.ai.txt @@ -2,7 +2,7 @@ AI Context: Configure Theme (website-builder/theme.mdx) Source of Information: 1. /Users/adrian/dev/lw/content/lessons/website-builder/theming-and-styling.mdx - Learn Webiny lesson -2. @webiny/website-builder-nextjs SDK documentation +2. @webiny/sdk-nextjs SDK (6.5; was @webiny/website-builder-nextjs in 6.4) 3. Website Builder Next.js starter kit theme files Key Documentation Decisions: @@ -23,7 +23,7 @@ Understanding Website Builder Theme System: - Three-file system: theme.css (define), theme.ts (register), tailwind.css (bridge) - Two-step pattern for editor-selectable styles: CSS variable → theme.ts registration - theme.css defines CSS custom properties and typography classes -- theme.ts exports theme object via createTheme() - passed to contentSdk.init() +- theme.ts exports theme object via createTheme() - passed to sdk.init() as wb.theme - tailwind.css bridges WB variables to Tailwind tokens (bg-primary, text-primary) - __THEME_CSS__ global provided by webpack plugin injectThemeCss - Fonts array in theme.ts injects fonts into editor iframe @@ -59,10 +59,10 @@ Related Documents: - website-builder/custom-component.mdx - Creating components Key Code Locations: -- src/theme/theme.css - CSS variables and typography classes -- src/theme/theme.ts - Theme registration -- src/theme/tailwind.css - Tailwind bridge -- src/contentSdk/ - SDK initialization (imports theme) +- theme/theme.css - CSS variables and typography classes +- theme/theme.ts - Theme registration +- theme/tailwind.css - Tailwind bridge +- sdk/ - SDK initialization (receives theme) - next.config.ts - injectThemeCss webpack plugin Tone Guidelines: diff --git a/docs/developer-docs/6.x/website-builder/theme.mdx b/docs/developer-docs/6.x/website-builder/theme.mdx index 14db36525..60fccb866 100644 --- a/docs/developer-docs/6.x/website-builder/theme.mdx +++ b/docs/developer-docs/6.x/website-builder/theme.mdx @@ -41,8 +41,8 @@ A webpack plugin (`injectThemeCss` in `next.config.ts`) reads `theme.css` at bui The `theme.css` file defines all semantic color variables and typography classes: -```css src/theme/theme.css -@import "@webiny/website-builder-nextjs/lexical.css"; +```css theme/theme.css +@import "@webiny/sdk-nextjs/lexical.css"; :root { --wb-theme-color-primary: #4632f5; @@ -77,7 +77,7 @@ CSS variables use semantic names (`--wb-theme-color-primary`, `--wb-theme-color- The `tailwind.css` file maps Website Builder CSS variables to Tailwind's color tokens: -```css src/theme/tailwind.css +```css theme/tailwind.css @import "tailwindcss"; @theme inline { @@ -96,8 +96,8 @@ This bridge enables `bg-primary`, `text-primary`, and other Tailwind utilities i The `theme.ts` file exports a theme object that the SDK uses to populate the editor's color picker and typography toolbar: -```typescript src/theme/theme.ts -import { createTheme } from "@webiny/website-builder-nextjs"; +```typescript theme/theme.ts +import { createTheme } from "@webiny/sdk-nextjs"; declare const __THEME_CSS__: string; @@ -141,7 +141,7 @@ The `colors` array populates the editor's color picker. Each entry has an `id`, alt="The Website Builder editor showing the color picker open with all theme colors available for selection" /> -The exported `theme` and `css` are imported in `initializeContentSdk` and passed to `contentSdk.init()`. +`app/layout.tsx` imports `theme` and `css`. It injects `css` into the page and passes `theme` to `SdkInitializer`, which hands it to `sdk.init()` as `wb.theme`. ## Customizing Colors @@ -149,7 +149,7 @@ For colors that editors can pick in the Admin, follow a two-step pattern: define To change the primary color: -```css src/theme/theme.css +```css theme/theme.css :root { --wb-theme-color-primary: #16a34a; /* changed to green */ --wb-theme-color-secondary: #15803d; @@ -170,7 +170,7 @@ For colors that are not meant to be selectable by editors (border radius, shadow To add or modify a typography style, update both `theme.css` and `theme.ts`: -```css src/theme/theme.css +```css theme/theme.css .wb-heading-display { font-family: var(--wb-theme-font-family); font-weight: 900; @@ -181,7 +181,7 @@ To add or modify a typography style, update both `theme.css` and `theme.ts`: } ``` -```typescript src/theme/theme.ts +```typescript theme/theme.ts headings: [ { id: "heading1", label: "Heading 1", tag: "h1", className: "wb-heading-1" }, { id: "headingDisplay", label: "Display", tag: "h1", className: "wb-heading-display" } @@ -200,9 +200,9 @@ The editor will now show "Display" as an option in the typography toolbar. To switch fonts (e.g., from Inter to Geist), update four files: -**`src/app/layout.tsx`** — swap the font import and config: +**`app/layout.tsx`** — swap the font import and config: -```typescript src/app/layout.tsx +```typescript app/layout.tsx import { Geist } from "next/font/google"; const geist = Geist({ @@ -213,21 +213,21 @@ const geist = Geist({ }); ``` -**`src/theme/tailwind.css`** — update the `--font-sans` token: +**`theme/tailwind.css`** — update the `--font-sans` token: -```css src/theme/tailwind.css +```css theme/tailwind.css --font-sans: Geist, sans-serif; ``` -**`src/theme/theme.css`** — update the CSS variable: +**`theme/theme.css`** — update the CSS variable: -```css src/theme/theme.css +```css theme/theme.css --wb-theme-font-family: "Geist", sans-serif; ``` -**`src/theme/theme.ts`** — update the `fonts` array so the editor iframe loads the font: +**`theme/theme.ts`** — update the `fonts` array so the editor iframe loads the font: -```typescript src/theme/theme.ts +```typescript theme/theme.ts fonts: ["https://fonts.googleapis.com/css2?family=Geist&display=swap"], ``` @@ -237,7 +237,7 @@ The `fonts` array injects the font into the editor iframe, ensuring the Admin pr If you load multiple font weights in `layout.tsx`, include the full weight range in the `fonts` URL. For example: -```typescript src/app/layout.tsx +```typescript app/layout.tsx const inter = Inter({ weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"] // ... @@ -246,7 +246,7 @@ const inter = Inter({ The `fonts` array must include the same range: -```typescript src/theme/theme.ts +```typescript theme/theme.ts fonts: ["https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap"], ``` diff --git a/docs/release-notes/6.5.0/changelog.mdx b/docs/release-notes/6.5.0/changelog.mdx index ff0dee285..d112be0c0 100644 --- a/docs/release-notes/6.5.0/changelog.mdx +++ b/docs/release-notes/6.5.0/changelog.mdx @@ -132,6 +132,327 @@ Existing content models using the `file` field will continue to work. You can mi The CMS entry revision comparison feature has been relocated from the `/extensions` folder into the `@webiny/ai-powerups` package. This change consolidates AI-powered features in a single package and adds proper architectural layering with UseCase, Repository, and Gateway patterns. The `compareEntryRevisions` GraphQL query is now nested under `CmsQuery`, accessed via `cms { compareEntryRevisions(...) }`. +### New Asset Field with Non-Destructive Crop and Focal Point ([#5452](https://github.com/webiny/webiny-js/pull/5452)) +{/* REVIEW-PENDING @SvenAlHamad — confirm this entry, then delete this line */} + +A new first-class **Asset** field replaces the bare-URL `file` field for images. It stores crop coordinates, focal point, alt text, and caption — all non-destructively. The original file is never modified; edits are applied server-side at delivery time, producing a URL that works with any frontend framework. + +```typescript +import { fields } from "webiny/api-headless-cms"; + +const model = { + fields: [ + fields.asset("heroImage").imagesOnly(), + fields.asset("document").accept(["application/pdf"]) + ] +}; +``` + +The Asset field exposes a resolved `url` in the read API with crop and focal point baked in, so frontends get a turnkey URL without knowing the delivery-param contract. The underlying `src` remains the pristine original. + +**Image editor** — File Manager, CMS file fields, and the new Asset input all share the same crop/focal-point editor. Choose from aspect-ratio presets or enter a custom ratio; a live preview shows the result. + +**Delivery URL parameters** — `?crop=t,l,b,r`, `?aspectRatio=16:9`, and `?focal=x,y` join the existing `?width`, `?format`, and `?quality` params. The server computes the largest rectangle of the requested aspect ratio inside the crop, centered on the focal point. + +Existing `file` fields are unaffected. The legacy flat value is upgraded at render time with no migration needed. + +### Injectable String Formatting on the API ([#5526](https://github.com/webiny/webiny-js/pull/5526)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +`StringFormatter`, `Slugify`, and `DateFormatter` are now available in `@webiny/api-core`, mirroring the admin-side features. Server code can inject them for consistent, decoratable formatting. + +```typescript +class MyRepository { + constructor(private stringFormatter: StringFormatter.Interface) {} + + private makeSlug(name: string) { + return this.stringFormatter.slugify(name); + } +} +``` + +To override slug logic project-wide, decorate the fine-grained `Slugify` feature: + +```typescript +const MySlugify = Slugify.createDecorator(() => ({ + execute: value => value.trim().toLowerCase().replace(/\s+/g, "_") +})); + +container.registerDecorator(MySlugify); +``` + +### Scheduled Publish/Unpublish State Now Visible Across the CMS UI ([#5441](https://github.com/webiny/webiny-js/pull/5441)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Previously, scheduled publish/unpublish actions were only visible inside the schedule dialog. Now the scheduled state appears everywhere users expect to see publish status: + +- **Entries list "Live" column**: Shows a `Scheduled` pill with the go-live time when an entry has a pending action. Entries can display both "Live" and "Scheduled" simultaneously when an older revision is live and a newer one is queued. +- **Entry form banner**: An amber info banner appears above the form when the open entry has a scheduled action. +- **Publish/Unpublish confirm dialogs**: A warning appears when the entry has a scheduled action, explaining that publishing now will cancel the scheduled action. + +The UI refreshes automatically when you schedule, cancel, publish, or unpublish — no page reload required. + +### Fixed Image Crop Not Being Applied During Asset Delivery ([#5595](https://github.com/webiny/webiny-js/pull/5595)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +When you cropped an image using the File Manager's ImageEditor, the crop was saved correctly but never actually applied when the image was delivered. The image transformation pipeline was reading crop data from the wrong metadata path. This has been fixed — crops set via the ImageEditor UI are now properly applied during asset delivery. + +### Stable `_id` Property for Object and Dynamic Zone Field Values ([#5606](https://github.com/webiny/webiny-js/pull/5606)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Object fields and dynamic zone fields now include a server-generated `_id` property on each value, giving external systems (comments, annotations, collaboration tools) a stable reference identifier for individual items within nested structures. + +```typescript +// Example GraphQL response with the new _id property +{ + "data": { + "getArticle": { + "authors": [ + { "_id": "abc123xyz456", "name": "John Doe" }, + { "_id": "def789uvw012", "name": "Jane Smith" } + ], + "content": [ + { "_id": "ghi345rst678", "_templateId": "heroBlock", "title": "Welcome" } + ] + } + } +} +``` + +Key behaviors: +- IDs are auto-generated on create/update using a 12-character alphanumeric identifier +- Existing IDs are preserved when updating entries — only new items receive new IDs +- Client-provided IDs are accepted; duplicates within the same array are silently regenerated +- Existing entries return `_id: null` until re-saved (no data migration required) + +### Revision List Now Refreshes Automatically After Changes ([#5614](https://github.com/webiny/webiny-js/pull/5614)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +After creating or deleting a revision, the revisions drawer now re-fetches the list from the API. Previously, the list stayed stale until the drawer was closed and reopened. + +### Improved Revision List Actions and Visibility ([#5616](https://github.com/webiny/webiny-js/pull/5616), [#5617](https://github.com/webiny/webiny-js/pull/5617)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +The revision number in the revisions list is now more visible, and the revision list actions have been reorganised for better usability. + +### Revision Management Improvements ([#5618](https://github.com/webiny/webiny-js/pull/5618)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +The content entry revision system received significant usability improvements across the revision drawer, entry list, and form interfaces. + +**Revision drawer & list:** +- The revision list now refreshes automatically after creating, deleting, unpublishing, or editing a note +- Version numbers are more prominent — rows display `#N · Title` format +- Each revision shows a visible status tag (Draft, Published, or Previously published) instead of tooltip-only status +- Created-by and published-by information now appears on each revision +- You can add or edit notes on any revision, not just at publish time +- An alert banner warns when viewing an older revision with a link to the latest +- Unpublish action is available in the 3-dots menu for published revisions + +**Content entries table:** +- The Live column now shows a relative timestamp indicating when content was last published +- Clicking the Live column header sorts entries by `lastPublishedOn` + +**Entry form:** +- Clicking "+ New Revision" on a locked entry now navigates directly to the newly created revision URL + +### Timezone-Aware Scheduling ([#5618](https://github.com/webiny/webiny-js/pull/5618)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +The scheduler dialog now includes a timezone dropdown, allowing you to pick a scheduled time in any timezone. The picker converts your selection to UTC before sending to the backend. All scheduler date displays (alert bar, table tooltip, rescheduling dialog) now show consistent `UTC+02:00` format instead of the browser's inconsistent `GMT+2` output. + +### Fixed Revision Note Updates Overwriting Latest Revision ([#5618](https://github.com/webiny/webiny-js/pull/5618)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +Editing a note on an older revision would inadvertently overwrite the latest revision's content. A new `updateRevision` storage operation now writes only to the specific revision record without syncing to latest. This fix is implemented across DDB, DDB-ES, and SQL backends. + +### Fixed Stale Live Pointer After Deleting Published Revision ([#5618](https://github.com/webiny/webiny-js/pull/5618)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +Deleting the currently published revision left a stale `live` pointer on the entry. The system now correctly clears the `live` field when you delete a published revision. + +### Fixed Pattern Validator Crash with Null Flags ([#5618](https://github.com/webiny/webiny-js/pull/5618)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +Custom pattern validation fields with null or undefined flags caused `new RegExp(regex, null)` to crash. Falsy flags are now coerced to `undefined`, and validation errors now properly include field details in the GraphQL response. + +### Google AI Provider Support and Updated Model Lists ([#5670](https://github.com/webiny/webiny-js/pull/5670)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +Google is now available as an AI provider for AI Powerups, joining OpenAI and Anthropic. The integration uses Google's Gemini models including `gemini-3.8-flash`, `gemini-3.5-pro`, and `gemini-2.5-flash-lite` among others. + +Model lists for all providers have been updated to reflect current offerings: + +- **OpenAI**: Added `gpt-6-astra`, `gpt-5.5-pro`, `gpt-5.2`, `gpt-5.2-pro`, `gpt-5`, `gpt-5-pro`, `gpt-5-mini`, and `gpt-5-nano` +- **Anthropic**: Added `claude-fable-5-1` and `claude-opus-4-8`, with flagship models now listed first +- **Google**: 9 Gemini models available + +AI models now expose `deprecated` and `endOfLife` dates when known, allowing you to see which models are approaching end-of-life. Deprecated models (`o3-pro`, `o3`, `gpt-4.1-nano`, `o4-mini`) remain available for existing presets but will be removed after their shutdown dates. + + + +To use Google AI, set the `WEBINY_API_GOOGLE_API_KEY` environment variable or configure the API key through your provider preset. + + + +### Unified Preview Domain Configuration for CMS Live Preview ([#5689](https://github.com/webiny/webiny-js/pull/5689)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +CMS live preview now reads the preview domain from Frontend Settings instead of requiring per-model configuration. The `previewPrefix` and `previewSlug` model settings have been replaced with a single `previewPath` setting that defines only the URL path pattern. The preview domain is managed centrally in Frontend Settings. + +```typescript +// Model configuration now uses previewPath only +{ + previewPath: "/articles/{values.slug}" +} +``` + +When a path token cannot be resolved (e.g., `{values.slug}` on a new entry), the entire path falls back to `/new` rather than displaying an incomplete URL. + +The Frontend Settings permission has been moved to the Dev Tools permission group, and the "Configure Frontend" menu item is now gated by this permission. + +### Fixed Field Filtering and Sorting for Field IDs Containing Underscores ([#5701](https://github.com/webiny/webiny-js/pull/5701)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Fields with underscores in their IDs (e.g., `on_sale`) were incorrectly parsed by the CMS query mappers. Filtering on `on_sale_contains` would fail with an error because the system only recognized the part before the first underscore as the field name. Sorting was even more problematic — sort directives like `on_sale_DESC` were silently dropped, causing queries to return unsorted results with no error. + +Both mappers now correctly handle underscored field IDs: + +- **Filtering** matches the longest possible field ID first, so `price_range_gte` correctly routes to `price_range` even when a `price` field also exists. +- **Sorting** allows underscores within field names and validates against the full field ID. + +Additionally, when combining an explicit `values` object with flat field keys in a filter, the two are now merged instead of one overwriting the other. + +### Edit Referenced Entries Inline via Drawer ([#5440](https://github.com/webiny/webiny-js/pull/5440)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Reference field cards now include a pencil icon that opens the referenced entry in a drawer for inline editing. You can edit and save without leaving your current entry. The drawer stays open after save so you can continue editing, and the card updates immediately without a loading flash. + +### CMS Preview URL Now Respects Model's `previewPath` Setting ([#5705](https://github.com/webiny/webiny-js/pull/5705)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +The CMS preview iframe was always loading at `/preview` regardless of the model's `previewPath` configuration. A model with `previewPath = "/articles/{values.slug}"` will now correctly load its preview iframe at `/articles/preview`. + +### Structured Values from AI Content Generation Use Case ([#5477](https://github.com/webiny/webiny-js/pull/5477)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +`CmsGenerateEntryContentUseCase` previously returned its result pre-serialized as a JSON string, requiring callers to parse it. It now returns a structured `values` object directly. Transport serialization (for websocket streaming) has been moved to the transport edge. + +```typescript +// Before: result.value.output was a JSON string +// After: result.value.values is a typed object +const result = await generateContentUseCase.execute(params); +const summary = result.value.values.aiSummary; +``` + +### Bulk Action API Exposed via webiny Package ([#5411](https://github.com/webiny/webiny-js/pull/5411)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Custom CMS bulk actions can now be authored from your project using the public `webiny` package. Bulk actions are automatically run as background tasks and can include AI-powered content generation with real-time websocket progress. + +```typescript +import { EntriesBulkAction } from "webiny/api/cms/entry"; +import { BulkActionFeature, BulkActionButton } from "webiny/admin/cms/entry/list"; +import { CmsGenerateEntryContentUseCase } from "webiny/api/ai-powerups"; +import { WebsocketEventHandler } from "webiny/admin/websockets"; +``` + +### AI Model Configuration Now Uses Capabilities and Roles ([#5693](https://github.com/webiny/webiny-js/pull/5693)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +AI features previously hardcoded the first configured model, requiring duplicate API key entries for multiple models from the same vendor. The configuration system now uses three distinct concepts: + +- **Connections** store a vendor and credential pair, so one API key covers all models from that vendor. +- **Model roles** (`fast`, `standard`, `vision`) let you assign models by purpose rather than per-feature. +- **Capabilities** allow per-feature overrides when needed, with sensible defaults otherwise. + +Features declare which role they need, and the resolver picks the appropriate model. Projects upgrading from earlier versions have their existing configuration migrated automatically — the first preset becomes the `standard` role, preserving current behaviour. + +AI features also now register their own capabilities alongside their code, so the settings screen stays in sync without manual updates. Extensions adding AI features get a settings row automatically. + + + +The legacy `providers` configuration continues to work during the upgrade period but will be removed in the next major release. + + + +### Entry Collaboration with Comments and Notifications Inbox ([#5619](https://github.com/webiny/webiny-js/pull/5619)) +{/* REVIEW-PENDING @SvenAlHamad — confirm this entry, then delete this line */} + +You can now leave comments directly on CMS entry fields, enabling real-time collaboration between content editors. Comments are anchored to specific fields — including individual items within repeatable fields — so discussions stay contextually relevant even as content changes. + +A new notifications inbox surfaces comment activity, mentions, and other collaboration events in the Admin interface. The inbox integrates with the CMS comment system out of the box, and provides a bridge for custom notifications if needed. + +Key capabilities: + +- **Field-level comments** — attach comments to any entry field; nested and repeatable fields are supported via stable internal IDs. +- **Notifications inbox** — view and manage collaboration notifications from a central location in the Admin app. +- **Automatic scroll and highlight** — clicking a notification or comment reference scrolls to and highlights the relevant field in the entry form. + + + +After upgrading, you will need to redeploy your API to enable the comments and notifications backend. + + + +### Fixed CMS Field Visibility and Access Control Rules Not Working in Content Entry Forms ([#5766](https://github.com/webiny/webiny-js/pull/5766)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Conditional visibility and access control rules configured in the CMS model editor were not being applied when editing content entries. Several issues have been fixed: + +- **Comparison operators:** The form only recognized basic operators (`eq`, `neq`, etc.), while the CMS stores operators like `==`, `!=`, `>`, `<`, `contains`, `startsWith`, and their negated variants. All CMS operators are now supported. +- **Boolean comparisons:** Rule values were being converted to strings, so conditions like `advanced == false` never matched. Boolean values are now compared correctly. +- **Access control evaluation:** The access control evaluator was incomplete and never matched any rules. A proper evaluator now checks `admin:` and `team:` patterns against the current user, correctly hiding or disabling fields based on their access level. + +### Fixed List Order When Loading More Items ([#5783](https://github.com/webiny/webiny-js/pull/5783)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Items loaded via "load more" in the CMS entries list appeared in the wrong position — often at the top instead of appended to the existing results. The internal cache was shared across views with different sort orders, causing the ordering to leak between views. List views now apply their own sorting after retrieving items from the cache. + +### Publishing No Longer Changes Saved/Modified Timestamps ([#5783](https://github.com/webiny/webiny-js/pull/5783)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Publishing, republishing, or unpublishing an entry previously updated the `savedOn`, `modifiedOn`, `savedBy`, and `modifiedBy` fields. This caused entries to jump in lists sorted by these fields — sometimes disappearing past the loaded page. These meta fields are now preserved during publish operations and only updated when actual content changes. + +Additionally, saving an entry without making changes no longer bumps timestamps. The system now compares normalized values and only updates meta fields when content differs. + +### Fixed Update Operations Erasing Delete/Restore History ([#5783](https://github.com/webiny/webiny-js/pull/5783)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +When updating an entry that had been deleted and restored from trash, the `deletedOn`, `deletedBy`, `restoredOn`, and `restoredBy` fields were incorrectly reset to `null`. The entry no longer recorded its trash history after a simple save. These fields are now preserved across updates unless explicitly overridden. + +### Fixed Republishing Older Revisions on DynamoDB ([#5783](https://github.com/webiny/webiny-js/pull/5783)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Republishing an older revision (one that was already published but not the latest) failed with "Provided list of item keys contains duplicates". The operation attempted to write the same record twice. This has been fixed. + +### Fixed Datetime Field Subtypes Preserving List Renderer ([#5793](https://github.com/webiny/webiny-js/pull/5793)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Datetime subtype methods (`.date()`, `.time()`, `.dateTimeWithTimezone()`, `.dateTimeWithoutTimezone()`) now preserve the list renderer when applied to list fields. Additionally, `.datetime()` now defaults to `.withTimezone()`, and the `DateTimeZ` scalar is registered in the main GraphQL schema. + +### Lifecycle Events Disabled for Internal Private Models ([#5792](https://github.com/webiny/webiny-js/pull/5792)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Private CMS models used internally by Webiny (files, folders, pages, workflows, etc.) now have entry lifecycle events disabled by default. These models already publish their own domain-specific events (`File*`, `Folder*`, `Page*`, etc.), so the generic `Cms/Entry/*` events were redundant noise. + +If you have custom `Cms/Entry/*` event handlers targeting one of these internal private models, they will no longer fire. Use the model's own domain events instead. + +For custom private models, you can control this behaviour via the `lifecycleEvents` option: + +```typescript +builder.private({ + modelId: "myPrivateModel", + name: "My Private Model", + lifecycleEvents: false // disables Cms/Entry/* events +}); +``` + +### Fixed "Create From Revision" Failing for Entries with Dynamic Zone Fields ([#5837](https://github.com/webiny/webiny-js/pull/5837)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +Creating a new revision from a published entry that contained a dynamic zone field would fail with the error `Field "_templateId" is not defined by type "Page_BlocksInput"`. The issue was that the "create from revision" operation wasn't properly transforming dynamic zone data before sending it to the API. This has been fixed — dynamic zone fields are now correctly prepared across all entry operations. + ## Website Builder ### Nuxt Starter Kit Configuration ([#5265](https://github.com/webiny/webiny-js/pull/5265)) @@ -198,6 +519,74 @@ Live preview in the CMS entry editor now correctly updates when you change asset - Asset URLs recompute immediately when crop settings change - The editing SDK only activates when the preview iframe is showing a CMS entry +### Fixed Redirect Type Changes Not Invalidating the CDN Cache ([#5505](https://github.com/webiny/webiny-js/pull/5505)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Changing a redirect from Permanent to Temporary (or vice versa) wrote to the database but never flushed CloudFront. The response is cached for up to a year, so the old redirect type kept being served. The type field is now included in the cache-invalidation check. + +The Admin dialog also now explains the difference between the two options: + +- **Temporary** — browsers check with the server on each visit, so you can change or remove this redirect later. +- **Permanent** — browsers remember it indefinitely and may keep redirecting even after you change or delete it. + +New redirects now default to Temporary. The option you get by not choosing shouldn't be the one you can't undo. + +### Fixed Frontend API Key Missing Read Permission for Redirects ([#5531](https://github.com/webiny/webiny-js/pull/5531)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +The installer's auto-generated API key for the frontend was missing `wb.*` read permission. This caused `GET /wb/redirects` to return a 403, breaking redirect resolution on the frontend. The permission has been restored. + + + +Existing environments are unaffected by new installs — if your redirects stopped working after a recent deploy, manually edit the Website Builder API key in Admin and grant it Read access. + + + +### New `contentEntry` Input Type for CMS-Connected Components ([#5594](https://github.com/webiny/webiny-js/pull/5594)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Website Builder components can now consume CMS entries directly via a new `contentEntry` input type. This enables three modes of content binding: + +- **Manual single**: Select a specific entry by ID +- **Manual list**: Select multiple specific entries +- **Query**: Define a dynamic query with model, sort, limit, and search parameters + +```typescript +import { createContentEntryInput } from "webiny/website-builder-sdk"; + +const input = createContentEntryInput({ + mode: "query", + models: ["article"], + query: { + sort: "createdOn_DESC", + limit: 10 + } +}); +``` + +Content entries are resolved server-side during SSR, so pages with CMS-connected components render correctly on first load. A `useContentEntryList` React hook is available for client-side pagination in query mode. + +### Redirects Now Work with Website Builder API Key ([#5502](https://github.com/webiny/webiny-js/pull/5502)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Redirects were not resolving on websites because the `Website Builder` API key was missing the `wb.redirect` permission. The installer now grants `wb.*` read access, matching what the Admin UI writes when you select "Read-only" access level. + + + +For existing environments, manually update the `Website Builder` API key in Admin: open it and set Website Builder → Read-only. New deployments will work automatically. + + + +### CloudFront Cache Invalidation Task Moved to Website Builder Package ([#5524](https://github.com/webiny/webiny-js/pull/5524)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Website Builder's redirect cache invalidation was using a CloudFront invalidation task defined in `@webiny/api-file-manager-s3`, creating a hidden runtime dependency. Website Builder now has its own CloudFront invalidation task, removing the cross-package coupling. + +### Fixed Page List Order When Loading More Items ([#5783](https://github.com/webiny/webiny-js/pull/5783)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Pages loaded via "load more" appeared at the top of the page list instead of in the correct sorted position. This affected both the page list and the redirects list. The ordering is now handled correctly by the view layer. + ## Admin ### Workflow State Folder Permissions ([#5331](https://github.com/webiny/webiny-js/pull/5331)) @@ -248,6 +637,130 @@ The API key slug for frontend integration has been renamed from `website-builder A new `TenantThemeExtension` has been added to the Next.js starter kit, enabling per-tenant theme customization including a font selector. Themes are merged at runtime based on the current tenant. +### Dark Theme Support ([#5316](https://github.com/webiny/webiny-js/pull/5316)) +{/* REVIEW-PENDING @SvenAlHamad — confirm this entry, then delete this line */} + +The Admin UI now supports multiple themes, including dark mode. You can register custom themes through an extension, and several dark themes are included as sample implementations. + +### Injectable String Formatting ([#5517](https://github.com/webiny/webiny-js/pull/5517)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +`StringFormatter` and `Slugify` are now injectable features in `@webiny/app-admin`. They replace direct imports of the `slugify` npm package scattered across presenters, giving you a single, decoratable seam for slug generation. + +```typescript +const MySlugify = Slugify.createDecorator(() => ({ + execute: value => value.trim().toLowerCase().replace(/\s+/g, "_") +})); + +container.registerDecorator(MySlugify); +``` + +Consumers call `stringFormatter.slugify()`; projects that need custom logic decorate `Slugify` alone rather than the whole formatter. + +### Fixed Redirect After Deleting the Current Folder ([#5626](https://github.com/webiny/webiny-js/pull/5626)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +When you deleted a folder while viewing it, you could end up stranded with no visible content. Now the UI automatically navigates to the nearest surviving ancestor folder (parent → grandparent → … → root) without requiring a manual navigation or page refresh. This also works when a folder is removed from the cache externally — for example, by another component calling `useDeleteFolder`. The fix uses optimistic cache updates instead of refetching from the API, avoiding issues with eventually-consistent storage. + +### Fixed Schema Validation for Empty Non-Required Fields ([#5642](https://github.com/webiny/webiny-js/pull/5642)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Non-required fields with empty values (`null` or `undefined`) were incorrectly failing Zod schema validation, even though they should be allowed to remain empty. This affected scalar fields, object fields with all-empty children, and list fields with empty arrays. The validation logic now skips schema checks for non-required fields when their values are empty, while still validating populated fields and enforcing required field constraints as expected. + +### Unified Frontend Settings Configuration ([#5667](https://github.com/webiny/webiny-js/pull/5667)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Frontend settings — including the preview domain and starter kit configuration — were previously tied to the Website Builder package. This made it awkward to configure frontend settings in projects not using Website Builder, or to share settings across multiple frontend frameworks. + +A new `@webiny/frontend-settings` package now manages these settings centrally: + +- **New GraphQL API:** `frontend { getSettings }` and `frontend { updateSettings }` provide a dedicated endpoint for frontend configuration, separate from Website Builder. +- **Unified "Configure Frontend" dialog:** The Dev Tools dialog now includes a domain input field with save functionality. Starter kit configuration (Next.js, Nuxt) has been relocated here from Website Builder. +- **New permission:** A `frontend.settings` permission controls access to the frontend settings API. Website Builder's `wb.settings` permission has been removed. + +If you were using the Website Builder "Settings" menu item to configure the preview domain, use the "Configure Frontend" dialog in Dev Tools instead. The `websiteBuilder { getSettings }` query still works but now delegates to the new frontend settings repository internally. + +### Removed Unused `useDataList` and `useAutocomplete` Hooks ([#5346](https://github.com/webiny/webiny-js/pull/5346)) +{/* REVIEW-PENDING @brunozoric — confirm this entry, then delete this line */} + +The `useDataList` and `useAutocomplete` hooks in the `@webiny/app` package had no usage across the codebase and have been removed. If you were importing these hooks directly, you'll need to implement equivalent functionality in your own code. + +### Command Palette with Global Search and Keyboard Shortcuts ([#5432](https://github.com/webiny/webiny-js/pull/5432)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +A new command palette is now available via ⌘K / Ctrl+K in the admin app. It provides fuzzy search over navigation destinations and registered actions, keyboard-driven navigation, and is styled according to the design system. The palette auto-derives navigation commands from your sidebar menus, supports detail views for commands that need input, and allows commands to register global keyboard shortcuts. + +To register a custom command: + +```typescript +import { Command } from "webiny/admin"; + +class DeployCommand implements Command.Interface { + name = "myapp.deploy"; + label = "Deploy"; + category = "Actions"; + shortcut = "cmd+shift+d"; // optional global hotkey + execute() { + // your action logic + } +} + +export const DeployCommandImpl = Command.createImplementation({ + implementation: DeployCommand, + dependencies: [] +}); +``` + +### Command Palette No Longer Blocks Backspace in Text Fields ([#5480](https://github.com/webiny/webiny-js/pull/5480)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Pressing Backspace in a description field, textarea, or rich-text editor was not deleting characters. The command palette's "back out" shortcut handler was registered globally and was intercepting Backspace even when the palette was closed. This has been fixed — the handler now only activates when the palette is open and properly exempts all editable elements. + +### Injectable Date Formatter for Consistent Date Display ([#5512](https://github.com/webiny/webiny-js/pull/5512)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Date formatting across the admin was inconsistent. A new injectable `DateFormatter` feature in `@webiny/app-admin` provides a single, decoratable formatter that any admin module can use. The default format is `YYYY-MM-DD HH:mm` in 24-hour format using the browser locale. + +To override the format project-wide: + +```typescript +import { DateFormatter } from "webiny/admin"; + +const MyDateFormat = DateFormatter.createDecorator(() => ({ + format: (date) => { + // your custom format logic + } +})); + +container.registerDecorator(MyDateFormat); +``` + +### Empty State Design System Component ([#5442](https://github.com/webiny/webiny-js/pull/5442)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +A reusable `EmptyState` component has been added to `@webiny/admin-ui`. It supports multiple illustration types (content, table, listing, layout, upload, select), optional title/description/actions, and three sizes (sm/md/lg). Widgets now use this consistent empty state component. + +### Admin UI Improvements for AI Models, Filters, and Task Details ([#5498](https://github.com/webiny/webiny-js/pull/5498)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Several admin UI improvements: + +- AI Power-Ups Provider Model dropdown is now searchable and includes the latest OpenAI and Anthropic models +- Background Tasks and Webhooks filter drawers now match the Audit Logs pattern with an icon button in the header +- Task detail drawer labels and values are no longer visually stuck together +- Reference field inline edit now shows a proper tooltip on hover +- DataTable columns menu stays open when toggling multiple column checkboxes + +### Fixed Field Visibility Rules Inside Lists and Dynamic Zones ([#5793](https://github.com/webiny/webiny-js/pull/5793)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +Field visibility rules using the `$.field` syntax inside object lists and dynamic zones were not resolving correctly. Rules with `==` operators never matched, while `!=` rules always matched regardless of the actual field values. List item children now receive an index-aware scope path, ensuring rules evaluate correctly for each item independently. + +### Fixed List Renderer Crash on Single-Value Fields ([#5793](https://github.com/webiny/webiny-js/pull/5793)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +The CMS form would crash with `t.map is not a function` when a list renderer was mistakenly applied to a single-value field (for example, `dateTimeInputs` without `.list()`). The form now falls back to the single-value renderer instead of crashing. + ## Development ### Custom GraphQL Playground Built from Scratch ([#5357](https://github.com/webiny/webiny-js/pull/5357)) @@ -295,6 +808,65 @@ Webiny now uses TypeScript 7.0.2. Most dependencies have also been updated to th The `@svgr/webpack` package has been removed from Webiny's dependencies. If your project relied on this package for SVG handling, you may need to add it to your own project dependencies. +### Lexical Editor Aligned with Design System ([#5509](https://github.com/webiny/webiny-js/pull/5509)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +The Lexical rich-text editor has been restyled to match the Webiny design system: + +- Toolbar uses design tokens with rounded controls and proper hover/active states +- Icons migrated to `@webiny/icons` Material Symbols +- Dropdown menus styled to match admin-ui patterns with check indicators for selected items +- Color picker uses square swatches with proper theme color borders and a no-color swatch option +- Link editor popover uses admin-ui form components and no longer opens at (0,0) when pressed with no selection +- Placeholder text shows for any empty block and matches the current block's typography + +### MCP Skills for Bulk Actions, AI Power Ups, and Websocket Notifications ([#5434](https://github.com/webiny/webiny-js/pull/5434)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Three new MCP authoring skills help developers build extensions using 6.5.0 APIs: + +- `api/cms-bulk-actions` — custom bulk actions with background tasks and Admin UI wiring +- `api/ai-powerups-content` — generating CMS content via `CmsGenerateEntryContentUseCase` +- `api/websocket-notifications` — real-time API to Admin notifications + +### Fixed tsconfig Generation Preserving Manual Excludes ([#5455](https://github.com/webiny/webiny-js/pull/5455)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +The tsconfig generation script was overwriting any manually added `exclude` entries. This caused build failures in packages like `create-webiny-project` that need to exclude vendored files. Manual excludes are now preserved across regeneration. + +### GraphQL Resolver Dependencies Now Honor `multiple: true` ([#5698](https://github.com/webiny/webiny-js/pull/5698)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +When using `addResolver` with a `[abstraction, { multiple: true }]` dependency tuple, the resolver was receiving a single object instead of an array. This has been fixed — multiple dependencies are now correctly resolved as arrays. + +```typescript +builder.addResolver({ + path: "Query.things", + dependencies: [[Thing, { multiple: true }]], + resolver: (things: Thing.Interface[]) => () => things.map(/* ... */) +}); +``` + +### A/B Testing and AI Powerups License Flags ([#5499](https://github.com/webiny/webiny-js/pull/5499)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +New WCP license feature flags are now consumed throughout the stack: + +- `abTesting` — A/B testing capability +- `aiPowerups.options.websiteBuilder.pageTranslation` — WB page translation +- `aiPowerups.options.cms.entryGeneration` — CMS AI entry generation +- `aiPowerups.options.cms.entryComparison` — CMS AI entry comparison +- `aiPowerups.options.cms.entryTranslation` — CMS AI entry translation + +Features are gated at resolver/render time and missing flags are treated as disabled for backward compatibility. + +### Fixed Peer Dependency Ranges Not Preserved When Publishing Packages ([#5776](https://github.com/webiny/webiny-js/pull/5776)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +The published Next.js packages (`@webiny/sdk-nextjs`, `@webiny/cms-nextjs`, `@webiny/website-builder-nextjs`) were declaring exact peer dependency versions instead of the intended ranges. This caused npm installations to fail with `ERESOLVE unable to resolve dependency tree` errors when your project used different (but compatible) versions of Next.js or React. + +For example, installing `@webiny/sdk-nextjs` into a Next.js 15.5 / React 19 app would fail because the package incorrectly required exactly `next: 16.2.11` and `react: 18.3.1`. The Next.js packages now correctly declare `next: >=15.0.0 <16.0.0` and `react: >=19.0.0 <20.0.0` as peer dependencies. + ## AI ### AI Prompt File Attachments and Audit Logging ([#5384](https://github.com/webiny/webiny-js/pull/5384)) @@ -382,3 +954,44 @@ Adding a new language in the Admin and then opening the Create Page dialog would You can now translate pages using AI directly from the Page Builder. When you have an AI provider configured in AI Power-Ups settings, the translate page dialog will use AI to translate the page title, snippet, and all text content within the page. For Latin-script languages (e.g., Croatian, Spanish), the page path slug is also translated automatically. Non-Latin languages (e.g., Russian, Japanese) keep the original slug to avoid URL encoding issues. The translation works as a decorator on the built-in `TranslatePageUseCase`, so if no AI provider is configured, the page is still cloned as before — AI translation is a graceful enhancement rather than a requirement. + +## Infrastructure + +### EventBridge Schedule Names Now Valid for CMS Actions ([#5445](https://github.com/webiny/webiny-js/pull/5445)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Scheduling CMS entry publish/unpublish was failing because the EventBridge Scheduler name contained invalid characters (slashes from the CMS namespace) and exceeded the 64-character limit. The schedule ID is now used directly as the EventBridge name, which is always valid. + +## Bug Fixes + +### Scheduled Actions Reload When Scheduler Overlay Closes ([#5506](https://github.com/webiny/webiny-js/pull/5506)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Cancelling a scheduled publish from the Scheduler overlay was leaving a stale "Scheduled" tag in the entries list. Scheduled actions are now reloaded when the overlay closes. + +### Test Expectations Updated for Generated Object IDs ([#5686](https://github.com/webiny/webiny-js/pull/5686)) +{/* REVIEW-PENDING @adrians5j — confirm this entry, then delete this line */} + +Several test suites were failing after stable `_id` generation was added to object and dynamic-zone values. Test expectations have been updated to account for the new `_id` field on stored objects. + +## Breaking Changes + +### Entry Data Factories Now Return `Result` Instead of Throwing ([#5783](https://github.com/webiny/webiny-js/pull/5783)) +{/* REVIEW-PENDING @Pavel910 — confirm this entry, then delete this line */} + +The five entry data factories exported from `webiny/api/cms/entry` now return a `Result` type instead of throwing exceptions. If you're calling these factories directly in custom code, you'll need to update your error handling: + +```typescript +import { createEntryDataFactory } from "webiny/api/cms/entry"; +import { isFail } from "webiny/api"; + +const result = await factory.create(/* ... */); + +if (isFail(result)) { + // Handle the error + console.error(result.error); + return; +} + +const { entry } = result.value; +``` diff --git a/docs/release-notes/6.5.0/upgrade-guide.mdx b/docs/release-notes/6.5.0/upgrade-guide.mdx index 833f6da08..97b684da9 100644 --- a/docs/release-notes/6.5.0/upgrade-guide.mdx +++ b/docs/release-notes/6.5.0/upgrade-guide.mdx @@ -1,5 +1,5 @@ --- -id: 7nffwnm5 +id: 3cwfqjzl title: Upgrade from 6.4.x to 6.5.0 description: Learn how to upgrade Webiny from 6.4.x to 6.5.0. --- @@ -58,4 +58,22 @@ Proceed by redeploying your Webiny project: yarn webiny deploy --env {environment} ``` +{/* custom-steps:start - kept when the guide is regenerated */} + +### 3. Upgrade Your Next.js Frontend Packages + +In your Next.js project, bump `@webiny/website-builder-nextjs` and `@webiny/sdk` to 6.5.0, so they match your Webiny version. + +No code changes are needed. Your components keep working, including ones that read `mimeType`, `width`, or `height` from a file input. Crops, focal points, and alt text set in the 6.5 editor show up once your components read file inputs through `normalizeToAsset()`. See [File Inputs](./upgrade-nextjs-frontend#file-inputs). + +Optionally, move to the new `@webiny/sdk-nextjs` package, which adds Headless CMS content, live preview, and content entry inputs to your frontend. [Upgrade Next.js Frontend to 6.5.0](./upgrade-nextjs-frontend) has an AI prompt for it, plus the same changes as manual steps. + + + +Your project keeps its existing "Website Builder" API key, which can only read Website Builder data. To use Headless CMS content, content entry inputs, or CMS live preview in your frontend, give the key read access to Headless CMS under **Settings → Access Management → API Keys**. + + + +{/* custom-steps:end */} + diff --git a/docs/release-notes/6.5.0/upgrade-nextjs-frontend.mdx b/docs/release-notes/6.5.0/upgrade-nextjs-frontend.mdx new file mode 100644 index 000000000..68ae7680b --- /dev/null +++ b/docs/release-notes/6.5.0/upgrade-nextjs-frontend.mdx @@ -0,0 +1,458 @@ +--- +id: wb6sdksc +title: Upgrade Next.js Frontend to 6.5.0 +description: Upgrade a Next.js frontend to the Webiny 6.5 SDK, whether it's built on the 6.4 starter kit or talks to Webiny without the SDK. +--- + +import { Alert } from "@/components/Alert"; + + + +- what changed in the Next.js SDK in Webiny 6.5 +- how to upgrade a frontend built on the 6.4 starter kit +- how to add the Webiny SDK to a Next.js app that has never used it +- which AI prompt to give your coding agent for each case + + + + + +This guide covers your Next.js frontend. Upgrade your Webiny project first, following [Upgrade from 6.4.x to 6.5.0](./upgrade-guide). + + + +## Overview + +Webiny 6.5 replaces the two packages the Next.js starter kit used before, `@webiny/website-builder-nextjs` and `@webiny/sdk`, with a single package, `@webiny/sdk-nextjs`. One `sdk` object now covers Website Builder pages, Headless CMS entries, languages, file manager, and tenant manager. The same package also powers Headless CMS live preview. + +What you need to do depends on your frontend: + +- **Built on the 6.4 starter kit.** Follow [Upgrade a 6.4 Starter Kit Project](#upgrade-a-6-4-starter-kit-project). +- **A Next.js app that has never used the Webiny SDK.** Follow [Add the SDK to an Existing Next.js App](#add-the-sdk-to-an-existing-next-js-app). +- **No frontend yet.** Start from the 6.5 starter kit. See [Setup Next.js Project](/website-builder/setup-nextjs). + +Each section gives you two ways to do the same work. Pick one: + +- **Use an AI agent.** Paste the prompt into Claude Code, Cursor, Copilot, or a similar agent from your Next.js project root. The prompts are self-contained: they describe the target setup, point the agent at the reference starter kit, and say what to leave alone. +- **Make the changes yourself.** Follow the steps under the prompt. They list the same changes the prompt makes, so they also work as a checklist for reviewing what the agent did. + +The reference implementation is the `starter-kit-6.5.x` branch of [webiny/website-builder-nextjs](https://github.com/webiny/website-builder-nextjs/tree/starter-kit-6.5.x). + +## File Inputs + +6.5 adds crops, focal points, and alt text to images picked in a `createFileInput()` input. The stored value keeps every field 6.4 had, so existing components keep working, and gains these: + +| Field | Holds | +| -------------------------------------------------------------- | --------------------------------------------------------------- | +| `image.width`, `image.height` | The image's intrinsic size, the same values as `width`/`height` | +| `image.crop`, `image.focalPoint`, `image.alt`, `image.caption` | Edits made in the 6.5 editor | +| `url` | `src` with the crop applied | + +The root `mimeType`, `width`, and `height` stay for backwards compatibility. New code should read `image.width` and `image.height`. + +This has two consequences: + +- A frontend on 6.4 packages reads `src`, so it shows the original, uncropped image and ignores alt text and focal points set in the 6.5 editor. Nothing breaks. +- Values saved in 6.4 have no `image` or `url` until an editor picks or edits the image in 6.5. To read old and new values the same way on 6.5 packages, use `normalizeToAsset()`. It fills in `image` and `url` for values saved in 6.4: + +```typescript +import { normalizeToAsset } from "@webiny/sdk-nextjs"; // also exported by @webiny/website-builder-nextjs 6.5 + +const asset = normalizeToAsset(inputs.image); +// asset.url, asset.mimeType, asset.image?.width, asset.image?.height, asset.image?.alt +``` + +Use `asset.url` rather than `asset.src`, so crops set in the editor show up. + +## When to Upgrade + +Keep your frontend's Webiny packages on the same version as your Webiny project. The quickest way to get there is to bump `@webiny/website-builder-nextjs` and `@webiny/sdk` to 6.5.0. That needs no code changes. The API calls, the editor connection, and the exported functions are the same as in 6.4. + +Moving to `@webiny/sdk-nextjs` is a larger change that you can make later. It's what gives your frontend Headless CMS content, CMS live preview, and content entry inputs. + +A frontend that stays on 6.4 packages also keeps working against a 6.5 API. It shows images uncropped, because crops set in the 6.5 editor need 6.5 packages. That helps when the frontends aren't yours to deploy, for example when each of your clients hosts their own site. Upgrade Webiny first, then give each client this page. + +### Upgrade Order + +Upgrade Webiny first, then the frontend: + +- Upgrade and deploy your Webiny project, following [Upgrade from 6.4.x to 6.5.0](./upgrade-guide). A frontend on 6.4 packages keeps working against the 6.5 API. +- Bump the frontend packages to 6.5.0 when it suits you. To show crops, focal points, and alt text from the 6.5 editor, read file inputs through `normalizeToAsset()`. See [File Inputs](#file-inputs). +- Move to `@webiny/sdk-nextjs` whenever it suits you. + +Don't upgrade the frontend first. The 6.5 packages rely on API features that a 6.4 project doesn't have. + +## What Changed + +| Area | 6.4 starter kit | 6.5 starter kit | +| ---------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| Packages | `@webiny/website-builder-nextjs` and `@webiny/sdk` | `@webiny/sdk-nextjs` | +| SDK object | `contentSdk` for pages, a separate `new Webiny()` client for everything else | One `sdk` object: `sdk.wb`, `sdk.cms`, `sdk.languages`, and so on | +| Initialization | `contentSdk.init({ apiKey, apiHost, apiTenant, preview, theme }, callback)` | `sdk.init({ endpoint, token, tenant, preview, wb: { theme, componentGroups } })` | +| Return values | `contentSdk.getPage()` returns the page or `null` | `sdk.wb.getPage()` returns a `Result` | +| Component groups | `registerComponentGroup()` calls in a callback | `componentGroups` array passed to `sdk.init()` | +| Component registration | `createComponent()` | `createWbComponent()` (`createComponent()` still works, it's the same function) | +| Environment variables | `NEXT_PUBLIC_WEBSITE_BUILDER_API_KEY`, `_API_HOST`, `_API_TENANT`, `_ADMIN_HOST` | `NEXT_PUBLIC_WEBINY_API_KEY`, `_API_HOST`, `_API_TENANT`, `_ADMIN_HOST` | +| API key | "Website Builder" (Website Builder read only) | "Frontend Integration" (Website Builder, Headless CMS, and languages read) | +| Project layout | Everything under `src/` | Files at the project root (`app/`, `sdk/`, `theme/`, ...) | + +The rest works the same way as before: the middleware that handles draft mode, tenants, and redirects, the `/api/preview` route, the `DocumentRenderer` wrapper, and the theme files. + +## API Key and Environment Variables + +The 6.5 starter kit reads `NEXT_PUBLIC_WEBINY_*` variables. Where you get the values depends on when your Webiny project was first deployed. + +- **Deployed on 6.5 or later.** Webiny created a "Frontend Integration" API key for you. The **Configure Frontend** dialog (**Dev Tools** menu in Admin) shows the key and the `NEXT_PUBLIC_WEBINY_*` variables. Copy them as they are. +- **Upgraded from 6.4.** The project has the older "Website Builder" key and no "Frontend Integration" key. The dialog falls back to the old key and shows the old `NEXT_PUBLIC_WEBSITE_BUILDER_*` names. Rename the variables to `NEXT_PUBLIC_WEBINY_*` and keep the values. + +The old "Website Builder" key can only read Website Builder data. That covers rendering pages and redirects. To use Headless CMS content, content entry inputs, or CMS live preview in your frontend, the key also needs read access to Headless CMS. Edit it under **Settings → Access Management → API Keys**, or create a new read-only key with Website Builder, Headless CMS, and Languages read access. + +## Upgrade a 6.4 Starter Kit Project + +Use this if your app started from the Next.js starter kit before Webiny 6.5. You can tell by the imports: `@webiny/website-builder-nextjs` and a `src/contentSdk/` folder. + +### Option 1: Use an AI Agent + +Paste this prompt into your agent. It makes every change listed in Option 2, so you don't need to do those steps as well. + +```text +Upgrade this Next.js app from the Webiny 6.4 SDK to the Webiny 6.5 SDK. + +The app started from the Webiny Next.js starter kit (6.4). It uses +@webiny/website-builder-nextjs and @webiny/sdk. Webiny 6.5 replaces both with a +single package, @webiny/sdk-nextjs. + +Reference implementation (read these files before changing anything): +https://github.com/webiny/website-builder-nextjs/tree/starter-kit-6.5.x +Most relevant: sdk/initializeSdk.ts, sdk/SdkInitializer.ts, sdk/groups.ts, +app/layout.tsx, app/(site)/[[...slug]]/page.tsx, app/api/redirects/route.ts, +components/DocumentRenderer.tsx, next.config.ts, theme/theme.ts, theme/theme.css. + +Make these changes: + +1. Dependencies + - Remove @webiny/website-builder-nextjs and @webiny/sdk from package.json. + - Install @webiny/sdk-nextjs@~6.5.0 with the package manager this project + already uses (check the lockfile). + +2. Imports + - Replace every import from "@webiny/website-builder-nextjs" and + "@webiny/sdk" with "@webiny/sdk-nextjs". + - "@webiny/website-builder-nextjs/webpack" becomes "@webiny/sdk-nextjs/webpack.js". + - "@webiny/website-builder-nextjs/lexical.css" becomes "@webiny/sdk-nextjs/lexical.css". + - The Language type is now exported from "@webiny/sdk-nextjs". + +3. SDK initialization + - Replace contentSdk.init(...) with sdk.init(...) from "@webiny/sdk-nextjs": + sdk.init({ + endpoint: String(process.env.NEXT_PUBLIC_WEBINY_API_HOST), + token: String(process.env.NEXT_PUBLIC_WEBINY_API_KEY), + tenant: tenantId ?? String(process.env.NEXT_PUBLIC_WEBINY_API_TENANT), + preview, + wb: { theme, componentGroups } + }); + - The old second argument (a callback calling registerComponentGroup) is gone. + Turn the registered groups into an exported array of ComponentGroup objects + (same name, label, description, icon, filter) and pass it as wb.componentGroups. + - Keep the existing initializer function and client initializer component, but + rename them if you like (the reference uses initializeSdk and SdkInitializer). + - Delete the separate `new Webiny({...})` client (usually src/webinySdk.ts). + Replace its uses: webinySdk.languages -> sdk.languages, webinySdk.cms -> sdk.cms, + webinySdk.fileManager -> sdk.fileManager, webinySdk.tenantManager -> sdk.tenantManager. + +4. Data fetching. The new methods return a Result instead of a raw value: + - contentSdk.getPage(path) -> sdk.wb.getPage(path) + old: page or null. new: check result.isOk(), then use result.value. + - contentSdk.listPages() -> sdk.wb.listPages() + old: pages.data. new: result.isOk() ? result.value.data : []. + - contentSdk.getRedirectByPath(path) -> sdk.wb.getRedirectByPath(path) + new: the redirect is result.value when result.isOk(). Keep the redirects + route's response shape ({ redirect }) unchanged, because middleware.ts reads it. + + Initialization order matters. In 6.4 the separate `new Webiny()` client was + created when its module loaded, so calls like webinySdk.languages always worked. + In 6.5 every sdk.* call throws "SDK is not initialized" until sdk.init() has run. + - Call the SDK initializer once at the top of every server entry point, before + any sdk.* call: page components, generateMetadata, generateStaticParams and + route handlers. In page components and generateMetadata, pass the draft mode + flag: + const { isEnabled } = await draftMode(); + initializeSdk({ preview: isEnabled, tenantId: await getTenant() }); + draftMode() isn't available in generateStaticParams, and the redirects route + gets the tenant from its query string, so pass only the tenant there. + - Do not leave the initializer inside a helper such as getPage() when other SDK + calls (for example sdk.languages.listLanguages()) run next to it in + Promise.all. The other call can run first and fail on the first request + after a cold start. + +5. Components + - createComponent(...) can stay, or be renamed to createWbComponent(...). Both + are the same function. Do not change component `name` values. They are + stored in published pages, and renaming breaks those pages. + - File inputs (createFileInput). 6.5 adds an `image` object (width, height, + crop, focalPoint, alt, caption) and a `url` with the crop applied. The root + mimeType, width and height stay for backwards compatibility. Values saved in + 6.4 have no `image` or `url`. Find every component that reads a file input + and read the value through normalizeToAsset() from "@webiny/sdk-nextjs", + which fills in `image` and `url` for values saved in 6.4: + const asset = normalizeToAsset(inputs.image); + // asset.url, asset.mimeType, asset.image?.width, asset.image?.height, asset.image?.alt + Use asset.url (not src) so crops set in the editor show up, and + asset.image?.alt for the alt text. + +6. Environment variables + - Rename NEXT_PUBLIC_WEBSITE_BUILDER_API_KEY, NEXT_PUBLIC_WEBSITE_BUILDER_API_HOST, + NEXT_PUBLIC_WEBSITE_BUILDER_API_TENANT and NEXT_PUBLIC_WEBSITE_BUILDER_ADMIN_HOST + to NEXT_PUBLIC_WEBINY_API_KEY, NEXT_PUBLIC_WEBINY_API_HOST, + NEXT_PUBLIC_WEBINY_API_TENANT and NEXT_PUBLIC_WEBINY_ADMIN_HOST. + - Update every reference: .env files, .env.example, next.config.ts (image + remotePatterns and the frame-ancestors CSP header), the SDK initializer, and + any CI or hosting configuration files in the repo. Keep the values. + - List every environment variable I need to rename in my hosting provider. + +7. next.config.ts + - Add "pino-pretty": "commonjs pino-pretty" next to the existing + "thread-stream" entry in config.externals. + +Do not: +- Move files out of src/. The 6.5 starter kit keeps files at the project root, + but that layout is optional. Keep the current structure and path aliases. +- Change my custom components, styles, or theme values beyond import paths and + the file input change in step 5. +- Add the starter kit's example features (articles, ContentEntryDemo, tenant + theme loading) unless I ask. + +When you're done: +- Run the TypeScript check and `next build`, and fix any errors. +- Check that every server file calling sdk.* initializes the SDK first in the + same function, before any parallel calls. +- Search the codebase for "website-builder-nextjs", "@webiny/sdk\"", "contentSdk." + and "NEXT_PUBLIC_WEBSITE_BUILDER" and confirm nothing is left (yarn.lock and + folder names don't count). +- Summarize what you changed and list anything you were unsure about. +``` + +### Option 2: Make the Changes Yourself + +Use these steps if you're not using an AI agent. If you ran the prompt, use them to review the agent's changes. + +- Replace `@webiny/website-builder-nextjs` and `@webiny/sdk` with `@webiny/sdk-nextjs@~6.5.0` in `package.json`, and update every import. The webpack helper moves to `@webiny/sdk-nextjs/webpack.js`, the Lexical styles to `@webiny/sdk-nextjs/lexical.css`, and the `Language` type to `@webiny/sdk-nextjs`. +- Replace `contentSdk.init()` with `sdk.init()`. The config keys change (`apiHost` to `endpoint`, `apiKey` to `token`, `apiTenant` to `tenant`), and `theme` moves under `wb`. +- Turn your `registerComponentGroup()` calls into a `componentGroups` array and pass it as `wb.componentGroups`. +- Delete the separate `new Webiny()` client and use the matching property on `sdk` instead (`sdk.languages`, `sdk.cms`, and so on). +- Update page, page list, and redirect calls to `sdk.wb.*` and handle the returned `Result`. +- Call your SDK initializer at the top of every server entry point (page components, `generateMetadata`, `generateStaticParams`, route handlers) before any `sdk.*` call. In 6.4 the separate `new Webiny()` client was ready as soon as its module loaded. In 6.5, `sdk.languages` and the other namespaces throw until `sdk.init()` has run, so a call that runs in parallel with a helper that initializes the SDK fails on the first request after a cold start. +- Read file inputs through `normalizeToAsset()`, so crops, focal points, and alt text from the 6.5 editor show up. See [File Inputs](#file-inputs). +- Rename the environment variables in `.env`, `next.config.ts`, the SDK initializer, and your hosting provider. +- In `next.config.ts`, add `"pino-pretty": "commonjs pino-pretty"` next to the existing `thread-stream` entry in `config.externals`. + +Here's the SDK initializer before and after: + +```typescript src/contentSdk/initializeContentSdk.ts +// ❌ 6.4 +import { + contentSdk, + type WebsiteBuilderThemeInput, +} from "@webiny/website-builder-nextjs"; +import { registerComponentGroups } from "./groups"; + +export const initializeContentSdk = ({ + tenantId, + preview, + theme, +}: ContentSdkOptions = {}) => { + contentSdk.init( + { + apiKey: String(process.env.NEXT_PUBLIC_WEBSITE_BUILDER_API_KEY), + apiHost: String(process.env.NEXT_PUBLIC_WEBSITE_BUILDER_API_HOST), + apiTenant: + tenantId ?? String(process.env.NEXT_PUBLIC_WEBSITE_BUILDER_API_TENANT), + preview, + theme, + }, + registerComponentGroups, + ); +}; +``` + +```typescript sdk/initializeSdk.ts +// ✅ 6.5 +import { sdk, type WebsiteBuilderThemeInput } from "@webiny/sdk-nextjs"; +import { componentGroups } from "./groups"; + +export const initializeSdk = ({ + tenantId, + preview, + theme, +}: SdkOptions = {}) => { + sdk.init({ + endpoint: String(process.env.NEXT_PUBLIC_WEBINY_API_HOST), + token: String(process.env.NEXT_PUBLIC_WEBINY_API_KEY), + tenant: tenantId ?? String(process.env.NEXT_PUBLIC_WEBINY_API_TENANT), + preview, + wb: { theme, componentGroups }, + }); +}; +``` + +And fetching a page: + +```typescript +// ❌ 6.4 +const page = await contentSdk.getPage(path); + +// ✅ 6.5 +const result = await sdk.wb.getPage(path); +const page = result.isOk() ? result.value : null; +``` + +## Add the SDK to an Existing Next.js App + +Use this if you have a Next.js App Router app that has never used the Webiny SDK and you want to render Website Builder pages in it. Your routes, layouts, and components stay. The SDK adds a route for Webiny pages and the plumbing the editor needs to load your app in an iframe. + +### Option 1: Use an AI Agent + +Paste this prompt into your agent. It makes every change listed in Option 2, so you don't need to do those steps as well. + +Before running the prompt, decide where Webiny pages should live and replace `` with it. Use `/` if Webiny should own every URL that your app doesn't already handle, or a prefix such as `/pages` to keep Webiny pages in one section. + +```text +Add the Webiny SDK (Webiny 6.5) to this existing Next.js App Router app so it can +render Webiny Website Builder pages and support live editing in the Webiny editor. + +Reference implementation (read these files before changing anything): +https://github.com/webiny/website-builder-nextjs/tree/starter-kit-6.5.x +Most relevant: middleware.ts, next.config.ts, app/layout.tsx, +app/(site)/[[...slug]]/page.tsx, app/api/preview/route.ts, +app/api/redirects/route.ts, sdk/*, components/DocumentRenderer.tsx, +editorComponents/index.tsx, theme/theme.ts, theme/theme.css, utils/normalizeSlug.ts, +utils/SlugNormalizer.ts, constants.ts. + +Webiny pages should be served under: + +First, inspect the app and tell me: +- the Next.js version, whether it uses the App Router, and whether a src/ folder is used +- whether a middleware.ts already exists and what it does +- whether a catch-all route would conflict with existing routes under +- how global styles and fonts are loaded +Then make these changes, adapting paths to this project's structure: + +1. Install @webiny/sdk-nextjs@~6.5.0 with the package manager this project + already uses. It needs Next.js 15 and React 19. Next.js 16 isn't supported + yet. If the app is on a different major version, stop and tell me. + +2. Environment variables. Add to .env.example (never commit real values): + NEXT_PUBLIC_WEBINY_API_KEY= + NEXT_PUBLIC_WEBINY_API_HOST= + NEXT_PUBLIC_WEBINY_API_TENANT=root + NEXT_PUBLIC_WEBINY_ADMIN_HOST= + I'll get the values from the "Configure Frontend" dialog in Webiny Admin. + +3. SDK setup. Create an sdk/ folder (or src/sdk/) modeled on the reference: + - initializeSdk.ts: calls sdk.init({ endpoint, token, tenant, preview, + wb: { theme, componentGroups } }) from "@webiny/sdk-nextjs", reading the + NEXT_PUBLIC_WEBINY_* variables. + - SdkInitializer.ts: a "use client" component that calls initializeSdk with + the draft mode flag, theme, and tenant id. + - getTenant.ts: reads the X-Tenant request header, defaulting to "root". + - groups.ts: a componentGroups array with at least one group. + +4. Theme. Create theme/theme.css that starts with + @import "@webiny/sdk-nextjs/lexical.css"; and defines the CSS variables the + editor should know about, and theme/theme.ts that exports `css` and + `theme = createTheme({...})` exactly as in the reference. Base the colors, + fonts, and typography on the styles this app already uses, not on the + reference values. + +5. next.config.ts. Merge these into the existing config without removing + anything: + - injectThemeCss from "@webiny/sdk-nextjs/webpack.js" and its webpack + plugins, pointed at theme/theme.css. + - A Content-Security-Policy header "frame-ancestors http://localhost:3001 + " so Webiny Admin can embed the app. + If a CSP header already exists, add frame-ancestors to it. + - images.remotePatterns for the NEXT_PUBLIC_WEBINY_API_HOST hostname. + - config.externals entries for "thread-stream" and "pino-pretty". + If next.config is not async yet, convert it to an async function as in the + reference. + +6. Middleware. Port the reference middleware.ts logic: + - wb.preview=true or wb.editing=true query params enable draft mode via + /api/preview, and disable it when the params are gone. + - wb.tenant query param sets the X-Tenant request header. + - Redirect lookup through /api/redirects. + If a middleware already exists, merge this logic into it and keep its current + behavior. Keep the matcher from excluding the app's existing needs. + +7. API routes. Add app/api/preview/route.ts and app/api/redirects/route.ts from + the reference, and constants.ts (trailingSlash, redirectsCacheTtl). + +8. Page route. Add an optional catch-all route under based on + app/(site)/[[...slug]]/page.tsx: + - Initialize the SDK with the draft mode flag at the top of each function, + before any SDK call (including calls run in parallel), + including in generateStaticParams and generateMetadata. + - Fetch with sdk.wb.getPage(path) and handle the Result (result.isOk()). + - Render through a DocumentRenderer wrapper (components/DocumentRenderer.tsx + in the reference) that uses SSR normally and disables SSR when the + wb.editing query param is "true". + - If is not "/", strip the prefix before calling getPage, and + map paths from sdk.wb.listPages() back into the prefix in + generateStaticParams. + - Skip the language selector and multi-language logic unless I ask for it. + +9. Root layout. In the root layout, call initializeSdk with the draft mode flag + and tenant id, add to , and render as the first child + of . Keep everything else in the layout. + +10. Editor components. Create editorComponents/index.tsx with one simple example + component registered with createWbComponent(Component, { name, label, + inputs }) and createTextInput. Tell me which of my existing components would + make good editor components, but don't register them yet. + +Do not: +- Change existing routes, pages, or components beyond what is listed above. +- Copy the reference's example features (articles, ContentEntryDemo, tenant theme + loading, Header, LanguageSelector). +- Put real credentials in any committed file. + +When you're done: +- Run the TypeScript check and `next build`, and fix any errors. +- Summarize every file you added or changed, and explain how to test: start the + dev server, set Frontend Domain in Webiny Admin's "Configure Frontend" dialog + to the dev server URL, create and publish a page, and open it. +``` + +### Option 2: Make the Changes Yourself + +Use these steps if you're not using an AI agent. If you ran the prompt, use them to review the agent's changes. Each item below corresponds to a file in the [starter kit](https://github.com/webiny/website-builder-nextjs/tree/starter-kit-6.5.x). Copy it and adapt it to your app. + +- **`package.json`.** Install `@webiny/sdk-nextjs@~6.5.0`. It requires Next.js 15 and React 19. Next.js 16 isn't supported yet. +- **`.env`.** Add the `NEXT_PUBLIC_WEBINY_*` variables from the **Configure Frontend** dialog. +- **`sdk/`.** Add `initializeSdk.ts`, `SdkInitializer.ts`, `getTenant.ts`, and `groups.ts`. +- **`theme/`.** Add `theme.css` and `theme.ts`. The editor reads colors, fonts, and typography styles from `createTheme()`, so base them on your existing design. +- **`next.config.ts`.** Add the theme CSS webpack plugins, the `frame-ancestors` CSP header, and the image remote pattern. Without the CSP header, the browser refuses to load your app inside the Webiny editor. +- **`middleware.ts`.** Add draft mode handling, the tenant header, and redirect lookup. Merge it with your middleware if you already have one. +- **`app/api/preview/route.ts` and `app/api/redirects/route.ts`.** Add both routes. +- **A page route.** Add a catch-all route that fetches the page with `sdk.wb.getPage()` and renders it with `DocumentRenderer`. +- **Root layout.** Initialize the SDK, inject the theme CSS, and render `SdkInitializer`. +- **`editorComponents/`.** Register the components editors can use. See [Create Custom Component](/website-builder/custom-component). + +Then set **Frontend Domain** in the **Configure Frontend** dialog to your app's URL, create a page in **Website Builder → Pages**, and publish it. + +## Headless CMS Content and Live Preview + +The same `sdk` object reads Headless CMS content, so you don't need a second client: + +```typescript +const result = await sdk.cms.listEntries({ + modelId: "article", + where: { values: { slug } }, + limit: 1, +}); +``` + +To show an entry in the Headless CMS live preview pane, set `previewPath` in the model settings. In a code-defined model, use `.settings({ previewPath: "/articles/{values.slug}" })`. The pane loads `/articles/preview` with `wb.editing=true`, `wb.type=entry`, and `wb.id=` query parameters. Your app needs a route at that path that renders the entry with `EntryRenderer`. The `app/(site)/articles/` folder in the starter kit shows both the published route and the preview route. + +For CMS features, the API key needs Headless CMS read access. See [API Key and Environment Variables](#api-key-and-environment-variables). diff --git a/scripts/generate-upgrade-guide.ts b/scripts/generate-upgrade-guide.ts index 90bc9b042..ad624481e 100644 --- a/scripts/generate-upgrade-guide.ts +++ b/scripts/generate-upgrade-guide.ts @@ -4,9 +4,12 @@ * * Usage: * yarn tsx scripts/generate-upgrade-guide.ts --version 6.1.0 + * + * Regenerating an existing guide keeps its frontmatter `id` and everything between the + * custom steps markers below, so release-specific steps survive the release workflows. */ -import { writeFileSync, mkdirSync, readdirSync } from "fs"; +import { writeFileSync, mkdirSync, readdirSync, existsSync, readFileSync } from "fs"; import { join } from "path"; import { valid, lt } from "semver"; @@ -60,12 +63,48 @@ function inferPreviousVersion(version: string): string { return `${parseInt(major, 10) - 1}.x.x`; } +// --------------------------------------------------------------------------- +// Preserving an existing guide +// --------------------------------------------------------------------------- + +const CUSTOM_STEPS_START = "{/* custom-steps:start - kept when the guide is regenerated */}"; +const CUSTOM_STEPS_END = "{/* custom-steps:end */}"; + +interface ExistingGuide { + id?: string; + customSteps?: string; +} + +function readExistingGuide(path: string): ExistingGuide { + if (!existsSync(path)) { + return {}; + } + const content = readFileSync(path, "utf-8"); + const id = content.match(/^id:\s*(\S+)\s*$/m)?.[1]; + + const start = content.indexOf(CUSTOM_STEPS_START); + const end = content.indexOf(CUSTOM_STEPS_END); + const customSteps = + start !== -1 && end > start + ? content.slice(start + CUSTOM_STEPS_START.length, end).trim() + : undefined; + + return { id, customSteps }; +} + // --------------------------------------------------------------------------- // MDX builder // --------------------------------------------------------------------------- -function buildUpgradeGuideMdx(version: string, previousVersion: string): string { - const id = Math.random().toString(36).slice(2, 10); +function buildUpgradeGuideMdx( + version: string, + previousVersion: string, + existing: ExistingGuide +): string { + const id = existing.id ?? Math.random().toString(36).slice(2, 10); + const customSteps = existing.customSteps + ? `${CUSTOM_STEPS_START}\n\n${existing.customSteps}\n\n${CUSTOM_STEPS_END}\n\n` + : ""; // previousVersion is like "6.2.x" — derive example patch versions from it const prevBase = previousVersion.replace(".x", ""); @@ -130,7 +169,7 @@ Proceed by redeploying your Webiny project: yarn webiny deploy --env {environment} \`\`\` - +${customSteps} `; } @@ -151,11 +190,16 @@ async function main(): Promise { const previousVersion = inferPreviousVersion(version); console.log(` Previous version inferred as: ${previousVersion}`); - const mdx = buildUpgradeGuideMdx(version, previousVersion); - const outDir = join(process.cwd(), "docs", "release-notes", version); mkdirSync(outDir, { recursive: true }); const outPath = join(outDir, "upgrade-guide.mdx"); + + const existing = readExistingGuide(outPath); + if (existing.customSteps) { + console.log(" Keeping the custom steps from the existing guide."); + } + + const mdx = buildUpgradeGuideMdx(version, previousVersion, existing); writeFileSync(outPath, mdx, "utf-8"); console.log(`\n✓ Upgrade guide written to: docs/release-notes/${version}/upgrade-guide.mdx`);