React4XP - API reference

Contents

The react4xp library (Enonic Market) exposes a couple of methods that can be run from XP controllers.

This chapter also covers the SSR engine, compilation, and configuration of those.




Getting started

Install

Instructions on how to install the library locally or insert it into your project are at the library docs at github.

Import library

In an XP controller:
const React4xp = require('/lib/enonic/react4xp');




React4xp.render

All-in-one shorthand function for a lot of use cases. Covers both client- and serverside rendering.

Inserts a react component into an (optional) pre-existing HTML string and adds any necessary page contributions to make all work: links to assets, both shared and specific to the entry, and a client-side JS call (.render or .hydrate in the client wrapper) that activates the react component in the browser.


Signature

The signature is analogous to thymeleaf’s familiar render(view, model). But there are two extra parameters, and a full XP response object is returned:

{body, pageContributions} = React4xp.render(entry, props, request, options);


Parameters

Name Type Description

entry

string or component object, mandatory

Reference to an entry: the react component to be rendered. JsxPath reference - or if the entry is a JSX file with the same name in the same folder as the controller, you can use a portal.getComponent() object here. Corresponds to the template argument in thymeleaf.render.

props

object, optional

Data model passed into the react component. JS object must be serializable (no functions can be passed). Corresponds to the model argument in thymeleaf.render.

request

XP request object, optional (but mandatory for react activation)

Include to detect the rendering mode inside/outside Content Studio: inside Content Studio there should be only a static serverside-rendering, no browser react activation (or client-side rendering), only returning get an HTML visualization with the initial props. Special case: if request is not an object but omitted/falsy, page-contributions rendering is completely skipped. In this case, the options argument (below) is still valid: any added body there will still serve as a container for the rendered output from this call, and any pageContributions inside options are still added and returned.

options

object, optional

Additional options to control the rendering. All of them are optional within this object:

Properties
Name Type Default Description

id

string

Generated unique string

ID of the component, targeting the ID of an element in body (below): react will render into that container element. Should be a unique ID within the entire HTML document.

If no matching element ID is found in body, this sets the ID of a generated element in the HTML output. If id is missing, a unique ID is generated, either random or generated from the content.

body

string: valid HTML

<div> with matching id

HTML to serve as container for the react content. Can be a hardcoded string, come from a thymeleaf/mustache/XSLT render, or any source. When server-side rendering, the rendered output will be inserted into the matching-id element inside here (replacing whatever was already in that element), and everything (container body with rendered content in it) is returned as one HTML string. When client-side rendering, this insertion happens in the browser.

If no body is supplied, an empty <div> element with an ID matching id is generated and used as container.

If a body is supplied but it doesn’t contain any element with a matching id, an extra matching-id <div> element will be generated and inserted into body - as a child of the root element of body and after any other content that’s already there.

pageContributions

object: valid XP page contributions

If you already have some page contributions you want to add to the output of this rendering, add them here. These added page contributions will be added before the ones that will be rendered (within each section headBegin, bodyEnd etc).

clientRender

boolean

false

Switch between clientside and servierside rendering, on this particular rendering. Other renderings are not affected, even within the same controller or using the same entry more than once.

If false / falsy or omitted, you get serverside rendering and the returned object will contain an HTML representation of the react component with the initial props, and page contributions will make the client call .hydrate.

If true / truthy, the server-side rendering is skipped for this particular rendering. The client will call .render.

This only applies in live mode and previews: inside edit or browse modes in Content Studio, you still only get a static server-side rendered representation).


Returns

Returns an XP response object with these main attributes:

Attribute Type Description

body

string, rendered HTML

HTML output.

The root of this HTML is always a surrounding container HTML that will have a matching-ID target element in it somewhere (an element matching the ID of the clientside call to .render or .hydrate: that ID is options.id if that was set, or a generated unique one if not). This surrounding structure is options.body, unchanged if that already contained a matching-ID element, or with a new target element generated and inserted at the end if it didn’t have one. If there is no options.body, the surrounding container is just a generated target <div> element.

Inside that matching-ID element, there will be a serverside rendering of the entry (with the initial props from options.props) if options.clientRender is falsy.

pageContributions

object

Regular XP page contributions. Includes everything the browser needs to activate (or client-side render) the react component: script tags with urls to auto-compiled assets for the entry and its dependencies, a client-side react4xp wrapper asset and an activating client-wrapper call. Urls point to react4xp’s own optimized asset services. Also included before this, are any input options.pageContributions.



Request and automated behavior

.render is intended to be convenient to work with and safely wrap around some common corner cases. It automates a little bit of behavior, depending on the request object argument (which stems from the XP controller):


render with request:

React4xp.render(entry, props, request);
React4xp.render(entry, props, request, options); // ...etc etc

If request is supplied, viewing context is detected from request.mode: is rendering happening inside or outside of Content Studio?

  • Inside Content Studio, .render will always select serverside rendering (no matter what clientRender is) and skip JS dependency assets and the clientside render/hydrate trigger (but still still supply other dependencies, such as CSS). This ensures that a static HTML placeholder rendering is visible inside Content Studio’s browse and edit modes, but keeps react from being activated. This is by design: preventing the possibility that react code might intervene with the UX of Content Studio itself. In order to see working react and interact with your app, preview the page or navigate to a published version:

  • Outside Content Studio, the rendering will be activated as a react app (i.e. all pageContributions are rendered). Also, clientRender is used, so if this is true, serverside rendering is skipped and render is called in the client instead of hydrate.

Also, when request is used, .render will output error messages from SSR in error containers and browser consoles (except in live view mode, where error containers and browser log are more generic: shown without the specific messages).


render without request:

React4xp.render(entry, props);
React4xp.render(entry, props, null);
React4xp.render(entry, props, undefined, options); // ...etc etc

Omitting request from render has the effect of always rendering as if it’s inside Content Studio (see above). Again, this is by design - trying to make sure that a viewable and safe rendering is always returned, even when viewing context can’t be determined.

However, it also means that clientRender has no effect (you always get SSR), and there is no call to activate the react app in the browser. Basically, it’s as if JSX is used as a pure, static HTML templating language (same as XP’s Thymeleaf renderer - which shares the same basic signature).

When request is omitted, .render will never output error messages from SSR in error containers or browser console.


Examples

Most of the lessons in the guide use React4xp.render (except the "custom flow" ones). For example here or here.



React4xp object: custom flow

More flexible and controllable than React4xp.render: create a data-holding react4xp object with the React4xp contructor, manipulate it or extract data from it, combine with other objects, and then later render it to an HTML body string and/or page contributions, separately. This is actually what React4xp.render does behind the scenes.

Call the two rendering methods from the same react4xp object. Remember, if using clientRender and/or request options, they should usually be the same value across the two corresponding calls. A typical (compact) usage example:

exports.get = function(request) => {

    // Object constructor:
    const myComponent = new React4xp('my-entry');

    // ...read myComponent attributes and/or use its setter methods...

    // const clientRender = ...true or false...

    // Call the rendering methods:
    return {
        body: myComponent.renderBody({
            // clientRender, etc
            request
        }),
        pageContributions: myComponent.renderPageContributions({
            // clientRender, etc
            request
        })
    }; // ...etc, etc
}

See the "custom flow syntax" lesson to go more in depth.



Object constructor

const myComponent = new React4xp(entry);

Creates an initial react4xp data object from an entry.

Parameter Type Description

entry

string or component object, mandatory

Reference to an entry: the react component to be rendered. Direct JsxPath string, or a portal.getComponent() object. If you use a component object like that, the entry must be a JSX file with the same name in the same folder as the controller, and react4xp will try to generate an ID from the content.

Constructs a react4xp data object, which exposes the attributes and methods below:



Main object attributes

Extract from the object the data that has been generated or set in it.

Name Type Description

react4xpId

string

Target id of the HTML element the entry will be rendered into (if it’s been set yet - see setId and uniqueId below). Also identifies the object.

jsxPath

string

jsxPath to the entry.

props

object

props for the entry’s initial rendering. At the time of rendering, an attribute react4xpId is added to the props, allowing each entry to access its own unique ID at runtime.

Example:
const targetElementId = myComponent.react4xpId;



Setter methods

Use these to set the object’s properties. All of them are optional; if not used, the object will render with empty values or placeholders where needed, along the same logic as for React4xp.render above.

All the setter methods return the data object itself, so that you can use a builder pattern where…​

myComponent.firstSetter("a").secondSetter("b").thirdSetter("c");

…​is equivalent to:

myComponent.firstSetter("a");
myComponent.secondSetter("b");
myComponent.thirdSetter("c");

The order between the setters doesn’t matter - except for setId and uniqueId, which affect each other.


.setProps

myComponent.setProps(props);

Sets props for the entry.

Parameter Type Description

props

object, mandatory

props passed into the react component for initial rendering. JS object must be serializable (no functions can be passed).


.setId

myComponent.setId(id);

Sets an ID - directly and literally, so uniqueness is up to you. This ID both identifies this react4xp object (aka. react4xpId), and crucially, points React to an HTML element (in the body param, during render or renderBody later) which is the target container for rendering the entry into. Phew.

If render or renderBody are called without an ID having been set yet, then a unique random ID will be generated on the fly. This of course implies that there will be no matching-ID element in body. In cases like this (or when there’s no body at all), an empty target element with a matching ID will be generated/inserted, to contain the React rendering.

If the data object already has an ID, .setId(id) will overwrite it. If id is omitted/empty, .setId() just deletes any previous ID (which has the later effect of giving this a new, unique ID at the time of rendering).

Parameter Type Description

id

string, optional

ID of both the target HTML element and the data object itself.


.uniqueId

myComponent.uniqueId();

Enforces a unique ID, either by itself or after running .setId(). If the object already has an ID (react4xpId), a random string will be added to it. If not, the ID will just be the random string.

No parameters.


.setJsxPath

myComponent.setJsxPath(jsxPath);

If you for some reason need to override the JsxPath that was set (or inferred from the component object) in the constructor.

Parameter Type Description

jsxPath

string, mandatory

New jsxPath to a different entry.



Rendering methods

These methods perform specific rendering tasks independently, using the data object as a basis, the way it’s set up with the setters and with the entry from the constructor (or the setJsxPath setter).

Most of these rendering methods will lock down the jsxPath and ID if the react4xp data object, the first time one of them is run. After this, the setters will prevent these from being changed so that another conflicting rendering can’t be performed from the same data object.



.renderBody

const responseBody = myComponent.renderBody(options);

Similar to React4xp.render above, but renderBody in itself only renders a static HTML output.

Does not render page contributions. Combine with a corresponding renderPageContributions call from the same data object, or the rendering will not be active in the browser.

→ See the custom flow syntax examples.

renderBody renders based on the state of the data object reached at the time of rendering.

Just like render does, renderBody ensures that the output HTML will always contain a matching-ID target element for react-rendering/hydrating the entry into (in the browser). And if serverside rendering is switched on (that is, clientRender is falsy, or safe context-dependent rendering is enforced by adding request - see the summary), the target element will contain the static HTML rendering.


Parameters
Parameter Type Description

options

object, optional

Options to control the rendering, all of them optional:

Properties
Name Type Default Description

body

string: valid HTML

<div> with matching id (same as react4xpId in the data object)

Same as the options.body in React4xp.render above.

clientRender

boolean

false

Switch between clientside and serverside rendering, on this particular rendering.

request

XP request object

undefined

Including this here (and in the corresponding renderPageContributions call) is the easiest way to handle view-context dependent behavior. Without this, clientRender may be active in Content Studio preview rendering, leading to possibly undesired results.

Other renderings are not affected, even from the same data object (so you usually want to make sure a different rendering from the same data object uses the same mode).


Returns

Returns an HTML string ready to return as the body attribute in an XP response object from the controller.

