To create a React client extension in Liferay using Vite, you begin by setting up a React app and converting it into a web component. After configuring it with basic metadata, the extension can be deployed and added to any Liferay page for seamless integration.
Create a New React App Using Vite
yarn create vite react-chat-app --template react
Or with npm
npm create vite@latest react-chat-app -- --template react
For creating React client extensions for running it in Liferay, you may refer to:
https://liferay.dev/es/blogs/-/blogs/from-react-app-to-react-client-extension-in-5-easy-steps
If you’re not in the mood for a long read and just want to dive in, stick with me — I’ve laid out the steps. But if you enjoy the scenic route with all the details, the official guide above is your best travel buddy.
Change connecting logic – Update the main.jsx
import App from "./App"; // Ensure App is correctly imported
import { ELEMENT_NAME } from "./commons/constants";
import React from "react";
import { createRoot } from "react-dom/client";
class WebComponent extends HTMLElement {
appLoaded = false;
constructor() {
super();
this.appLoaded = false;
}
connectedCallback() {
if (!this.appLoaded) {
this.appLoaded = true;
this.root = createRoot(this); // Use createRoot
this.root.render(<App accessTokenParamProps={accessTokenParamProps} />);
}
}
}
if (!customElements.get(ELEMENT_NAME)) {
customElements.define(ELEMENT_NAME, WebComponent);
}
Note: I have added the “react-chat-app” name in the constants.js file under the common folder. You may keep it as a static string as well.
Update index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="module" crossorigin src="/o/react-chat-app/assets/index-CAlfPsPC.js"></script>
<link rel="stylesheet" crossorigin href="/o/react-chat-app/assets/index-Cs9mLtrU.css">
</head>
<body>
<react-chat-app></react-chat-app>
</body>
</html>
Note: Make sure to add react-chat-app as a tag to ensure the React application runs properly.
Create a client-extension.yaml file
assemble:
- from: vite-build
into: static
react-chat-app:
# cssURLs:
# - assets/*.css
friendlyURLMapping: react-chat-app
htmlElementName: react-chat-app
instanceable: true
name: React Chat App
portletCategoryName: category.client-extensions
type: customElement
urls:
- assets/*.js
useESM: true
Note: I have commented the code for CSS as I am loading CSS from the theme, you may uncomment and pass the CSS here as well.
Update Vite Configuration
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
base: '/o/react-chat-app/',
plugins: [
react({
jsxRuntime: 'classic',
}),
],
build: {
outDir: './vite-build',
rollupOptions: {
external: [],
},
},
});
Place the react-chat-app in the client extension folder under your workspace.
For reference, here’s the file structure
Run the React client extension
In a terminal window open to client-extensions/my-react-custom-element, we’re going to issue the command:
../../gradlew clean deploy
If it’s deployed successfully, then you will get your React client extension deployed on the Liferay server, also after this, put it on a page wherever you want.



