Liferay Client Extensions: The future of Liferay development

Introduction
Liferay Client Extensions are transforming how modern Liferay development is done. For years, customization relied on OSGi modules powerful but tightly coupled and difficult to scale.
Today, enterprises demand cloud-native, flexible, and upgrade-safe solutions. This is where Client Extensions come in enabling external, API-driven, and independently deployable architectures that align with Liferay headless development principles.
Why Client Extensions matter now
For years, Liferay customization meant one thing, OSGi modules. You wrote Java bundles, deployed them to the server, and hoped your customizations survived the next upgrade cycle.
That model worked. Until it didn't.
As enterprises demanded faster deployments, cloud-native architectures, and decoupled frontends, the OSGi approach started showing its age. Upgrades became risky. Deployments were heavy. Every customization was tightly coupled to the platform version underneath it.
Liferay's answer? Client Extensions Liferay teams rely on today. Not a patch, a fundamental shift in how Liferay headless development is done. In 2026, it's no longer the future. It's the standard.
What are Client Extensions?
Client Extensions are external, independently deployable applications or configurations that extend Liferay without modifying its core. They live outside the Liferay container, communicating via REST/GraphQL APIs, webhooks (for event-driven flows), and secured using OAuth2.
This decoupled approach is the foundation of modern Liferay headless development, your extensions are fully independent from the platform's internals and only interact through stable, versioned APIs.
Old OSGi way
- Code lives inside Liferay's container
- Tightly coupled to internal APIs
- Upgrades break customizations
- Full redeployment for every change
Client Extensions way
- Code lives outside Liferay
- Communicates via stable, versioned APIs
- Upgrade-safe (API dependent)
- Deploy, update, roll back independently
Types of Client Extensions
Client Extensions Liferay fall into four broad categories. Each serves a different layer of your Liferay implementation.
1. Frontend Client Extensions
These control what users see - themes, styles, JavaScript behavior, and custom UI components.
Types :
| Type | What It Does |
|---|---|
| CSS Client Extension | Inject custom CSS into Liferay pages |
| Theme CSS Client Extension | Replace Liferay's default theme styles entirely |
| JS Client Extension | Add custom JavaScript without touching Liferay's core JS |
| Static Content Client Extension | Serve static assets (images, fonts, files) from an external CDN or server |
| Theme Favicon Client Extension | Override the default favicon per site |
| Theme Spritemap Client Extension | Replace Liferay's default spritemap with a custom icon set |
| Custom Element Client Extension | Register a web component (React, Angular, Vue) as a Liferay widget |
| IFrame Client Extension | Embed an external application inside a Liferay page |
| Frontend Data Set Filter Client Extension | Add custom filter options to Liferay's Frontend Data Set component |
| Frontend Data Set Cell Renderer Client Extension | Customize how individual cells render inside a Frontend Data Set |
| Editor Config Contributor Client Extension | Customize the rich text editor configuration across your instance |
| JS Import Maps Entry Client Extension | Add entries to the browser's import map for module resolution |
Example : Registering a React component as a Liferay widget :
1# client-extension.yaml
2assemble:
3 - from: build/static
4clientExtensions:
5 my-react-widget:
6 cssURLs:
7 - index.*.css
8 friendlyURLMapping: my-react-widget
9 htmlElementName: my-react-widget
10 name: My React Widget
11 type: customElement
12 urls:
13 - index.*.js
14 useESM: true2. Microservice Client Extensions
These provide API endpoints that Liferay can trigger - object actions, workflow steps, notification events, with the actual logic running outside Liferay as a standalone microservice. Your service can be written in any language.
The Liferay microservices architecture built around this CE type lets you run each piece of business logic independently, no Liferay restart, no re-deployment of the whole platform, no shared JVM risk.
Types :
| Type | What It Does |
|---|---|
| Object Action Client Extension | Run custom logic when a Liferay Object record is created, updated, or deleted |
| Object Validation Client Extension | Add custom validation rules to Liferay Objects before a record is saved |
| Workflow Action Client Extension | Trigger external logic at a specific step in a Liferay workflow |
| Notification Type Client Extension | Create custom notification delivery channels beyond the defaults |
| Captcha Client Extension | Integrate external CAPTCHA providers to validate user interactions and prevent automated or bot submissions |
Example : Notification Type triggering an external microservice :
1assemble:
2 - fromTask: bootJar
3
4my-oauth-user-agent:
5.serviceAddress: localhost:58081
6.serviceScheme: http
7name: My OAuth User Agent
8Scopes:
9 - Liferay.Headless.Admin.Workflow.everything
10type: oAuthApplicationUserAgent
11When a Liferay Object record is saved, Liferay sends a POST request to your external service at /sync-to-crm, authenticated via OAuth2. Your service can be in Node.js, Python, Go, anything.
Going deeper into Microservice CEs? We have a dedicated blog covering the full Liferay microservices architecture , OAuth2 flow, and real implementation with Spring Boot : Microservice Client Extension in Liferay
3. Batch Client Extensions
Batch Client Extensions provide data entities to your Liferay instance, things like object definitions, workflow definitions, and other structured data. They work via Liferay's batch engine framework and are particularly useful for moving configurations between environments without manual re-setup.
Types :
| Type | What It Does |
|---|---|
| Batch Client Extension | Import or export structured data entities (object definitions, workflow definitions) via Liferay's batch engine |
Example : Batch Client Extension for importing object definitions :
1# client-extension.yaml
2assemble:
3 - from: batch
4 include: "**/*"
5 into: batch
6clientExtensions:
7 my-batch-client-extension:
8 name: My Batch Client Extension
9 oAuth2ApplicationExternalReferenceCode: my-oauth-app
10 type: batchThe exported *.batch-engine-data.json files (in jsont format) go into the batch/ directory. When deployed, Liferay's batch engine reads and imports them automatically into the target instance.
Batch Client Extensions can be grouped with Configuration Client Extensions in the same project, since both represent configuration-type workloads rather than running services.
4. Configuration Client Extensions
These provide specific configurations to change functionality within your Liferay instance. They can be combined with other Client Extension types, for example, pairing an OAuth User Agent CE with a Microservice CE to handle authentication for user-triggered events.
In Liferay SaaS development, Configuration Client Extensions are especially important — since you can't touch the server directly, deploying configurations as code is the standard way to manage instance-level settings.
Types :
| Type | What It Does |
|---|---|
| OAuth User Agent Client Extension | Register an OAuth2 application for user-facing flows |
| OAuth Headless Server Client Extension | Register an OAuth2 application for server-to-server flows |
| Instance Settings Client Extension | Configure a wide variety of Liferay instance settings via their PID (Persistent IDentity), deployable as code |
OSGi vs Client Extensions : Which One Should You Use?
The Liferay OSGi vs Client Extensions debate is the question that matters most for teams making architectural decisions. Both can extend the platform, but they take fundamentally different approaches, and choosing the wrong one creates long-term pain.
| Factor | OSGi Modules | Client Extensions |
|---|---|---|
| Architecture | Runs inside Liferay's JVM, tightly coupled to the platform | Runs outside Liferay, communicates via headless APIs |
| Language | Java only | Any language, Node.js, Python, Go, Spring Boot, React |
| Development Complexity | High, requires deep OSGi and Liferay internals knowledge | Lower, YAML config or standalone app, no OSGi knowledge needed |
| Deployment | Bundled JARs deployed into Liferay runtime | Independent service or Docker container, deployed separately |
| Upgrade Safety | Risky, internal API changes often break modules | Safe, depends only on stable headless APIs |
| Scalability | Scales only with the entire Liferay server | Scales independently like any microservice |
| Cloud Readiness | Limited, not suitable for Liferay SaaS | Fully cloud-ready — the only option for Liferay SaaS development |
| Fault Isolation | A faulty module can impact the whole server | A faulty extension doesn't affect Liferay's core |
| Best For | Deep platform customizations, core service modifications | External integrations, frontend widgets, decoupled logic |
When to still use OSGi : If you need direct access to Liferay's internal services, complex Service Builder modules, or deep hooks into core platform behavior, OSGi is still the right tool. Client Extensions Liferay can't replace everything.
For everything else : Client Extensions are the recommended path, simpler, safer, and future-proof. This is especially true for teams moving toward Liferay headless development or working in Liferay SaaS environments.
Want the full breakdown? We've covered the Liferay OSGi vs Client Extensions comparison in detail - architecture, deployment, scalability, and real use cases, in a dedicated blog: Comparison of Client Extensions and OSGi: Modern vs Traditional Liferay Development
Real-World Use Cases
Client Extensions Liferay enterprises adopt are solving real problems, not just developer convenience.
1. Multi-brand theming without forking A global enterprise runs 5 brand sites on one Liferay instance. Instead of forking themes or managing separate deployments, they use Theme CSS Client Extensions, one per brand, each deployed independently, each overriding only what needs to change.
2. Liferay Objects + external CRM sync A sales portal built on Liferay Objects needs to sync leads to Salesforce in real time. An Object Action Client Extension fires on every record creation, sending a webhook to a Node.js microservice that handles the Salesforce API call. Liferay never touches Salesforce directly. A clean example of Liferay microservices architecture in practice.
3. React microfrontend inside Liferay A team already has a React dashboard they've built separately. Instead of rebuilding it inside Liferay, they register it as a Custom Element Client Extension. It lives in their own CI/CD pipeline, gets deployed independently, and appears as a draggable widget in Liferay's page editor, pure Liferay headless development thinking.
4. AI-powered content validation An Object Validation Client Extension calls an external AI service to check whether user-submitted content meets brand guidelines before saving - without any core Liferay modification.
Getting Started : Your First Client Extension
Here's the minimum you need to get a Client Extension running locally.
Prerequisites :
- Liferay DXP (latest) running locally or via Docker
- Node.js 18+
- Liferay CLI (@liferay/cli)
Step 1 : Create a workspace :
1liferay init client-extension-workspace
2cd client-extension-workspaceStep 2 : Pick a sample :
1liferay add
2# Select a Client Extension templateStep 3 : Configure client-extension.yaml :
Every Client Extension is defined by this file. It tells Liferay what type of extension this is, where to find its assets, and how to connect to it.
1clientExtensions:
2 my-global-css:
3 name: My Global CSS
4 type: globalCSS
5 url: ./css/custom.cssStep 4 : Deploy :
1./gradlew deployLiferay picks it up automatically. No server restart needed.
Final Thought
Client Extensions aren't just a new API, they represent a philosophy shift. Liferay is moving toward a world where the platform is the stable core, and everything built on top of it is independently deployable, independently scalable, and upgrade-safe by design.
Whether you're exploring Liferay SaaS development, building a Liferay microservices architecture, or simply trying to move away from OSGi, Client Extensions are the direction the platform is heading.
For development teams, that's not a constraint. That's freedom.
FAQs
1. Can I still use OSGi modules in 2026? Yes, OSGi modules aren't removed. But for most use cases, Client Extensions Liferay developers now prefer are the recommended approach. OSGi makes more sense for deep platform customizations that can't be achieved externally.
2. What languages can I use for backend Client Extensions? Any language that can run an HTTP server, Node.js, Python, Go, Java, .NET. This language-agnostic flexibility is one of the biggest advantages of the Liferay microservices architecture that Client Extensions enable.
3. Are Client Extensions supported on Liferay SaaS? Yes. In fact, Client Extensions are the primary and recommended way to extend Liferay SaaS, since you can't deploy OSGi modules to a managed cloud environment. Liferay SaaS development is built around this model.
4. How does authentication work between Liferay and my Client Extension? Via OAuth2. Liferay sends authenticated requests to your service using tokens. You configure this through OAuth Client Extensions in your client-extension.yaml, a core part of Liferay headless development patterns.
5. Do Client Extensions affect Liferay's upgrade path? No, that's the whole point. Since your code lives outside Liferay, upgrades don't touch it. The Liferay OSGi vs Client Extensions question often comes down to exactly this, upgrade safety is one of the strongest reasons teams are making the switch.