The root of the returned HTML is always a surrounding container HTML that will have a matching-ID target element in it somewhere (an element matching the data object’s ID (react4xpId), either from the ID setter methods, or a generated ID if they haven’t been run). This surrounding structure is options.body, unchanged if that already contained a matching-ID element, or with a new target element generated and inserted at the end if it didn’t have one. If there is no options.body, the surrounding container is just a generated target element.

Inside that matching-ID element, there will be a serverside rendering of the entry (with the initial props from .setProps) if options.clientRender is falsy.



.renderPageContributions

const outputPageContributions = myComponent.renderPageContributions(options);

Similar to React4xp.render above, but only renders the page contributions needed to run and activate the react component in the browser:

  • references to the entry’s own asset,

  • dependency assets,

  • and the react-activating trigger call in the browser (.render or .hydrate, depending on the clientRender and request options).

Renders based on the state of the data object at the time of rendering.

Does not render any HTML. Run .renderBody from the same data object, or the browser may have nothing to activate / nowhere to render the entry.

Also, unless you add the request option, there is no detection of inside-vs-outside Content Studio, and consequently the client is not automatically prevented from running client-side code in Content Studio. That is not recommended - see the summary.

Parameters:

Parameter Type Description

options

object, optional

Options to control the rendering, all of them optional:

Properties
Name Type Default Description

pageContributions

object: valid XP page contributions

empty object

If you already have some page contributions you want to add to the output of this rendering, add them here. These added page contributions will be added before the ones that will be rendered (within each section headBegin, bodyEnd etc).

clientRender

boolean

false

Switch between clientside and servierside rendering, on this particular rendering.

request

XP request object

undefined

Including this here (and in the corresponding renderPageContributions call) is the easiest way to handle view-context dependent behavior. Without this, clientRender may be active in Content Studio preview rendering, leading to possibly undesired results.

Other renderings are not affected, even from the same data object (so you usually want to make sure a different rendering from the same data object uses the same mode).

Returns:

A regular XP page contributions object, ready to be used as the pageContributions attribute in an XP response object from the controller.

Includes everything the browser needs to activate (or client-side render) the react component: script tags with urls to auto-compiled assets for the entry and its dependencies, a client-side react4xp wrapper asset and an activating trigger call to the client wrapper. Urls point to react4xp’s own optimized asset services. Also included before this, are any input options.pageContributions.

With a serverside rendering (options.clientRender is falsy), the client will expect an existing target element with a pre-rendered entry in the response body, and call react4xp.CLIENT.hydrate. If options.clientRender is truthy, an empty target element is expected in the response body, and the rendering is left to the client with react4xp.CLIENT.render.



Request and automated behavior

The "custom flow" (.renderBody in tandem with .renderPageContributions) is intended as a more low-level approach: less hand-holding, more control to the developer for cases where that’s needed.

However, lib-react4xp version 1.6.0 introduced support for a request option parameter for these methods as well. The main idea is that using request in both calls will now automate some behavior the same way as calling .render with request (see above).

Omitting request will still work the same way as before, leaving more to developers.


Custom flow with request

const body = myComponent.renderBody({
    // clientRender, etc
    request
});
const pageContributions = myComponent.renderPageContributions({
    // clientRender, etc
    request
});
// ...etc, etc

This will act the same way as render used with a request: viewing context is detected, so inside Content Studio, clientRender is ignored and you always get SSR, and JS assets and the .hydrate call is held back so the react component isn’t activated inside Content Studio. And outside Content Studio, you get a fully active render (with optional clientRender).

As with render, error message details are held back in live view mode.


Custom flow without request:

const body = myComponent.renderBody({ /* clientRender, etc */ });
const pageContributions = myComponent.renderPageContributions({ /* clientRender, etc */ });
// ...etc, etc

