Upgrading
Contents
Requirements
| Next.XP 6 requires Enonic XP 8.1 or later, Guillotine 9, and Next.XP app 6. Guillotine 8 is required for Next.XP 5 on Enonic XP 8. |
Upgrading from v5.x
Compare your project with starter-nextjs while following these steps; the proxy, API routes, and next.config.js are easiest to copy from it.
-
Dependencies: Update
@enonic/nextjs-adapterto^6.0.0. On the XP side, install Guillotine 9 and Next.XP app 6. -
ENONIC_API: Point it to the XP API root instead of the
/siteendpoint:# Before (v5.x) ENONIC_API=http://localhost:8080/site # After (v6.x) ENONIC_API=http://localhost:8080/apiThe Guillotine endpoint is
ENONIC_API+/+GUILLOTINE_API(optional, defaultcom.enonic.app.guillotine:graphql). Media URLs useENONIC_MEDIA_CDN(optional, defaultENONIC_API). Uselocalhostrather than127.0.0.1locally, so draft media loads with the Content Studio session. -
Guillotine 9 query syntax: Pass
siteKey,project, andbranchto theguillotine()field:# Before (v5.x) query($path:ID!) { guillotine { get(key: $path) { ... } } } # After (v6.x) query { guillotine(siteKey: $siteKey, branch: $branch, project: $project) { get(key: $path) { ... } } }The adapter supplies
$siteKey,$branch,$project, and$path. Query and variables functions now receive them as aGlobalVariablesobject instead of the content path; a variables function must return them, optionally with custom variables:import type {GlobalVariables} from '@enonic/nextjs-adapter'; export function commonVariables(vars: GlobalVariables) { return vars; } -
Media and page URLs:
imageUrl,mediaUrl,attachmentUrl, andpageUrlreturn objects in Guillotine 9. Query them with the adapter’s helpers and render them with the matching functions, replacinggetUrl(),getAsset(), andtype: absolute:// Before (v5.x) imageUrl: imageUrl(type: absolute, scale: "width(500)") <img src={getUrl(photo.imageUrl, meta)}/> // After (v6.x) ${imageUrlQuery({scale: 'width(500)'})} <img src={imageUrl(photo.imageUrl)}/> ${pageUrlQuery()} <Link href={pageUrl(parent.pageUrl, meta)}>Back</Link>Reference files in
public/by their plain path, for example/images/logo.svg. Remove anymedia.defaultBaseUrlconfiguration in XP. -
Proxy: Replace
src/proxy.tswith the starter’s version. It validates thexpparameter, enables draft mode through/api/preview, rewrites prefix-less URLs to the internal default-locale route, and redirects/<default locale>/…to/…. URLs without the default-locale prefix were already supported in v5; redirecting explicitly prefixed URLs is new in v6. For example, withenas the default,/aboutcontinues to work and/en/aboutnow redirects to/about. -
Encrypted API routes: Replace the token check with blob validation in
/api/previewand/api/revalidate, and copysrc/utils.tsfrom the starter:// Before (v5.x) import {validateToken} from '../../../utils'; // After (v6.x) import {validateBlob} from '../../../utils';validateBlob()returns{ok: true, params}on success and{ok: false, response}otherwise, so routes can read the decryptedxpProject.ENONIC_API_TOKENis still required; it is now the encryption key./api/previewenables draft mode, records the build in a__next_build_idcookie, and redirects to the content path without thexpparameter./api/revalidaterevalidatespageUrl({path}, {locale})for the project’s locale. -
/api/mappings: Add the route from the starter. It returnslocalizeMappings(MAPPINGS, mapping)for the project in the blob. -
/api/renderable: Delete the route. It is no longer called. -
next.config.js: Add
env.BUILD_IDand the CORS headers from the starter. The allowed origin is derived from the optionalENONIC_ADMINvariable, falling back to the origin ofENONIC_API:const config = { env: { BUILD_ID: Date.now().toString(36), }, // ... }; // response headers for '/:path*' {key: 'Access-Control-Allow-Origin', value: xpOrigin}, {key: 'Access-Control-Allow-Credentials', value: 'true'}, {key: 'Access-Control-Allow-Methods', value: 'GET, POST, PUT, DELETE, PATCH, OPTIONS'} -
Adapter API changes:
-
getContentApiUrl(),getUrl(),getAsset(), andUrlProcessorremoved -
MetaData.apiUrlreplaced bybranch,project, andsite -
BaseMacrono longer acceptsrenderInEditMode;RichTextViewno longer acceptsrenderMacroInEditMode, and itscustomReplacerreceives(domNode, data, meta) -
IS_DEV_MODEfollowsNODE_ENV; theMODEvariable is no longer read
-
-
XP configuration: Remove any
contentSecurityPolicy.headerentries added for Next.XP tocom.enonic.app.contentstudio.cfg. The Next.XP app now allows its configured origins automatically, but only explicitly configured URLs in production mode.com.enonic.app.nextxp.cfgkeeps the same property format. Site setup is unchanged: the Next.XP app must be added to the XP site with its front-end configuration selected.
Upgrading from v4.x
Complete these steps first, then follow the v5.x steps above.
-
Adapter upgrade: Update
@enonic/nextjs-adapterto^5.0.0.Requires Node.js 24.15.0+ and npm 10.9.2+. The Guillotine query syntax and
ENONIC_APIURL format are unchanged. -
Enonic XP 8: The back-end must run Enonic XP 8, with application schemas in the YAML format under
src/main/resources/cms/. Guillotine 8 is used with XP 8 — existing queries work unchanged. Note that media links in rich text now use the/_/media:image/and/_/media:attachment/URL format, which requires adapter v5.
Upgrading from v3.x
Complete these steps first, then follow the v4.x and v5.x steps above.
-
Framework upgrades: Next.js 16 and React 19 are now required.
-
Adapter upgrade: Update
@enonic/nextjs-adapterto^4.0.0.The Guillotine query syntax and
ENONIC_APIURL format are unchanged. -
Middleware replaced by proxy: Rename
src/middleware.tstosrc/proxy.ts, following the Next.js 16 convention.If you have custom middleware logic, migrate it to the new proxy pattern.
-
Next.js 16 async params: Route params are now
Promiseand must be awaited:// Before (v3.x) export type PageProps = { locale: string, contentPath: string[], } export default async function Page({params}: { params: PageProps }) { const {locale, contentPath} = params; // After (v4.x) export type PageProps = { locale: string, contentPath?: string[], } export default async function Page({params}: { params: Promise<PageProps> }) { const resolvedParams = await params; -
ESLint flat config: Migrate from
.eslintrctoeslint.config.mjs:import nextVitals from 'eslint-config-next/core-web-vitals' import {defineConfig, globalIgnores} from 'eslint/config' const eslintConfig = defineConfig([ ...nextVitals, { rules: { "@next/next/no-img-element": "off", "eol-last": "warn" } }, globalIgnores(['.next/**', 'out/**', 'build/**', 'next-env.d.ts']) ]) export default eslintConfig -
Default port change:
npm run startno longer forces port 4242 — production now runs on the default port 3000.If you relied on port 4242, update your
com.enonic.app.nextxp.cfgand any reverse-proxy configuration accordingly.
Upgrading from v2.x
-
You need to use Next.js app router to be able to use v3.x. See upgrade instructions from pages router here.
-
_renderablepage is now an API route atsrc/api/renderable/route.ts. -
@enonic/nextjs-adaptermethods renamed:-
getLocaleProjectConfig() → getLocaleMapping()
-
getProjectLocaleConfigById → getLocaleMappingById()
-
-
@enonic/nextjs-adaptermethod signature changes:-
fetchFromApimethod changed to :fetchFromApi( apiUrl: string, projectConfig: ProjectLocaleConfig, options?: FetchOptions): Promise<GuillotineResponseJson>
-
fetchGuillotinemethod signature changes:fetchGuillotine( contentApiUrl: string, projectConfig: ProjectLocaleConfig, options?: FetchOptions): Promise<GuillotineResult>
-
-
@enonic/nextjs-adapterexports rearranged:Exports working both on the server and client sides are now available from
@enonic/nextjs-adapterpackage root. Client-only exports are available from@enonic/nextjs-adapter/clientpackage. Server-only exports are available from@enonic/nextjs-adapter/serverpackage. Views are available at@enonic/nextjs-adapter/views/<ViewName>. -
ENONIC_PROJECTSenvironmental variable change:ENONIC_PROJECTS -> ENONIC_MAPPINGS
Language is now required for every project config:ENONIC_MAPPINGS=<lang>:<repo-name>/site/path[,<lang>:<repo-name>/site/path,...]
First config in the list is used as default. -
next.config.jsconfig changes:Remove i18n block from next.js config:i18n: { locales: ['en', 'no'], defaultLocale: 'en', }Add phrases block to webpack config:config.resolve.alias = { ...config.resolve.alias, "@phrases": path.resolve(__dirname, "./src/phrases"), } -
Add
src/phrasesfolder to your project root. This folder should contain static translations for every language fromENONIC_MAPPINGSenv var in the following format:src/phrases/<lang>.jsonsrc/phrases/en.json src/phrases/no.json
-
Middleware file has been updated to support new i18n config. If you have custom proxy, you need to update it to support new i18n config.