diff --git a/flow-server/src/main/java/com/vaadin/experimental/FeatureFlags.java b/flow-server/src/main/java/com/vaadin/experimental/FeatureFlags.java index 5898a2aa922..4f5dc2521f4 100644 --- a/flow-server/src/main/java/com/vaadin/experimental/FeatureFlags.java +++ b/flow-server/src/main/java/com/vaadin/experimental/FeatureFlags.java @@ -95,6 +95,10 @@ public class FeatureFlags implements Serializable { "https://github.com/vaadin/web-components/issues/5340", true, "com.vaadin.flow.component.card.Card"); + public static final Feature REACT19 = new Feature( + "React 19 (default in Vaadin 25)", "react19", + "https://react.dev/blog/2024/12/05/react-19", true, null); + private List features = new ArrayList<>(); File propertiesFolder = null; @@ -123,6 +127,7 @@ public FeatureFlags(Lookup lookup) { features.add(new Feature(COPILOT_EXPERIMENTAL)); features.add(new Feature(DASHBOARD_COMPONENT)); features.add(new Feature(CARD_COMPONENT)); + features.add(new Feature(REACT19)); loadProperties(); } diff --git a/flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeUpdater.java b/flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeUpdater.java index 67d8763f1e9..0a0559a27e3 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeUpdater.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/frontend/NodeUpdater.java @@ -37,6 +37,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.vaadin.experimental.FeatureFlags; import com.vaadin.flow.server.Constants; import com.vaadin.flow.server.frontend.scanner.ClassFinder; import com.vaadin.flow.server.frontend.scanner.FrontendDependencies; @@ -302,6 +303,10 @@ Map getDefaultDependencies() { if (options.isReactEnabled()) { dependencies .putAll(readDependencies("react-router", "dependencies")); + if (options.getFeatureFlags().isEnabled(FeatureFlags.REACT19)) { + dependencies + .putAll(readDependencies("react19", "dependencies")); + } } else { dependencies .putAll(readDependencies("vaadin-router", "dependencies")); @@ -368,6 +373,9 @@ Map getDefaultDevDependencies() { if (options.isReactEnabled()) { defaults.putAll( readDependencies("react-router", "devDependencies")); + if (options.getFeatureFlags().isEnabled(FeatureFlags.REACT19)) { + defaults.putAll(readDependencies("react19", "devDependencies")); + } } return defaults; diff --git a/flow-server/src/main/resources/com/vaadin/flow/server/frontend/dependencies/react19/package.json b/flow-server/src/main/resources/com/vaadin/flow/server/frontend/dependencies/react19/package.json new file mode 100644 index 00000000000..34a7c26159a --- /dev/null +++ b/flow-server/src/main/resources/com/vaadin/flow/server/frontend/dependencies/react19/package.json @@ -0,0 +1,12 @@ +{ + "private": true, + "description": "A list of Flow dependencies when using React 19", + "dependencies": { + "react": "19.0.0", + "react-dom": "19.0.0" + }, + "devDependencies": { + "@types/react": "19.0.2", + "@types/react-dom": "19.0.2" + } +} diff --git a/flow-server/src/test/java/com/vaadin/flow/server/frontend/NodeUpdaterTest.java b/flow-server/src/test/java/com/vaadin/flow/server/frontend/NodeUpdaterTest.java index 386ff448df3..c96e17747f2 100644 --- a/flow-server/src/test/java/com/vaadin/flow/server/frontend/NodeUpdaterTest.java +++ b/flow-server/src/test/java/com/vaadin/flow/server/frontend/NodeUpdaterTest.java @@ -127,6 +127,31 @@ public void getDefaultDependencies_includesAllDependencies() { Assert.assertEquals(expectedDependencies, actualDependendencies); } + @Test + public void react19UsedWhenFeatureFlagIsOn() { + Map react18Deps = nodeUpdater.getDefaultDependencies(); + Map react18DevDeps = nodeUpdater + .getDefaultDevDependencies(); + Mockito.when(options.getFeatureFlags().isEnabled(FeatureFlags.REACT19)) + .thenReturn(true); + + Map react19Deps = nodeUpdater.getDefaultDependencies(); + Map react19DevDeps = nodeUpdater + .getDefaultDevDependencies(); + + Assert.assertTrue(react18Deps.get("react").startsWith("18.")); + Assert.assertTrue(react18Deps.get("react-dom").startsWith("18.")); + Assert.assertTrue(react18DevDeps.get("@types/react").startsWith("18.")); + Assert.assertTrue( + react18DevDeps.get("@types/react-dom").startsWith("18.")); + + Assert.assertTrue(react19Deps.get("react").startsWith("19.")); + Assert.assertTrue(react19Deps.get("react-dom").startsWith("19.")); + Assert.assertTrue(react19DevDeps.get("@types/react").startsWith("19.")); + Assert.assertTrue( + react19DevDeps.get("@types/react-dom").startsWith("19.")); + } + @Test public void getDefaultDevDependencies_includesAllDependencies_whenUsingVite() { Map defaultDeps = nodeUpdater