Contrary to when working with .render, omitting request from the custom flow does not enforce a max-safety rendering. Quite the opposite, removing request will remove all the "safety wheels", so this rendering mode needs a bit of attention to guarantee that everything works everywhere:

  • .renderBody will take clientRender into account in all contexts. What you set it to will take effect.

    This risks a missing/empty visualization inside Content Studio, since clientRender: true makes sure no SSR will render a static placeholder.
  • And .renderPageContributions will render all page contributions in all contexts, including JS dependency assets and the hydrate/render browser-side calls.

    Best case scenario: this might make a clientRender’ed entry visible in Content Studio too. Worst case, it risks intervening with Content Studio’s UX, or even break its functionality, depending on the code used/imported by the entry.



Custom flow examples

Custom flow usage in is demonstrated here.




SSR engine

The default running mode of react4xp is serverside rendering (SSR) (although clientRender can override this).

In a nutshell, source files like JSX are compiled into JS assets that lib-react4xp’s SSR engine runs to render HTML. This output is then delivered to the browser along with dependency code (usually references to necessary assets like CSS, JS etc) - these dependencies are also rendered, as page contributions. Rendering the HTML body and the page contributions happens to two different steps, using either render (which wraps both steps for convenience) or the "custom flow".

The aim of react4xp is isomorphic rendering: after the react component(s) are serverside rendered, they are activated (hydrated) in the browser, turning them into running, active react apps. It’s the same react code that runs at the server as in the browser: no need to write the same component twice - one for SSR and one for the browser (although occasionally, tweaks are needed to prevent browserspecific code from running on the server).


Nashorn

Currently, react4xp needs nashorn to do the actual SSR (albeit we’re looking at upgrading to GraalJS at some point). Since most of the world of react SSR is oriented towards node.js, and nashorn does not have full support for the features in node (nor all features expected by a lot of NPM packages that can be imported by react components), react4xp does some polyfilling of the SSR engine at startup.


Renderers

Starting from version 1.5.0, react4xp handles multithreaded rendering. This is done by setting up a number of renderers where each one is ready to answer to rendering requests in parallel, independently.

The number of renderer workers is determined in java but can be overriden.

When a renderer runs into an error during SSR, that renderer is torn down and a new one is initialized (see warmup time below). This happens as far as possible during idle time.


Warmup time

After your react4xp app is (re)started, the first time react4xp is triggered to render something, the engine will initialize. This means the renderers will load the compiled assets necessary for the rendering, into nashorn in memory:

  • nashorn polyfills,

  • react and reactDOM (externals.*.js),

  • packages from node_modules (vendors.*.js),

  • dependency assets imported by the react components (aka. chunks),

  • and finally, the entry assets themselves.

This causes some warmup time when starting your app: a noticable delay before the first rendering shows up. This may be just a couple of seconds in total, but it may also take longer. It depends on the size and complexity of the compiled assets involved. This will happen on every restart of the app (and every renderer must be initialized, but they do this in parallel).

But as long as the code runs in nashorn without errors, initialization happens only once (i.e. each asset is loaded once at most, on each renderer). After the warmup, the react apps are ready-to-run from nashorn memory, so repeated renderings after that (even with different props) are fast.

Improving warmup time for development

Since development can involve repeated app/server restarts, here are a couple of ways to improve initial loading time when developing large projects:

  • Set ssrLazyload = true (see below). This makes the engine start up and only load the bare minimum of dependency assets instead of preparing all of them at once. Each rendering will also only load/cache the dependencies it needs. The upside of this is that the first rendering (of a component that uses only a subset of the assets) is much faster since there is less to load. The downside is that there will still be assets that haven’t been loaded into the engine yet, which will cause a delay at some other time when they are first rendered.

  • Don’t build the react components and dependencies with buildEnv = development, but use production (which is the default - see below). Assets built with development are much more verbose, and this size difference - although functionally equal - actually makes a difference to nashorn (at the compile-to-bytecode stage).

(Other optimizations and approaches are under consideration, to shorten the warmup time even more).


SSR performance

Apart from when assets are initialized during the engine warmup, each SSR should be fast. However, in cases where you need to improve SSR performance further, it can be done by wrapping the rendering in a cache in the controller.

