Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Making react4j-todomvc j2cl compatible

How to build

First you need to install updated build tools that does not copy the */public/ assets into the directory used by j2cl compile. To install build tools locally do:

> git clone
> cd j2cl
> git checkout NoCopyPublicAssets
> mvn clean install

Future TODO

Change Notes

These are short-hand notes of the changes that were needed to get this "working".

  • Add both .java and .class files for annotation libraries. GWT2.x only required .class files and never processed them unless on source code path.
  • Annotation libraries now need to compilable under j2cl - previously not the case. Primarily this impacted libraries such as It uses several jre only features so instead of trying to fix that library I just extracted a j2cl compatible library org.realityforge.javax.annotation:javax.annotation:jar that included a few commonly used annotations (i.e. @Nonnull, @Nullable and @Generated). A similar approach was taken for jetbrains annotations -
  • As annotation processors were moved off the classpath onto processor classpath.
  • Some annotation processors had not been shaded and now needed to be. Previously when annotation processors were only on the classpath, each module would only have a processor and it deps on the classpath if it was needed. However due to limitations of some tooling (hello Intellij IDEA+maven), sometimes when adding to processor path, the processor would be visible to multiple compiles and if the processors have incompatible dependencies this would be an issue.

Jsinterop changes

  • The @JsConstructor annotation needed to be added to the constructor of the react4j.core.NativeComponent class and to the constructors of all subclasses, including those generated by the annotation processor. This is to satisfy J2CL which has stricter requirements with respect to the jsinterop annotations.
  • @JsType( isNative = true ) to @JsType( isNative = true, namespace = JsPackage.GLOBAL, name = "Object" ) In GWT2.x if the objects were created by the javascript library and passed to java it did not matter.
  • Change the namespace the jsinterop typing of the React class from @JsType( isNative = true, namespace = "React", name = "Component" ) to @JsType( isNative = true, namespace = JsPackage.GLOBAL, name = "React.Component" ) as J2CL only considers types with a namespace of JsPackage.GLOBAL to be capable of being externs.
  • Rework the way compile time constants are processed to be compatible with j2cl - see

Externs/Closure specifics

  • Find and include externs.
  • Add defines to the source projects (react4j+braincheck+arez) that required the defines from a ArezConfig.native.js file so that the compile constants were available to closure compiler.


  • No way to convert Enums into integers?

Size Analysis

  • Raw: 30598 (J2CL) vs 36826 (GWT 2.8.2) ~ 83%
  • Arez: 63316 (J2CL) vs 70358 (GWT 2.8.2) ~ 90%
  • Arez (with patched Arez): 62989 (J2CL) vs 70358 (GWT 2.8.2) ~ 90%
  • Dagger: 66418 (J2CL) vs 75335 (GWT 2.8.2) ~ 88%

The patched Arez mostly involved converting interfaces into @JsFunction interfaces. This increases sizes under GWT 2.8.2 and decreases sizes under J2CL. Thus it has not been applied to master. See the associated Pull Request.


  • Document namespace parameter value "<window>" of @JsType. Consider suggeating Extraction of jsinterop, javaemul and jre libraries to be moved outside GWT repository. Here is the description from Colin

    is an "escape hatch" of sorts, undocumented but supported, which lets you say "just let me run without any namespace at all" in gwt, GLOBAL means a $wnd. prefix (in j2cl it means prefix) see jsinterop.base.Js for a few examples of

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment