Vaadin 24.4 integrates with React, unifies Flow and Hilla development, and more!

Embedding Vaadin Flow apps in a React-based web app

Matti Tahvonen
Matti Tahvonen
On Nov 22, 2023 5:06:04 PM

In my recent blog post, I discussed wrapping a component implemented with React in a Vaadin application. A greatly undervalued way to use Vaadin is by implementing only certain functions or parts of your web app or website. In that case, we are essentially doing the opposite: wrapping a Vaadin component (or “view”) into a React application.

Embedding a component built with Vaadin Flow happens using Web Components, and React has decent support for Web Components. Technically, this is “just” like embedding any other Vaadin application. React’s improved property support for custom elements has been lingering in the backlog for years, so the primary way to pass parameters for the embedded Vaadin apps doesn't work properly at the moment. But in case you need to pass some data from the React app to the embedded Vaadin component (or vice versa), you can do that with plain JavaScript instead. 

Implementing mashups becomes significantly more manageable when they can operate within the same domain. Otherwise, the same-origin policy in browsers can complicate your work considerably. I highly recommend making this your primary option, as it is usually fairly easy to implement with a front proxy such as nginx, Apache, or HA Proxy. At least, it’s easier than working around the same-origin policy using technologies like CORS.

In this tutorial, we merge two separate web apps using the development server of the React application. We’ll use the React template available in the Vite tooling and add a simple Vaadin UI to it, running in a separate Spring Boot application. The handy Vite dev server is configured to proxy the Vaadin app to the same host and port. The principles for other development environments are the same, but the configuration is naturally different. 

Screenshot: The mashup built in this tutorial contains a trivial React UI containing a reusable “newsletter-subscription” Web Component built using Vaadin.

An alternative to configuring the front-end development server is to launch a separate nginx or HA Proxy instance and configure it to proxy both the React and Vaadin apps.

If the same domain approach isn't possible in production, you should probably perform the CORS dance in your development environment as well.

Prepare the Vaadin app for embedding

Vaadin documentation provides detailed instructions on preparing your application for embedding. The main step involves using the WebComponentExporter class instead of the Route annotation to ‘expose’ the ‘root component’ of your application. You can essentially expose any Vaadin component you want. In the demo, the NewsletterSubscription class is exported via the Exporter.

Some best practices could be highlighted here, though. In many cases, it is handiest to develop the embedded application in isolation – directly accessing the application under development instead of via the mashup. For this reason, it can be handy to expose the same component with a @Route annotation as well. Depending on the security constraints and configuration required for the app, it might be more convenient to extend the exported component on the src/test/java side of your source code and configure your development server to check those sources as well (for example, by providing an additional main method in a Spring Boot application).

Another trick is to make your math in the front-proxy setup easier. Whether embedding or working with regular Vaadin apps, I’ve found it more straightforward to configure the front proxy when using the same context path on the development server and in the final deployment. It is much easier to keep various URLs in sync if the URLs (relative to the server root) are the same in all servers of your setup.

In this tutorial, the Vaadin application will be proxied from /vaadin-app* so, to simplify configuration later on, the Spring Boot app should be configured to the same context path with the following configuration in the file:


Proxy the Vaadin app to the same domain with the Vite dev server

Vite has become a popular front-end build tool lately. it is also used internally by Vaadin to build its own front-end bundle. Thus, I decided to use Vite for this tutorial and kickstarted a React project using its react-ts template. Vite contains a handy development server that can be configured to proxy the “backend services” to your front-end development environment. This is typically used to map, for example, REST services that you use in your React front-end during development, but it works perfectly for Vaadin applications as well.

Modify your vite.config.ts file to include the proxy part of this example:

import { defineConfig } from 'vite'

import react from '@vitejs/plugin-react'


export default defineConfig({

  plugins: [react()],

  server: {

    proxy: {

      // proxy the locally running Spring Boot app (having the same 

      // /vaadin-app context path) to Vite dev server.

      // Production server should have a similar mapping in the front-proxy

      '/vaadin-app': 'http://localhost:8080'




You can now test the proxy setup. Ensure that your Vite dev server is running (launched with npm run dev) and that the @Route annotated development view is available. It should open from http://localhost:5173/vaadin-app/ (or whatever port Vite picked for your development server). Additionally, verify that you can access the exported Web Component definition from http://localhost:5173/vaadin-app/web-component/newsletter-subscription.js.

Use the Exported Vaadin App as a Web Component in the React app.

The final step is to drop the exported Vaadin application (technically a Web Component) into your React app. The easiest way to include the custom element definition is to locate the host page (typically the index.html file) and insert the following line inside the <head> tag:

<script type='module' src="/vaadin-app/web-component/vaadin-component.js"></script>

Alternatively, suppose you require a lot of configuration for your embedded application. In that case, you can build a separate initialization script for your front-end project and directly use it from your JS or TS code. This approach is explained in detail in the NPS example by Sami Ekblad. With this approach, the loading of any Vaadin resources is postponed in the UI until they are actually needed.

With the latest versions of React, using custom elements is trivial. All lowercase tags in JSX containing a dash are automatically interpreted as custom elements. The following code snippet declares the App component using JSX, which includes a button with a counter and the embedded Vaadin Web Component (newsletter-subscription):

function App() {

  const [count, setCount] = useState(0)

  return (


        <button onClick={() => setCount((count) => count + 1)}>

          count is {count}






If you use the TSX (the TypeScript variant of JSX), the above code will show a nasty error in your editor, but it will work just fine. You’ll need to let the JSX parser know about the custom element to get rid of the typing warning. Dropping the following to your TSX file will make the error disappear (change the tag name to match the name of the custom element in your exported Vaadin app): 

declare global {

  namespace JSX {

    interface IntrinsicElements {

      'newsletter-subscription': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>,HTMLElement>;





Embedding Vaadin apps into any React-based web app is pretty much like embedding Vaadin apps into raw HTML pages. As with all mashups, the most effective way to address same-origin restrictions is by using a front proxy. It’s highly recommended to set up a front proxy in your development environment as well.

Check out the full example project, containing a simple Vaadin app and React app living together in harmony!

Matti Tahvonen
Matti Tahvonen
Matti Tahvonen has a long history in Vaadin R&D: developing the core framework from the dark ages of pure JS client side to the GWT era and creating number of official and unofficial Vaadin add-ons. His current responsibility is to keep you up to date with latest and greatest Vaadin related technologies. You can follow him on Twitter – @MattiTahvonen
Other posts by Matti Tahvonen