Be sure to use any value that can change the rendering output - usually from props (and options?) or a subset of them - as a key in the cache.

For example:

const cacheLib = require('/lib/cache');

// Set the cache up with a size that's reasonable
// for the most used props combinations
// and the size of the rendered output HTML string:
const cache = cacheLib.newCache({
    size:   100,
    expire: 3600
});


const makeKey = props => {
    // ...return a string that's reliably determined by the relevant values from props
};


exports.get = request => {
    const props = {
        // ... build props from whatever sources are needed
    };

    const key = makeKey(props);

    // Now render is only called when the key is new.
    // If the key is cached before, just returns the output for that key.
    return cache.get(
        key,
        () => React4xp.render(
            myEntry,
            props,
            request,
            options
        )
    );

};




react4xp.properties

Several aspects of react4xp can be configured with a file react4xp.properties. Some of these settings are relevant to the SSR engine.

If you’re using the starter or your project is based on it, this is already set up and you can skip the following section:


Including react4xp.properties in your project

Put react4xp.properties at the root of your XP project. A template can be copied from here, or from node_modules/react4xp/react4xp.properties after installing the react4xp NPM package.

In order to take effect, it must be handled by gradle during the build:

  • Add this to your build.gradle:

    apply from: "node_modules/react4xp/react4xp.gradle"

    This will not only make sure react4xp.properties is handled, but also give a full react4xp build setup.

  • Of course, this requires that the react4xp NPM package is installed at the time gradle (or the enonic CLI) is run.

    If you want to automate that, you can always add this to your gradle.build:

    def react4xpGradleFile = new File('node_modules/react4xp/react4xp.gradle')
    if (!react4xpGradleFile.exists()) {
        def proc = "npm install".execute()
        proc.in.eachLine { line -> println line }
        proc.out.close()
        proc.waitFor()
    }
    
    apply from: 'node_modules/react4xp/react4xp.gradle'
  • Or, reverse-engineer the whole thing from react4xp.gradle and make your own gradle adaptation.


ssrLazyload

react4xp.properties:
ssrLazyload = true

As described above, ssrLazyload defines the initialization behavior of the renderer workers:

  • If switched on (true), only the required assets are loaded into the nashorn engine, speeding up the first load time, but subsequent loading times for other un-initialized assets will be longer.

  • If switched off (false), the engine will load all assets once and for all, before performing the first rendering. This makes the first warmup time predictably slower (and that may be a substantial difference in a large project), but all subsequent rendering will be spring-loaded and fast.

Default value depends on buildEnv (or -Pdev):

  • true in development

  • false in production


ssrMaxThreads

react4xp.properties:
ssrMaxThreads = 5

If this override value is not set, the number of threads (renderers) used for simultaneous SSR requests is determined by java, by…​

Runtime.getRuntime().availableProcessors()

…​but ssrMaxThreads sets this number manually instead.


ssrSettings

This value can be a number, or a comma-separated string of nashorn options:

react4xp.properties:
# Explicit string of settings:
ssrSettings = --persistent-code-cache, --class-cache-size=42, --lazy-compilation

# Numbers are a shorthand for cache size, so this is equivalent to: --persistent-code-cache, --class-cache-size=1000:
ssrSettings = 1000

# Default value, turns persistent code cache off:
ssrSettings = 0

Persistent code cache is switched off by default, since it was occasionally error-prone and didn’t appear to improve performance all that much. Your mileage may vary, but use with care.




buildEnv

Moving on to some react4xp.properties settings that don’t focus on SSR, but instead control the compilation of assets from source code and imports.

Common pattern: the following settings are picked up by gradle, which injects them into webpack.config.*.js files that are bundled with the react4xp NPM package. These in turn perform the actual compilation of your source code and its imports. See also entries and chunks.

Obviously, having a basic knowledge of webpack is fortunate (intro suggestion).

buildEnv switches between react4xp build modes (not to be confused with XP’s run modes).

