Legacy mode upgrade
Contents
React4XP 7 (for Enonic XP 8) is still compatible with how parts are rendered in version 5 and 6, where each part is a separate entry.
This means you may perform a partial upgrade of your application, where you will not be using the Component Registry.
Follow the steps below:
Upgrade XP
Set the XP version of your app to 8.0.0 or newer.
xpVersion=8.0.3
The XP 8 app plugin requires Gradle 9. Bump the wrapper:
./gradlew wrapper --gradle-version 9.4.1
| XP 8 also moves the app plugin version and the XP dependency catalog into a Gradle settings plugin - see Upgrade the build below. |
Upgrade package.json
-
Use latest React4XP types
"@enonic-types/lib-react4xp": "^7.0.0", -
Use latest React4XP runtime library
"@enonic/react4xp": "7.0.0", -
Add global Enonic types for XP 8 (includes core transitively)
"@enonic-types/global": "^8.0.3",this brings in @enonic-types/core:├─┬ @enonic-types/global@8.0.3 │ └── @enonic-types/core@8.0.3 -
Add React component library
"@enonic/react-components": "^7.0.0", -
Add CSS-Modules typing & plugin
"@types/css-modules": "^1", "typescript-plugin-css-modules": "^5", -
Server code is now compiled with tsdown. Add it and its SWC transformer
"tsdown": "^0.22.2", "@swc/core": "^1.15.33", -
Remove the obsolete CSS extractor (React4XP 7 uses Rspack’s native CSS support)
// delete this line: "mini-css-extract-plugin": "^2", -
Update the npm scripts. The build now runs tsdown (server code + client assets) and React4XP (the
.tsxentries), and the type checks are split into the React4XP and server projects:"scripts": { "build": "tsdown", "build:react4xp": "npm explore @enonic/react4xp -- npm run build:react4xp", "check": "concurrently -c auto -g --timings npm:check:types:*", "check:types:react4xp":"npx tsc --noEmit -p src/main/resources/react4xp/tsconfig.json", "check:types:server": "npx tsc --noEmit -p src/main/resources/tsconfig.json" }
The easiest way to get the exact, up-to-date config files (tsdown.config.ts, react4xp.config.js, webpack.config.react4xp.js, and the tsconfig.json files) is to copy them from the React4XP starter. |
Folder structure
Your React components stay under react4xp/, and static files and shared TS types keep their place.
In XP 8, CMS schemas and controllers move from site/ to cms/ (see Site mapping). Rename the folder:
git mv src/main/resources/site src/main/resources/cms
and convert its descriptors from XML to the YAML format (kind: "Site", kind: "Part", etc.). The recommended upgrade shows the converted descriptors.
Upgrade the build
XP 8 introduces a Gradle settings plugin that supplies the XP app plugin version and a dependency catalog (xplibs).
-
Add the settings plugin to
settings.gradle:settings.gradleplugins { id 'com.enonic.xp.settings' version '4.1.0' } rootProject.name = projectName -
In
build.gradle, drop the version from thecom.enonic.xp.appplugin - the settings plugin now supplies it:build.gradleplugins { id 'maven-publish' id 'com.enonic.xp.app' id 'com.enonic.defaults' version '2.1.6' id 'com.github.node-gradle.node' version '7.1.0' } -
Migrate XP library dependencies to the
xplibscatalog, and update the React4XP libraries:dependencies { include xplibs.content include xplibs.context include xplibs.portal include xplibs.auth include 'com.enonic.lib:lib-asset:2.0.1' include 'com.enonic.lib:lib-react4xp:7.0.0' }XP libraries ( com.enonic.xp:lib-) are referenced through thexplibs.catalog aliases (lib-contentbecomesxplibs.content, and so on). Third-party libraries such aslib-assetandlib-react4xpkeep their full coordinates. -
Keep TS/TSX/SASS source out of the JAR - tsdown and React4XP produce the compiled output:
processResources { exclude '**/.gitkeep' exclude '**/tsconfig.json' exclude '**/*.ts' // handled by tsdown exclude '**/*.tsx' // handled by @enonic/react4xp exclude '**/*.jsx' // handled by @enonic/react4xp exclude '**/*.scss' exclude '**/*.sass' } -
Inject npm-based type-checks:
tasks.register('npmCheck', NpmTask) { dependsOn npmInstall args = ['run', 'check'] environment = ['FORCE_COLOR': 'true'] } check.dependsOn npmCheck -
Register the tsdown (server + assets) and React4XP (
.tsxentries) build tasks, and wire them into the JAR:tasks.register('npmBuild', NpmTask) { args = ['run', '--silent', 'build'] dependsOn npmInstall environment = ['FORCE_COLOR' : 'true', 'LOG_LEVEL_FROM_GRADLE': gradle.startParameter.logLevel.toString(), 'NODE_ENV' : project.hasProperty('dev') || project.hasProperty('development') ? 'development' : 'production'] inputs.dir 'src/main/resources' outputs.dir 'build/resources/main' outputs.upToDateWhen { false } } tasks.register('react4xp', NpmTask) { def envMode = project.hasProperty('env') ? (project.property('env') == 'prod' ? 'production' : 'development') : 'production' args = ['run', 'build:react4xp'] dependsOn(npmInstall) description 'Compile react4xp resources' environment = [ 'R4X_APP_NAME' : "${appName}", 'R4X_BUILD_LOG_LEVEL' : gradle.startParameter.logLevel.toString(), 'R4X_DIR_PATH_ABSOLUTE_PROJECT': project.projectDir.toString(), 'NODE_ENV' : envMode ] group 'react4xp' inputs.dir 'node_modules/@enonic/react4xp' inputs.dir 'src/main/resources' outputs.dir 'build/resources/main' outputs.upToDateWhen { false } } jar.dependsOn(npmBuild, react4xp)
Upgrade webpack
React4XP 7 compiles React with Rspack and uses its native CSS support, so the old mini-css-extract-plugin / css-loader setup is gone.
-
Enable Rspack’s CSS experiment and disable CSS named exports:
config.experiments = { ...config.experiments, css: true }; config.module.parser = { ...config.module.parser, 'css/auto': { ...(config.module.parser && config.module.parser['css/auto']), namedExports: false } }; -
Replace the loader rules with
css/autorules for CSS and SCSS, plus a font-asset rule:config.module.rules = [ ...(config.module.rules || []), { test: /\.css$/i, type: 'css/auto' }, { test: /\.s[ac]ss$/i, type: 'css/auto', use: [ { loader: 'sass-loader', options: { sassOptions: { outputStyle: 'compressed' } } } ] }, { test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource' } ]; return config;type: 'css/auto'enables CSS Modules for.module.css/.module.scssfiles and plain global styling for the rest.
Upgrade to new types
-
React4XP 7 uses
@enonic-types/core, so you need to replace the old js-utilsRequesttype with the new one. -
Replace
Enonic.Xp.Http.Requestfrom@enonic/js-utils/types/RequestwithRequestfrom@enonic-types/core.
Make production build and deploy
enonic project build
enonic project deploy
You can also run the project in development mode, which watches for changes and automatically deploys them to the sandbox:
enonic dev
Migrating parts
In react4xp v5 the example controller looks like this:
import {render} from '/lib/enonic/react4xp';
import {getComponent} from '/lib/xp/portal';
import type {Enonic} from '@enonic/js-utils/types/Request';
import {toStr} from './toStr';
export function get(request: Enonic.Xp.Http.Request) {
const component = getComponent();
log.debug('component:%s', toStr(component));
const props = {};
const response = render(
component,
props,
request,
{
...
}
);
return response;
}
React4XP 7 uses @enonic-types/core, so you need to replace the old js-utils Request with the new one.
-
Replace
Enonic.Xp.Http.Requestfrom@enonic/js-utils/types/RequestwithRequestfrom@enonic-types/core.
This is how the upgraded component looks:
import {render} from '/lib/enonic/react4xp';
import {getComponent} from '/lib/xp/portal';
import {Request} from '@enonic-types/core';
import {toStr} from './toStr';
export function get(request: Request) {
const component = getComponent();
log.debug('component:%s', toStr(component));
const props = {};
const response = render(
component,
props,
request,
{
...
}
);
return response;
}
So far in this tutorial we have used componentRegistry to render and dataFetcher to invoke the processor for all components.
Since our old component does not have any processor and is rendered outside componentRegistry, we need to exclude it so it becomes available for XP. We do this by updating cms/site.yaml, adding an inverted pattern mapping that lets XP serve the legacy route directly:
kind: "Site"
mappings:
- controller: "/cms/app.js"
order: 10
pattern: "/r4xp5(/.*)?"
invertPattern: true
- controller: "/cms/component.js"
service: "component"
order: 10
apis:
- "asset"
- "react4xp"
Now we can use the v5 component with react4xp v7 - we just need to make sure the url contains /r4xp5.
Migrating pages and layouts
As this is a breaking change you need to follow the recommended guide for migrating pages and layouts, as these depend on the component registry.