React4XP starter
Contents
The React4XP starter is a ready-to-run Enonic XP application that wires together the library, the @enonic/react4xp build tool and a working Hello world page.
Most of this reference describes React4XP relative to "the starter setup" - the conventions and default values below are what that refers to.
| For a guided, hands-on walkthrough of building an app from the starter, use the React4XP tutorial. This page documents what the starter contains and which defaults it sets. |
Creating a project
enonic project create -r starter-react4xp
The Enonic CLI clones the starter, renames the app, and sets up a sandbox. Then build and deploy:
enonic project deploy
No separate npm install step is needed - the Gradle build runs it (see Build pipeline).
|
Enonic XP |
7.16.1 or later |
|
Node |
22.15.1 or later |
|
npm |
10.9.2 or later |
This page documents the starter’s 6.x branch, which targets Enonic XP 7 and React4XP 6.x (React 19). Create from it with enonic project create -r starter-react4xp -b 6.x. The master branch targets XP 8 and React4XP 7 - see the 7.x documentation for that. |
Project structure
react4xp.config.js (1)
webpack.config.react4xp.js (2)
rspack.config.mjs (3)
tsup.config.ts (4)
build.gradle (5)
gradle.properties
package.json
tsconfig.json
tsconfig.node.json (6)
tsconfig.react4xp.json
tsconfig.xp.nashorn.json
src/main/resources/
├── application.svg
├── assets/ (7)
├── site/
│ ├── site.xml (8)
│ ├── app.ts (9)
│ ├── component.ts (10)
│ ├── content-types/
│ ├── mixins/
│ ├── pages/
│ ├── processors/
│ └── x-data/
├── react4xp/
│ ├── entries/
│ │ └── App.tsx (11)
│ ├── componentRegistry.tsx (12)
│ ├── dataFetcher.ts (13)
│ ├── components/
│ │ └── hello/ (14)
│ ├── constants.ts
│ └── utils/
├── services/
└── types/
└── AppProps.ts
| 1 | React4XP build configuration - entries and chunks. See react4xp.config.js. |
| 2 | Webpack/RSpack customization. See webpack.config.react4xp.js. |
| 3 | RSpack config for the client-side asset build. |
| 4 | tsup config for server-side TypeScript and browser assets - everything except .tsx, which @enonic/react4xp owns. |
| 5 | XP app plugin plus the buildXpResources, react4xp and buildAssets Gradle tasks. |
| 6 | Three TypeScript projects - Node tooling, React4XP components and XP server code - each type-checked separately. See Build pipeline. |
| 7 | Static and client-only assets, served through lib-asset. |
| 8 | Site descriptor with the catch-all mapping and the component service. |
| 9 | The app function - handles all page rendering. See App.ts. |
| 10 | Component function enabling drag-and-drop in the visual page editor. |
| 11 | The one and only React4XP entry. |
| 12 | Maps content types and components to React components. |
| 13 | Maps content types and components to server-side data processors. |
| 14 | Example component: Hello.tsx, Hello.module.css and HelloProcessor.ts. |
Defaults the starter sets
These are the values the rest of this reference assumes.
| Setting | Value | Reference |
|---|---|---|
|
|
|
Entries are picked up from src/main/resources/react4xp/entries/ - see entryDirs |
|
|
unset |
Everything else under react4xp/ bundles into the single |
|
|
empty |
|
|
|
empty |
Nothing excluded from the bundle |
|
Entry count |
1 ( |
The whole site renders through one entry - see Single-entry architecture |
|
Webpack rules |
CSS, CSS modules, Sass, fonts |
Added on top of the built-in JSX/TSX rules - see webpack.config.react4xp.js |
The starter ships no .cfg file - all application config options fall back to their defaults (SSR and hydration both enabled).
Single-entry architecture
The starter does not create one entry per part or page. It registers a single entry, entries/App.tsx, and renders the whole content tree through it:
const App: React.FC<AppProps> = ({component, data, common, meta}) => {
const compMeta: MetaData = meta as MetaData;
compMeta.componentRegistry = componentRegistry; (1)
return <BaseComponent component={component} data={data} common={common} meta={compMeta}/>; (2)
};
| 1 | The registry tells BaseComponent which React component to use for each content type or component descriptor. |
| 2 | BaseComponent from @enonic/react-components walks the component tree and renders each node. |
Two registries drive this, and adding a component means adding an entry to each:
export const componentRegistry = new ComponentRegistry();
componentRegistry.addContentType('portal:site', {View: Hello});
export const dataFetcher = new DataFetcher();
dataFetcher.addContentType('portal:site', {processor: helloProcessor});
The dataFetcher runs server-side and produces the props; the componentRegistry maps them to a React component. The processor never runs in the browser:
export const helloProcessor: ComponentProcessor<PageDescriptor> = (params) => {
const title = params.content?.displayName || params.content._name || 'Display name not set';
return {
title: title,
initialCount: 0,
};
};
Site mapping
The single entry works because the site descriptor routes every request to one function:
<?xml version="1.0" encoding="UTF-8"?>
<site>
<form/>
<mappings>
<mapping controller="/site/app.js" order="10">
<pattern invert="true">/r4xp5.*</pattern> (1)
</mapping>
<mapping controller="/site/component.js" order="10">
<service>component</service> (2)
</mapping>
</mappings>
</site>
| 1 | Matches everything except the internal React4XP routes. |
| 2 | The component service mapping enables drag-and-drop in the visual page editor. |
React4XP’s assets are served by services that ship with the library, so nothing needs mounting - see Asset serving.
See App.ts for the function itself.
Build pipeline
enonic project deploy runs Gradle, which drives three independent npm builds:
| Gradle task | What it does |
|---|---|
|
|
Runs |
|
|
Runs |
|
|
Runs |
All three feed build/resources/main, and jar depends on all three. The division matters: .tsx files belong to the React4XP build, everything else to tsup. Server-side .ts files inside react4xp/ - dataFetcher.ts, processors, utils - are server code and are compiled by tsup.
Useful scripts:
| Command | Purpose |
|---|---|
|
|
|
|
|
Clean, deploy, then watch |
|
|
Type-check all three TypeScript projects and lint |
|
|
Proxy localhost:8080 with automatic browser reload |
|
|
Jest, configured for both server and client environments |
TypeScript projects
The starter keeps three separate TypeScript projects, because the three targets have incompatible requirements - npm run check type-checks each one:
| File | Scope |
|---|---|
|
tsconfig.xp.nashorn.json |
Server-side code running in XP’s Nashorn JS engine |
|
tsconfig.react4xp.json |
|
|
tsconfig.node.json |
Node-side build tooling and config files |
Inside React code, / resolves to src/main/resources/react4xp/ and /lib/xp/ to the @enonic-types packages, so imports like /types/AppProps and /lib/enonic/react4xp work without relative paths.
What the starter does not include
Deliberately out of scope - the starter is a foundation, not a template gallery:
-
No content types, pages, parts or layouts. The site/ subdirectories are empty placeholders. The Hello example is registered against the built-in
portal:sitecontent type. -
No ready-made GraphQL queries.
lib-guillotineis on the classpath, but the starter renders through thedataFetcherrather than through Guillotine. -
No styling framework. Only CSS modules and Sass support in the webpack config.
-
No production config. Add a
.cfgfile yourself to tune SSR, hydration and globals.
Version compatibility
| Starter ref | React4XP | Enonic XP | React |
|---|---|---|---|
|
|
7.x |
8.0.1+ |
19 |
|
|
6.1.0 |
7.16.1+ |
19 |
|
|
5.1.0 |
7.13.3+ |
18 |
v5.1.0 is the last tagged starter release; the 6.x and 7.x lines are maintained as branches. Older tags exist back to v0.2.8 (XP 7.0.0) but are not supported.
To move from React4XP 5 to 6, see the recommended upgrade or the legacy mode upgrade. For 6 to 7, switch to the 7.x documentation.