react4xp.properties:
buildEnv = production
  • production: assets are compiled more compact (and faster), with no source maps, and the entire SSR engine is initialized at once.

  • development: assets are compiled for more human-readability, with source maps, making errors easier to track down. If ssrLazyload hasn’t been set, development will activate lazy-loading.

Default value is production.

development mode can also be set ad hoc for one build without changing react4xp.properties, by adding the gradle CLI parameter -Pdev. For example:

enonic project gradle build deploy -Pdev


entryDirs

react4xp.properties:
entryDirs = myComponents, ../otherComponents

By default, react4xp will look for (ie. sets up webpack to look for) .JSX files to turn into entries below src/main/resources/site/.

Adding comma separated values under entryDirs adds more folder names/paths (relative to src/main/resources/react4xp/) where JSX files will also become entries.


chunkDirs

By default, react4xp will look for (ie. sets up webpack to look for) resources imported by the entries. These are bundled into separate assets that react4xp automatically loads at both server- and clientside rendering: chunks, by this pattern:

  • If they are react and reactDOM, they are bundled separately into externals.*.js (where the * is a content-dependent hash).

  • node_modules/react4xp-regions/ is bundled into templates.*.js

  • Other packages under node_modules are bundled into vendors.*.js

  • And everything else that’s not under a chunkDir marked here is bundled into react4xp.*.js

(Of course, non-JS bundles split out by webpack will have different extensions, such as .css)

The idea is to use chunkDirs to add a comma-separated list of names/paths of directories (relative to src/main/resources/react4xp/) that will be bundled into chunks of their own. The chunk name will be the name of the last directory in the path:

react4xp.properties:
chunkDirs = chunk1, bundle2, other/stuff

This example adds these folders as chunkDirs, and anything the entries import from below them is bundled separately into:

  • src/main/resources/react4xp/chunk1/chunk1.*.js

  • src/main/resources/react4xp/bundle2/bundle2.*.js

  • src/main/resources/react4xp/other/stuff/stuff.*.js


overrideComponentWebpack

React4xp comes with a minimal set of webpack rules built-in, for compiling react components in JSX files into vanilla JS. But if you need to change/expand this setup, write a custom webpack config file and add the name of it here:

react4xp.properties:
overrideComponentWebpack = webpack.config.react4xp.js

There can be several reasons to this:

  • Most commonly, the built-in webpack setup is pretty minimal, only adding loaders for compiling react from JSX. It’s likely you will need to add loaders of your own, maybe use additional plugins etc

  • You may want to adjust other aspects of the compilation rules, or even replace the built-in rules entirely

  • The assets that are built during the compilation are the same ones that are run in nashorn and in the browser. It’s possible you may need adjustments here to account for corner cases - but if the problem is missing feature support in nashorn, it’s better to add extra polyfills using nashornPolyfillsSource instead.

Config file shape: syntax variation!

Usually, webpack.config.js files tend to have a certain shape, something like:

module.exports = function(env) {
    var config = {
        entry: (...)
        output: (...)
        resolve: (...)
        module: {
            rules: (...)
        }
    };

    return config;
};
// ...etc, etc

The file referred to by overrideComponentWebpack can follow the same shape, but the exported function can also take a second config argument:

module.exports = function(env, config) {
    // ...
};

The extra incoming config object contains the built-in rules from react4xp. This enables you to both manipulate those rules and/or add your own, or entirely replace them by returning a different object.


nashornPolyfillsSource

React4xp doesn’t have any ambition to completely polyfill the nashorn engine so that the full feature set of node.js (or modern browsers) is supported.

In the event that your code (or imported packages) rely on functionality that isn’t supported in nashorn, you can add as-vanilla-as-possible JS in a .js file and add the path/name to it here:

nashornPolyfillsSource = extraNashornPolyfills.js

This will be run as part of the SSR engine initialization, adding functionality before packages or other compiled code is loaded.

For example, until recently Object.assign wasn’t polyfilled, causing problems for certain packages. That could be added by adding this code chunk to the file referred with nashornPolyfillsSource.


Contents